共変性と反変性 (計算機科学)

（コンピュータプログラミングの型システムでの）共変性と反変性（きょうへんせいとはんぺんせい、covariance and contravariance）とは、データコンテナのサブタイプ関係が、そのデータ要素のサブタイプ関係に連動して定義されるという概念を指す。また、関数の型のサブタイプ関係での、引数型と返り値型の汎化特化の制約を定義する概念でもある。ジェネリックなデータ構造、関数の型、クラスのメソッド、ジェネリックなクラス、ジェネリック関数などに適用されている。
共変性と反変性は、圏論由来の用語である。この用語には以下の概念がある。
総称化データ構造の事例.
総称化データ構造での共変性と反変性は、総称化されたデータ要素のサブタイプ関係を、そのコンテナであるデータ構造のサブタイプ関係にどのように反映させるのかを定義するものである。総称化データ構造は、ジェネリッククラスとして実装されることが多い。List・Set・Mapなどが代表である。
総称化コンテナはcodice_8のように書式される。ここでcodice_9をcodice_10のサブタイプとすると、codice_11とcodice_12のサブタイプ関係は、以下のようになる。
関数の型の事例.
での共変性と反変性は、そのサブタイプでのパラメータ型とリターン型の汎化特化を制約して、サブタイピングの型安全性を実現するための概念になる。
本節では幾つかの例から説明する。関数の型はcodice_24と書式される。記号codice_1は、codice_26を表わす。基底側の関数を派生側の関数で安全に代替できることを、関数の型の型安全性と言う。
ここで型codice_27 とすると、関数codice_28への関数codice_29の代入は、その反対よりも安全なので、codice_30が推奨される。パラメータ型が同一ならば、リターン型のサブタイプ関係をそのまま関数の型のサブタイプ関係に反映できる。これは共変である。
パラメータ型の方は事情が異なり、関数codice_28と関数codice_32の、どちらを代入先の基底型にするべきかという疑問が提起されていた。とによって、codice_33の方が型安全と結論付けられている。これは反変である。
パラメータ型とリターン型のコンビはやや複雑になる。ここでパラメータ型をcodice_27とし、リターン型をcodice_35とすると、その関数の型では、codice_36よりも、codice_37の方が、型安全という結論になっている。この辺りは代数学からの考え方になっている。
これはジェネリック関数でも用いられて、codice_38 のように構文化される。codice_39は反変記号、codice_40は共変記号である。関数codice_41の各インスタンスは、型引数を反映したサブタイプ関係で結ばれる。
一般的な規則は以下となる。
推論規則の記法を使うと以下のように書ける。
クラスの継承の事例.
共変性と反変性はクラスの継承でよく用いられる。ジェネリッククラスの継承の共変性反変性は、総称化データ構造の共変性反変性と似た用法になる。クラスのメソッドの継承の共変性反変性は、関数の型の共変性反変性と似た用法になる。
共変性反変性で枠組みされたクラスのメソッドの継承の型安全性を、バーバラ・リスコフはの概念で説明している。歴代オブジェクト指向言語でのメソッドの継承の共変性反変性は、下のように変遷している。Eiffel（86年発表）のパラメータ型は共変だったようだが、リスコフの置換原則（94年発表）で反変に路線修正されている。
形式的定義.
プログラミング言語の型システムにおいて、型構築子等が、
この区分は、クラス階層におけるメソッドの引数および戻り値の型を検討するときに重要である。C++のようなオブジェクト指向言語においては、クラス "B" がクラス "A" のサブタイプであるとき、"B" のメンバー関数はいずれも、戻り値の型集合が "A" のものと同じかより小さくなければならない。すなわち戻り値の型は共変である。一方、"B" のメンバー関数のとりうる引数の型集合が、"A" のものと同じかより大きいとき、引数の型は反変である。"B" のインスタンスにとって問題なのは、どうすれば "A" のインスタンスを完全に置換可能かということである。型安全性と置換可能性を保証する唯一の方法は、入力に対しては "A" と同等かより寛容に、出力に対しては "A" と同等かより厳格に振る舞うことである。ただし、すべてのプログラミング言語があらゆる文脈でこの2つの性質を保証しているわけではなく、不必要に厳格なものもある。つまり、特定の文脈においては共変性や反変性をサポートしないことがある。
例.
典型的な例を示す:
オブジェクト指向プログラミングにおいては、サブクラスでメソッドをオーバーライドした場合、置換が暗黙的に行われる。すなわち、元のコードで古いメソッドを呼び出すと、新しいメソッドが代わりに実行される。どのような形式のオーバーライドを許容するか、オーバーライドされたメソッドの型がどのように変化するかは、プログラム言語によって様々である。
クラスにおける型の同等性は、継承の階層関係によって暗黙的に示される (そしてこれこそが、継承を行う正当な理由である)。しかしながら、派生クラスでの変更によってはこの表明に違反する可能性があるため、プログラミング言語のなかには、特定の状況下でのこの暗黙の同等性に関する前提を限定するものもある。
C# 3.0 の総称型パラメータは共変性も反変性もサポートしていない。IEnumerable<TypeDerivedFromA> は IEnumerable に代入できそうにみえるが、代入可能でない。C# 4.0 ではこれがサポートされるようになった。なお、普通の配列型は、.NET の導入以来、常に共変性と反変性をサポートしつづけている (厳密に保証されているわけではない。配列の代入が正当に行われても、実行時に例外が発生する可能性がある)。
圏論との関係.
サブタイプ関係を射として型の集まり "C" を圏と見ることができる。
プログラム上で例えば型 "p" の値を受け取って型 "r" の値を返す関数を定義したとすると、型システムにおいては関数の型「"p" →"r" 」を生成したことになる。このような関数の型の構文（型構築子）は、2つの型から新たな型を生成する写像 "F" : "C" ×"C" → "C" と考えられる。
関数の型のルールとして静的型安全なのルールに従うとすると、
この写像 "F" は、第1引数についてはサブタイプ関係を反転して写し（反変関手に相当）、第2引数についてはサブタイプ関係を同じ形のまま写す（共変関手に相当）。