Podtypy a variance
Q# podporuje pouze několik mechanismů převodu. K implicitním převodům může dojít pouze při použití binárních operátorů, vyhodnocování podmíněných výrazů nebo při vytváření literálu pole. V těchto případech se určí běžný supertyp a potřebné převody se provádějí automaticky. Kromě těchto implicitních převodů jsou možné a často nezbytné explicitní převody prostřednictvím volání funkcí.
V současné době platí pro operace jediný existující vztah podtypů. Intuitivně dává smysl, aby jedna z nich nahradila operaci, která podporuje více než požadovanou sadu funktorů. Konkrétně pro jakékoli dva konkrétní typy TIn
a TOut
, je vztah podtypu
(TIn => TOut) :>
(TIn => TOut is Adj), (TIn => TOut is Ctl) :>
(TIn => TOut is Adj + Ctl)
kde A :> B
označuje, že B
je podtyp .A
Formulace odlišně, B
je více omezující než A
takový, že hodnota typu B
může být použita všude, kde je požadována hodnota typu A
. Pokud volatelný argument závisí na argumentu (položce) typu A
, může být argument typu B
bezpečně nahrazen, protože pokud poskytuje všechny potřebné funkce.
Tento druh polymorfismu se vztahuje na řazené kolekce členů v tom, že kolekce členů typu B
je podtypem typu A
řazené kolekce členů, pokud obsahuje stejný počet položek a typ každé položky je podtyp odpovídajícího typu položky v A
. To se označuje jako podtypy hloubky. V současné době neexistuje podpora podtypů šířky, to znamená, že neexistuje žádný vztah podtypu mezi dvěma typy definovanými uživatelem nebo uživatelem definovaným typem a jakýmkoli předdefinovaný typ. Tomu brání existence operátoru unwrap
, který umožňuje extrahovat kolekci členů obsahující všechny pojmenované a anonymní položky.
Poznámka
Pokud jde o volatelné položky, zpracovává-li volatelný argument typu A
, je také schopen zpracovat argument typu B
. Pokud se volatelný argument předá jinému volatelnému, musí být schopen zpracovat vše, co může podpis typu vyžadovat. To znamená, že pokud volatelný potřebuje být schopen zpracovat argument typu B
, lze bezpečně předat jakýkoli volatelný argument, který je schopen zpracovat obecnější argument typu A
. Naopak očekáváme, že pokud požadujeme, aby předaná volatelná vrátila hodnotu typu A
, pak je příslib vrácení hodnoty typu B
dostačující, protože tato hodnota poskytne všechny potřebné funkce.
Operace nebo typ funkce je kontravariantní ve svém typu argumentu a kovariantní ve návratovém typu.
A :> B
z toho vyplývá, že pro jakýkoli typ betonu T1
,
(B → T1) :> (A → T1), and
(T1 → A) :> (T1 → B)
kde →
zde může znamenat buď funkci, nebo operaci, a vynecháme všechny poznámky k charakteristikám.
Nahrazení a (T2 → A)
v uvedeném pořadí a nahrazení B
(A → T2)
a (T2 → B)
v uvedeném pořadí vede k závěru, že pro každý typ betonu T2
,A
(B → T2)
((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))
Z indukce vyplývá, že každá další zprostředkuje odchylku typu argumentu a ponechá rozptyl návratového typu beze změny.
Poznámka
To také jasně vysvětluje, jaké rozptylové chování polí musí být; načítání položek prostřednictvím operátoru přístupu k položkám odpovídá vyvolání funkce typu (Int -> TItem)
, kde TItem
je typ prvků v poli. Vzhledem k tomu, že tato funkce je implicitně předána při předávání pole, z toho vyplývá, že pole musí být kovariantní ve svém typu položky. Stejné aspekty platí i pro řazené kolekce členů, které jsou neměnné a proto kovariantní vzhledem ke každému typu položky.
Pokud by pole nebyla neměnná, existence konstruktoru, který by vám umožnil nastavit položky v matici, a tedy přijmout argument typu TItem
, by znamenala, že pole musí být také kontravariantní. Jedinou možností pro datové typy, které podporují získávání a nastavení položek, je tedy invariantní, což znamená, že neexistuje žádný vztah podtypů; B[]
není podtyp , A[]
i když B
je podtyp .A
Navzdory skutečnosti, že pole v Q# souboru jsou neměnná, jsou spíše invariantní než kovariantní. To například znamená, že hodnotu typu (Qubit => Unit is Adj)[]
nelze předat volatelné hodnotě, která vyžaduje argument typu (Qubit => Unit)[]
.
Zachování invariantní matice umožňuje větší flexibilitu související s tím, jak jsou pole zpracovávána a optimalizována v modulu runtime, ale v budoucnu může být možné to změnit.