Altípusok és variancia
Q# csak néhány konverziós mechanizmust támogat. Implicit konverziók csak bináris operátorok alkalmazása, feltételes kifejezések kiértékelése vagy tömbkonstans létrehozásakor történhetnek. Ezekben az esetekben meghatározunk egy gyakori szupertípust, és a szükséges átalakításokat automatikusan végrehajtjuk. Az ilyen implicit konverziók mellett a függvényhívásokon keresztüli explicit konverziók is lehetségesek és gyakran szükségesek.
Jelenleg az egyetlen olyan altiprási kapcsolat létezik, amely a műveletekre vonatkozik. Intuitív módon logikus, hogy lehetővé kell tenni egy olyan művelet helyettesítését, amely több, mint a szükséges funktorkészletet támogatja. Konkrétan, bármely két konkrét típus TIn
TOut
esetében és , az altípus-reláció a
(TIn => TOut) :>
(TIn => TOut is Adj), (TIn => TOut is Ctl) :>
(TIn => TOut is Adj + Ctl)
ahol A :> B
azt jelzi, hogy B
a altípusa A
. Másként fogalmazva, szigorúbb, B
mint A
az ilyen, hogy egy típusú B
érték használható, ahol szükség van egy típusú A
értékre. Ha egy hívható típus argumentumára (elemére A
) támaszkodik, akkor egy típus B
argumentuma biztonságosan helyettesíthető, mivel ha az összes szükséges képességet biztosítja.
Ez a fajta polimorfizmus abban az esetben terjed ki a rekordokra, ha egy rekordtípus B
egy rekordtípus A
altípusa, ha azonos számú elemet tartalmaz, és az egyes elemek típusa a megfelelő elemtípus altípusa a fájlban A
. Ezt mélységi altipréssel ismerjük. Jelenleg nem támogatott a szélességi altípus, azaz nincs altípus-kapcsolat két felhasználó által definiált típus vagy felhasználó által definiált típus és bármely beépített típus között. Az operátor megléte unwrap
, amely lehetővé teszi, hogy kinyerje az összes megnevezett és névtelen elemet tartalmazó rekordot, megakadályozza ezt.
Megjegyzés
A hívhatók esetében, ha a hívható egy típusú A
argumentumot dolgoz fel, akkor a típus argumentumát B
is képes feldolgozni. Ha egy hívhatót argumentumként ad át egy másik hívhatónak, akkor képesnek kell lennie a típus-aláírás által igényelt bármi feldolgozására. Ez azt jelenti, hogy ha a hívhatónak képesnek kell lennie egy típusú B
argumentum feldolgozására, minden olyan hívható, amely képes egy általánosabb típusú A
argumentum feldolgozására, biztonságosan átadható. Ezzel szemben azt várjuk, hogy ha azt követeljük meg, hogy az átadott hívható típusértéket A
adjon vissza, akkor a típus B
értékének visszaadására vonatkozó ígéret elegendő, mivel ez az érték minden szükséges képességet biztosít.
A művelet vagy függvény típusa argumentumtípusában a contravariant , a visszatérési típusában pedig a kovariant .
A :> B
ezért azt jelenti, hogy bármilyen konkrét típus T1
esetében,
(B → T1) :> (A → T1), and
(T1 → A) :> (T1 → B)
ahol →
itt egy függvényt vagy műveletet is jelenthet, és kihagyjuk a jellemzőkre vonatkozó megjegyzéseket.
A
A és a és az (B → T2)
(T2 → A)
, illetve B
az és az helyettesítése (A → T2)
(T2 → B)
arra a következtetésre vezet, hogy bármely konkrét típus T2
esetében,
((A → T2) → T1) :> ((B → T2) → T1), and
((T2 → B) → T1) :> ((T2 → A) → T1), and
(T1 → (B → T2)) :> (T1 → (A → T2)), and
(T1 → (T2 → A)) :> (T1 → (T2 → B))
Az indukcióból következik, hogy minden további közvetettség megfordítja az argumentumtípus varianciáját, és változatlanul hagyja a visszatérési típus varianciáját.
Megjegyzés
Ez azt is egyértelművé teszi, hogy a tömbök variancia-viselkedésének milyennek kell lennie; az elemek elemhozzáférési operátoron keresztüli lekérése egy típusú (Int -> TItem)
függvény meghívásának felel meg, ahol TItem
a tömb elemeinek típusa. Mivel ezt a függvényt implicit módon adja át egy tömb átadásakor, a tömböknek kovariánsnak kell lenniük az elemtípusukban. Ugyanezek a szempontok a nem módosítható, így az egyes elemtípusokra vonatkozó kovariáns rekordokra is kitérnek.
Ha a tömbök nem módosíthatók, akkor egy olyan szerkezet megléte, amely lehetővé teszi elemek tömbben való beállítását, és így egy típusú TItem
argumentumot vesz fel, azt jelenti, hogy a tömböknek is contravariantnak kell lenniük. Az elemek lekérését és beállítását támogató adattípusok esetében az egyetlen lehetőség az invariáns, ami azt jelenti, hogy semmilyen altípus-reláció nem létezik; B[]
nem a altípusa A[]
még akkor sem, ha B
a altípusa A
. Annak ellenére, hogy a tömbök Q#nem módosíthatók, a kováns helyett invariánsak. Ez például azt jelenti, hogy egy típusérték (Qubit => Unit is Adj)[]
nem adható át egy hívhatónak, amely típus argumentumot (Qubit => Unit)[]
igényel.
A tömbök invariáns megtartása nagyobb rugalmasságot tesz lehetővé a tömbök futtatókörnyezetben való kezelésével és optimalizálásával kapcsolatban, de ezt a jövőben át lehet majd vizsgálni.