サブタイプと変性
Q# でサポートされている変換機構はごくわずかです。 暗黙的な変換は、2 項演算子を適用する場合、条件式を評価する場合、および配列リテラルを構築する場合にのみ発生します。 このような場合、共通のスーパータイプが判別され、必要な変換が自動的に実行されます。 このような暗黙の変換とは別に、関数呼び出しによる明示的なメッセージ変換が可能であり、多くの場合必要になります。
現時点では、存在するサブタイプ関係のみが演算に適用されます。 直感的には、必要な一連の関手以上をサポートする演算を置換できるということはあたりまえに感じられるでしょう。 具体的には、次の2つの具象型 TIn
と TOut
に対して、サブタイプの関係は次のようになります。
(TIn => TOut) :>
(TIn => TOut is Adj), (TIn => TOut is Ctl) :>
(TIn => TOut is Adj + Ctl)
ここで、A :> B
は、B
がの A
サブタイプであることを示しています。 言い方を変えれば、B
は A
よりも拘束的であり、B
型の値は、A
の値が必要な場合にいつでも使用できます。 callable が、引数 (item) が A
型であることに依存している場合、型 B
の引数を安全に置き換えることができます。これは、必要な機能がすべて提供されているためです。
この種のポリモーフィズムは、タプルにも拡張され、型 B
のタプルは、同じ数の項目を含み、各項目の型が A
内の対応する項目型のサブタイプである場合、タプル型 A
のサブタイプになります。 これは、"depth subtyping (深さサブタイピング)" と呼ばれます。 現在、"幅のサブタイピング" はサポートされていません。2 つのユーザー定義型またはユーザー定義型と組み込み型の間には、サブタイプの関係はありません。 すべての名前付きおよび匿名の項目を含むタプルを抽出できるようにする unwrap
演算子の存在により、これが妨げられます。
Note
callable に関して、callable が A
型の引数を処理する場合、B
型の引数を処理することもできます。 callable は、別の callable に引数として渡される場合、その型のシグネチャで必要となりうるすべてのものを処理できる必要があります。 これは、callable が型 B
の引数を処理できる必要がある場合、型 A
のより汎用的な引数を処理できる callable であれば、安全に渡すことができることを意味します。 反対に、渡された callable が A
型の値を返す必要がある場合、型 B
の値を返すという保証で十分です。その値によって必要なすべての機能が提供されるからです。
演算または関数の型は、その引数の型では "反変" で、戻り値の型では "共変" です。 したがって、A :> B
は、すべての具象型 T1
に対して、以下のようになります。
(B → T1) :> (A → T1), and
(T1 → A) :> (T1 → B)
ここで、→
は、関数または演算を意味し、特性の注釈は省略しています。
A
を (B → T2)
と (T2 → A)
にそれぞれ置き換え、B
を (A → T2)
と (T2 → 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))
帰納により、間接を追加するたびに引数の型の変性が逆になり、戻り値の型の変性は変化しません。
Note
これにより、配列の変性動作をどのようにすべきかも明確になります。項目アクセス演算子を使用した項目の取得は、(Int -> TItem)
型の関数の呼び出しに対応します。ここで、TItem
は配列内の要素の型です。 この関数は配列を渡すときに暗黙的に渡されるため、その配列は、項目の型で共変である必要があります。 同様の考慮事項がタプルについても当てはまります。タプルは変更できないため、各項目の型に関して共変になります。
配列が不変でない場合、配列内の項目の設定を許可するコンストラクトが存在し、TItem
型の引数を受け取る場合、配列も反変である必要があることを意味します。 したがって、項目の取得と設定をサポートするデータ型の唯一のオプションは、"不変" であることです。つまりサブタイピングの関係はいっさい存在しません。B[]
は、B
が A
のサブタイプである場合でも、A[]
のサブタイプでは "ありません"。
Q# の配列は変更不可であるにもかかわらず、共変ではなく不変です。 これは、たとえば、(Qubit => Unit is Adj)[]
型の値を、(Qubit => Unit)[]
型の引数を必要とする callable に渡すことができないことを意味します。
配列を不変に保つことにより、ランタイムでの配列の処理方法や最適化方法に関連する柔軟性が向上しますが、将来の変更が可能になる可能性があります。