Udostępnij za pośrednictwem


Tworzenie podtypów i wariancja

Q# obsługuje tylko kilka mechanizmów konwersji. Niejawne konwersje mogą wystąpić tylko w przypadku stosowania operatorów binarnych, obliczania wyrażeń warunkowych lub konstruowania literału tablicy. W takich przypadkach określa się wspólny supertyp, a niezbędne konwersje są wykonywane automatycznie. Oprócz takich niejawnych konwersji jawne konwersje za pośrednictwem wywołań funkcji są możliwe i często konieczne.

Obecnie jedyną relacją podtypowania, która istnieje, ma zastosowanie do operacji. Intuicyjnie warto zastąpić operację, która obsługuje więcej niż wymagany zestaw functorów. W przypadku dwóch typów TIn betonowych i TOutrelacji podtypowania jest

    (TIn => TOut) :>
    (TIn => TOut is Adj), (TIn => TOut is Ctl) :>
    (TIn => TOut is Adj + Ctl)

where A :> B wskazuje, że B jest podtypem .A Frazowane inaczej, jest bardziej restrykcyjne niż A takie, B że wartość typu B może być używana wszędzie tam, gdzie wymagana jest wartość typuA. Jeśli obiekt wywołujący opiera się na argumencie (elemencie) typu , argument typu AB można bezpiecznie zastąpić, ponieważ jeśli zapewnia wszystkie niezbędne możliwości.

Ten rodzaj polimorfizmu rozszerza się na krotki w tym, że krotka typu jest podtypem typu BA krotki, jeśli zawiera taką samą liczbę elementów, a typ każdego elementu jest podtypem odpowiedniego typu elementu w A. Jest to nazywane podtypowaniem głębokości. Obecnie nie ma obsługi podtypowania szerokości, czyli nie ma relacji podtypu między dwoma typami zdefiniowanymi przez użytkownika lub typem zdefiniowanym przez użytkownika i dowolnym typem wbudowanym. unwrap Istnienie operatora, który umożliwia wyodrębnianie krotki zawierającej wszystkie nazwane i anonimowe elementy, uniemożliwia to.

Uwaga

W odniesieniu do wywołań, jeśli wywołalny przetwarza argument typu A, jest również w stanie przetworzyć argument typu B. Jeśli obiekt wywołujący jest przekazywany jako argument do innego wywołania, musi być w stanie przetwarzać wszystkie elementy, których może wymagać podpis typu. Oznacza to, że jeśli obiekt wywołujący musi być w stanie przetworzyć argument typu , każde wywołanie, które może przetworzyć bardziej ogólny argument typuBA, można bezpiecznie przekazać. Z drugiej strony oczekujemy, że jeśli wymagamy, aby przekazany obiekt wywołujący zwracał wartość typu , obietnica zwrócenia wartości typu AB jest wystarczająca, ponieważ ta wartość zapewni wszystkie niezbędne możliwości.

Typ operacji lub funkcji jest kontrawariantny w typie argumentu i kowariancie w zwracaniu typu. A :> B w związku z tym oznacza, że w przypadku dowolnego typu T1betonowego ,

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

gdzie tutaj może oznaczać funkcję lub operację i pomijamy wszelkie adnotacje dla cech. Podstawianie A odpowiednio i (T2 → A)(B → T2), i podstawianie B odpowiednio i (A → T2)(T2 → B), prowadzi do wniosku, że dla każdego konkretnego typu 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)) 

Przez indukcję następuje, że każda dodatkowa pośrednia odwraca wariancję typu argumentu i pozostawia wariancję typu zwracanego bez zmian.

Uwaga

Pozwala to również wyjaśnić, jakie jest zachowanie wariancji tablic; pobieranie elementów za pośrednictwem operatora dostępu do elementu odpowiada wywołaniu funkcji typu (Int -> TItem), gdzie TItem jest typem elementów w tablicy. Ponieważ ta funkcja jest niejawnie przekazywana podczas przekazywania tablicy, następuje, że tablice muszą być kowariantne w typie elementu. Te same zagadnienia dotyczą również krotki, które są niezmienne, a tym samym niezmienne w odniesieniu do każdego typu elementu. Gdyby tablice nie były niezmienne, istnienie konstrukcji, która umożliwiłaby ustawianie elementów w tablicy, a tym samym użycie argumentu typu TItemoznaczałoby, że tablice również muszą być kontrawariantne. Jedyną opcją dla typów danych obsługujących pobieranie i ustawianie elementów jest niezmienność , co oznacza, że nie ma żadnej relacji podtypowania; B[]nie jest podtypem A[] , nawet jeśli B jest podtypem A. Pomimo faktu, że tablice w obiekcie Q# są niezmienne, są niezmienne, a nie kowariantne. Oznacza to na przykład, że nie można przekazać wartości typu (Qubit => Unit is Adj)[] do wywołania, która wymaga argumentu typu (Qubit => Unit)[]. Utrzymywanie niezmienności tablic pozwala na większą elastyczność związaną z obsługą i optymalizacją tablic w czasie wykonywania, ale w przyszłości może być możliwe ich skorygowanie.