Nuta
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować się zalogować lub zmienić katalog.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Q# obsługuje tylko kilka mechanizmów konwersji. Niejawne konwersje mogą wystąpić tylko podczas 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 elementów functor. W przypadku dwóch typów TIn i TOutrelacja podtypowania jest
(TIn => TOut) :>
(TIn => TOut is Adj), (TIn => TOut is Ctl) :>
(TIn => TOut is Adj + Ctl)
gdzie A :> B wskazuje, że B jest podtypem A. Frazowane inaczej, B jest bardziej restrykcyjne niż A takie, że wartość typu B może być używana wszędzie tam, gdzie wymagana jest wartość typu A. Jeśli obiekt wywołujący opiera się na argumencie (elemencie) typu A, argument typu B można bezpiecznie zastąpić, ponieważ jeśli zapewnia wszystkie niezbędne możliwości.
Ten rodzaj polimorfizmu rozciąga się na krotki w tym, że krotka typu B jest podtypem typu krotki A, 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 struct lub typem struct i dowolnym typem wbudowanym. Istnienie operatora unwrap, który umożliwia wyodrębnianie krotki zawierającej wszystkie nazwane elementy, zapobiega temu.
Uwaga
Jeśli obiekt wywołujący przetwarza argument typu A, jest również w stanie przetworzyć argument typu B. Jeśli element wywołujący jest przekazywany jako argument do innego elementu wywołującego, 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 B, każde wywołanie, które może przetworzyć bardziej ogólny argument typu A można bezpiecznie przekazać. Z drugiej strony oczekujemy, że jeśli wymagamy, aby przekazane wywołanie zwracało wartość typu A, obietnica zwrócenia wartości typu B jest wystarczająca, ponieważ ta wartość zapewni wszystkie niezbędne możliwości.
Typ operacji lub funkcji jest kontrawariantny w typie argumentu i kowariantny w zwracanym typie.
A :> B z tego względu oznacza, że w przypadku dowolnego typu T1,
(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 (B → T2) i (T2 → A) oraz zastępowanie B odpowiednio (A → T2) i (T2 → B) prowadzi do wniosku, że dla dowolnego 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))
Indukując, następuje, że każda dodatkowa pośrednia odwraca wariancję typu argumentu i pozostawia wariancję zwracanego typu 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, wynika z tego, że tablice muszą być kowariantne w ich typie elementu. Te same zagadnienia dotyczą również krotki, które są niezmienne, a tym samym kowariantne w odniesieniu do każdego typu elementu.
Jeśli tablice nie były niezmienne, istnienie konstrukcji, która pozwoliłaby ustawić elementy w tablicy, a tym samym podjąć argument typu TItem, oznaczałoby to, że tablice również muszą być kontrawariantne. Jedyną opcją dla typów danych, które obsługują pobieranie i ustawianie elementów, jest niezmienne, co oznacza, że nie ma żadnego podtypowania relacji; B[] nie jest podtypem A[], nawet jeśli B jest podtypem A. Pomimo faktu, że tablice w 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 elementu wywołującego, który wymaga argumentu typu (Qubit => Unit)[].
Utrzymywanie niezmienności tablic pozwala na większą elastyczność związaną ze sposobem obsługi i optymalizacji tablic w środowisku uruchomieniowym, ale może być możliwe poprawienie tego w przyszłości.