Sdílet prostřednictvím


Podtypy a variance

Q# podporuje pouze několik mechanismů převodu. Implicitní převody mohou nastat pouze při použití binárních operátorů, vyhodnocování podmíněných výrazů nebo 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 explicitní převody prostřednictvím volání funkcí možné a často nezbytné.

V současné době platí jediný vztah podtypů, který existuje pro operace. Intuitivně má smysl, že by měla být povolena náhrada operace, která podporuje více než požadovanou sadu functorů. Konkrétně platí, že pro všechny dva konkrétní typy TIn a TOutje podtypový vztah

    (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. Frázi se liší, B je více omezující než A tak, aby se hodnota typu B použila všude, kde je požadována hodnota typu A. Pokud volatelný spoléhá na argument (položku) typu A, může být argument typu B bezpečně nahrazen, protože pokud poskytuje všechny nezbytné funkce.

Tento druh polymorfismu se rozšiřuje na řazené kolekce členů v tom, že řazená kolekce členů typu B je podtyp typu řazené kolekce členů A 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 hloubkové podtypování. V současné době není podporována podtyp šířka, to znamená, že neexistuje vztah podtypu mezi žádnými dvěma typy struct nebo struct typu a jakýmkoli předdefinovaný typ. Existence operátoru unwrap, který umožňuje extrahovat řazenou kolekci členů obsahující všechny pojmenované položky, to zabrání.

Poznámka:

Pokud jde o volatelné, zpracovává-li volatelný argument typu A, je také schopen zpracovat argument typu B. Pokud se volatelný předá jako argument jinému volatelnému, musí být schopen zpracovat cokoli, co může podpis typu vyžadovat. To znamená, že pokud volatelný musí být schopen zpracovat argument typu B, všechny volatelné, které jsou schopné zpracovat obecnější argument typu A lze bezpečně předat. Naopak očekáváme, že pokud požadujeme, aby předaný volatelný vrátil hodnotu typu A, pak příslib vrátit hodnotu typu B je 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 svém návratovém typu. A :> B tedy znamená, že pro jakýkoli konkrétní typ T1,

    (B → T1) :> (A → T1), and
    (T1 → A) :> (T1 → B) 

kde zde může znamenat funkci nebo operaci a vynecháme všechny poznámky pro charakteristiky. Nahrazení A(B → T2) a (T2 → A) a nahrazení B(A → T2) a (T2 → B) vede k závěru, že pro jakýkoli konkrétní typ 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)) 

Indukcí vyplývá, že každá další nepřímost obrátí rozptyl typu argumentu a ponechá rozptyl návratového typu beze změny.

Poznámka:

To také jasně vysvětluje, jaké chování rozptylu polí musí být; načítání položek prostřednictvím přístupového operátoru položky 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, následuje, že pole musí být kovariantní v jejich typu položky. Stejné úvahy platí také pro řazené kolekce členů, které jsou neměnné a tedy kovariantní vzhledem k jednotlivým typům položek. Pokud pole nebyla neměnná, existence konstruktoru, který by vám umožnil nastavit položky v matici, a tedy vzít argument typu TItem, by znamenalo, že matice musí být také kontravariantní. Jedinou možností datových typů, 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# jsou neměnné, jsou invariantní, nikoli kovariantní. To znamená, že například hodnotu typu (Qubit => Unit is Adj)[] nelze předat volatelnému, který vyžaduje argument typu (Qubit => Unit)[]. Udržování invariantní matic umožňuje větší flexibilitu související s tím, jak se pole zpracovávají a optimalizují v modulu runtime, ale v budoucnu je možné je upravit.