コンテンツにスキップ

「共変性と反変性 (計算機科学)」の版間の差分

出典: フリー百科事典『ウィキペディア(Wikipedia)』
削除された内容 追加された内容
Sycgln (会話 | 投稿記録)
Sycgln (会話 | 投稿記録)
見出し
1行目: 1行目:
{{型システム}}{{出典の明記| date = 2021年10月}}{{独自研究|date=2021年10月25日 (月) 01:03 (UTC)}}
{{型システム}}{{出典の明記| date = 2021年10月}}{{独自研究|date=2021年10月25日 (月) 01:03 (UTC)}}
[[コンピュータプログラミング]]の[[型システム]]において、'''共変性'''と'''反変性'''(きょうへんせいとはんぺんせい、covariance and contravariance)は、型の構成と[[サブタイプ]]関係に関する概念である。<!--[[形式手法]]である{{要出典}}。-->[[プログラミング言語]]では、[[ジェネリクス]]、[[派生クラス]]定義<!-- behavioral subtyping になっている場合に限らない。-->、{{仮リンク|関数の型|en|Function type}}などにして共変・反変という言葉が使われている。<!-- これら共変・反変という用語は数学の[[圏論]]に由来する。--><!--[[圏論]]由来の概念 ←用語は圏論由来だろうけど、概念として圏論由来と言っていいかは微妙。-->
[[コンピュータプログラミング]]の[[型システム]]において、'''共変性'''と'''反変性'''(きょうへんせいとはんぺんせい、covariance and contravariance)は、[[データ構造]]や{{仮リンク|関数の|en|Function type}}や[[オブジェクト (プログラミング)|オブジェクト]]の[[サブタイプ|サブタイピング]]に関連した概念である。[[プログラミング言語]]では、[[継承 (プログラミング)|継承]]、[[ジェネリクス]]、[[第一級関数]]などの用法して共変・反変という言葉が使われている。<!-- これら共変・反変という用語は数学の[[圏論]]に由来する。--><!--[[圏論]]由来の概念 ←用語は圏論由来だろうけど、概念として圏論由来と言っていいかは微妙。--><!-- behavioral subtyping は型システムからやや離れた概念も含む。「behavioral subtyping に適した型システム」を考えることはできるが、同一視はできない。 -->
<!-- behavioral subtyping は型システムからやや離れた概念も含む。「behavioral subtyping に適した型システム」を考えることはできるが、同一視はできない。 -->


* '''共変'''(covariant)は、specific <code>≤</code> generic とすると、<code>A ≤ B</code> ならば <code>I<nowiki><A> ≤ I<B></nowiki></code> になる。
* '''共変'''(covariant)は、specific <code>≤</code> generic とすると、<code>A ≤ B</code> ならば <code>I<nowiki><A> ≤ I<B></nowiki></code> になる。
9行目: 8行目:
* '''不変'''(invariant)は、共変・反変・双変のどれでもないことを指す。
* '''不変'''(invariant)は、共変・反変・双変のどれでもないことを指す。


== ジェネリック共変・反変 ==
== 総称共変・反変 ==
[[ジェネリック]]<!--(Generics)-->の共変反変は、型パラメータの[[サブタイプ]]関係それを使って定義される型<!-- それを内包した[[データ構造]] ←コンテナ限らななら言えな -->([[コンテナ (データ型)|コンテナ]]など)う反映されるのかについての概念である。典型的な例はコンテナ型の定義でデータ要素の型を型パラメータとする形である。ここで<code>Cat</code>を<code>Animal</code>の[[サブタイプ]]とすると、<code>Catリスト</code>と<code>Animalリスト</code>のサブタイプ関係はどうなるのか?疑問に答える。
[[ジェネリックプログラミング]]の共変反変の代表的な用法は、ータ要素型の[[継承 (ログラミング)|継承]]関係、それを内包する[[データ構造]]にどのように反映させるかとうものである。ここでのデータ構造はわゆる[[コンテナ (データ型)|コンテナ]](List・Set・Mapなど)どである。ここで<code>Cat</code>を<code>Animal</code>の[[サブタイプ]]とすると、<code>Cat_list</code>と<code>Animal_list</code>のサブタイプ関係はどうなるのか?という疑問に答える。


# '''共変'''(covariant)は、要素型のサブタイプ関係をそのままコンテナに反映させる。例えば<code>Catリスト</code>は<code>Animalリスト</code>のサブタイプになることを指して共変と言う。<code>Animalリスト</code><code>Catリスト</code>で<!--[[置換 (数学) ←違う-->置換可能。{{要説明|date=2021年10月|title=mutableなコンテナは(静的型安全でいくなら)要素型について共変にはならない。immutableなコンテナなら共変になる。}}
# '''共変'''(covariant)は、要素型の継承関係をそのままコンテナに反映させる。<code>Cat_list</code>は<code>Animal_list</code>のサブタイプになる。<code>Animal_list</code>型の変数に、<code>Cat_list</code>型の[[インスタンス]]を代入ようになる。
# '''反変'''(contravariant)は、型パラメータサブタイプ関係を反転させてジェネリック型に反映させる。通常コンテナとしては実用的な意味をなさないが、コンテナの書込み専用インターフェースの型は(要素型に対して)反変にる。<!-- Scala の trait Growable [https://www.scala-lang.org/api/current/scala/collection/mutable/Growable.html] はその形だと思う。 -->
# '''反変'''(contravariant)は、要素型の継承関係を反転させてコンテナに反映させる。ただしデータ構造上反変実用的なのでほとんど用いられない。メソッド付デー構造(いわゆる[[クラス (コンピュタ)|クラ]])上[[メソッド (計算機科学)|メソッド]]のパラメータ反変は用いられるが、その説明は後節先送る。
# '''双変'''(bivariant)は、{{要説明|date=2021年10月|title=静的型安全でいくなら 普通のコンテナは要素型について双変にはならない。}}<!-- 要素型のサブタイプ関係を等価にしてコンテナに反映させる{{要校閲}}。ここでは<code>Catリスト</code><code>Animalリスト</code>のサブイプるのと同時に、<code>Animalリスト</code><code>Catリスト</code>のサブイプにもる。従って双方は互いに置換可能である。-->
# '''双変'''(bivariant)は、<code>Animal_list</code>型の変数に<code>Cat_list</code>インスンスを代入可能るのと同時に、<code>Cat_list</code>型の変数に<code>Animal_list</code>インスンスを代入可能にもる。
# '''不変'''(invariant)は、要素型のサブタイプ関係をコンテナに反映しない。<code>Catリスト</code>は<code>Animalリスト</code>のサブタイプならず従って<code>Animalリスト</code>は<code>Catリト</code>で置換不可である。
# '''不変'''(invariant)は、要素型の継承関係をコンテナに反映しない。従って<code>Animal_list</code>変数に、<code>Cat_list</code>型のインスタンを代入すことは出来ない


== 派生クラス定義にする共変・反変 ==
== 関数の型と共変・反変 ==
関数の型(function type)での共変反変とは、[[第一級関数]]として扱われる各関数のパラメータとリターンにされた値の型の派生関係が、各関数の派生関係にどのような性質で反映されるのかを照会するコンセプトである。その主な目的は、基底側関数に対する派生側関数のより安全な代入(代わりに入れる - substitute)の実現である。
<!-- behavioral subtyping に限定するのは不当。反変パラメータ・共変戻り値 の型システムで型チェックが通るプログラムでも behavioral subtyping になっているとは限らない。 -->
<!-- == 振る舞いサブタイピングと共変・反変 == -->
<!-- == メソッドについて == -->
{{疑問点|date=2021年10月|title=Behavioral subtyping と 言語の型システムでのメソッドシグネチャの制約の話 を混同しているように見える。}}
<!-- Behavioral subtyping と合うように言語の型システムが設計されたということはあるのかもしれないが、 言語の型システムに適合するプログラムでも Behavioral subtyping に沿っているとは限らない。 -->


本節では幾つかの例を示しながら説明する。記号<code>≤</code>は、<code>specific ≤ generic</code>を示す。
振る舞いサブタイピング(Behavioral subtyping)は、[[リスコフの置換原則]]で重視されるようになった[[継承 (プログラミング)|継承]]デザインである。振る舞いに焦点を当てたオブジェクトの安全な継承のガイドラインである。振る舞いとは[[オブジェクト (プログラミング)|オブジェクト]]のメッセージレシーバーまたは各メソッドで体現される概念である。リスコフの置換原則に伴なう[[契約によ設計]]では、レシーバーへのメッセージ及びメソッドのパラメータ型は事前条件なりレシーバー結果オブジェクト及びメソッドのリターン型は事後条件になって、双方が共変性と反変性の対象にされる。

ここで<code>Cat ≤ Animal</code> とすると、関数<code>Animal->Animal</code>に対する<code>Animal->Cat</code>の代入は、その逆方向よりも安全なので、<code>(Animal->Cat) ≤ (Animal->Animal)</code>が推奨される。パラメータが不変ならば、リターンの派生関係をそのまま関数のそれに反映できる。これは共変である。


パラメータの方は事情が異なり、関数<code>Animal->Animal</code>と<code>Cat->Animal</code>の、どちらを代入先(基底側)にするべきかという疑問が存在した。{{仮リンク|ジョン・レイナルド|en|John C. Reynolds}}<ref>{{cite conference|first=John C.|last=Reynolds|title=The Essence of Algol|conference=Symposium on Algorithmic Languages|year=1981|url=https://www.cs.cmu.edu/~crary/819-f09/Reynolds81.ps|publisher=North-Holland}}</ref>と{{仮リンク|ルカ・カーデリ|en|Luca Cardelli}}<ref>{{cite conference|first=Luca|last=Cardelli|title=A semantics of multiple inheritance|conference=Semantics of Data Types (International Symposium Sophia-Antipolis, France, June 27–29, 1984)|year=1984|url=http://lucacardelli.name/Papers/Inheritance%20(Semantics%20of%20Data%20Types).pdf|series=Lecture Notes in Computer Science|volume=173|publisher=Springer|pages=51–67|isbn=3-540-13346-1|doi=10.1007/3-540-13346-1_2}}Longer version: {{cite journal|last=Cardelli|first=Luca|date=February 1988|title=A semantics of multiple inheritance|journal=Information and Computation|volume=76|issue=2/3|pages=138–164|doi=10.1016/0890-5401(88)90007-7|author-mask=1|citeseerx=10.1.1.116.1298}}</ref>によって<code>(Animal->Animal) ≤ (Cat->Animal)</code>の方が安全と結論付けられている。これは反変である。
ここで<code>T'</code>を<code>T</code>のサブクラスとすると(<code>T</code>は基底、<code>T'</code>は派生){{疑問点範囲|振る舞いサブタイピング共変反変はこう図式化でき|date=2021年10月}}。<gallery perrow="5" heights="180">

パラメータとリターンのコンビはやや複雑になる。ここでパラメータ型を<code>Ts ≤ Tg</code>とし、リターン型を<code>Ss ≤ Sg</code>とすると、その関数の型では<code>(Ts->Ss) ≤ (Tg->Sg)</code>よりも<code>(Tg->Ss) ≤ (Ts->Sg)</code>の方が、基底側に対する派生側のより安全な代入ができるという結論になっている。

関数の型の共変反変は、ジェネリック関数でも用いられて、<code>S func[-T, +S] (T x, T y) { ... }</code> のように書式される。<code>-</code>は反変記号、<code>+</code>は共変記号である。この関数<code>func</code>の各インスタンスは、与えられる型パラメータに沿った派生関係で結ばれる。

パラメータ型とリターン型の共変反変の適切な用い方安全性が保証された[[第一級関数]]の代入(代わりに入れる - substitute)は、[[Substructural型システム]](英語版)や[[モナド (プログラミング)|モナド]]などのアルゴリズム様々に扱われている。<!-- {{正確性|date=2021年11月|section=1|float, double は例として不適切。(変換の演算が絡み、型の包含関係ではない)}} --><!-- ↓まるっきりおかしい。たぶん型変換(キャスト等)と混同している。
関数の型(function type)では、共変性と反変性は、型の狭い方から広い方への順序付けと、その間の相互可換性、または特定の状況下 (引数や総称型、戻り値など) での同等性を指している。
* '''共変 ({{en|covariant}}):''' 広い型(例:[[倍精度浮動小数点数|double]])から狭い型(例:[[単精度浮動小数点数|float]])へ変換する(できる)こと。「double->float」「{a,b,c,d}->{a,b}」
* '''反変 ({{en|contravariant}}):''' 狭い型(例:[[単精度|float]])から広い型(例:[[倍精度|double]])へ変換する(できる)こと。「float->double」「{a,b}->{a,b,c,d}」
* '''不変 ({{en|invariant}}):''' 型を変換できないこと。
* '''双変 ({{en|bivariant}}):''' 広い型にも狭い型にも変換できること。

例えば、取り得る値が {a,b,c,d} である型は、取り得る値が {a,b} しかない型より広い。よって、型変換 {a,b,c,d}->{a,b} (double 型の値を float 型を期待している関数に渡した場合など) は共変変換である。同様に、型変換 {a,b}->{a,b,c,d} (float を返す関数を double を返す関数の代わりに呼び出した場合など) は関数の反変変換である (関数の型は戻り値の型)。

複数の同等でない型が、同一の値を取り得ることに注意すること。例えば、取り得る値が {a,b} である型と {b,c} である型は互いに同等でないが、取り得る値が {b} である型とは、それぞれ {b}->{a,b} および {b}->{b,c} となり、いずれも同等である。
-->

== 派生型の振る舞いと共変・反変 ==
<!-- behavioral subtyping に限定するのは不当。反変パラメータ・共変戻り値 の型システムで型チェックが通るプログラムでも behavioral subtyping になっているとは限らない。 --><!-- Behavioral subtyping と合うように言語の型システムが設計されたということはあるのかもしれないが、 言語の型システムに適合するプログラムでも Behavioral subtyping に沿っているとは限らない。 -->
振る舞いサブタイピング(Behavioral subtyping)は、[[リスコフの置換原則]]で重視されるようになった[[継承 (プログラミング)|継承]]デザインである。振る舞いに焦点を当てたオブジェクトの好ましい継承のガイドラインである。振る舞いとは[[オブジェクト (プログラミング)|オブジェクト]]の[[ド (計算機科学)|メソッド]]を指している。舞いサブタイピングでは、継承されるメソッドのパラメータ型とリターン型の仕様対して前節関数の型の共変反変のコンセプトが適用されている。その目的は、派生型オブジェクトが代入される基底型変数の振る舞い(各メソッド整合性・堅牢・安全性を維持するこである。

ここで<code>T'</code>を<code>T</code>のサブクラスとすると(<code>T</code>は基底、<code>T'</code>は派生)図式化される。<gallery perrow="5" heights="180">
ファイル:Inheritance invariant.svg|メソッドシグネチャを不変にした継承<!--(型安全)-->
ファイル:Inheritance invariant.svg|メソッドシグネチャを不変にした継承<!--(型安全)-->
ファイル:Inheritance covariant return.svg|メソッドのリターン型を共変にした継承<!--(型安全)-->
ファイル:Inheritance covariant return.svg|メソッドのリターン型を共変にした継承<!--(型安全)-->
53行目: 72行目:
|共変
|共変
|}
|}

== 関数の型と共変・反変 ==
関数の型(function type)での共変反変とは、[[第一級関数]]として扱われる各関数のパラメータとリターンにされた値の型の派生関係が、各関数の派生関係にどのような性質で反映されるのかを照会するコンセプトである。その主な目的は、基底側関数に対する派生側関数のより安全な代入(代わりに入れる - substitute)の実現である。

本節では幾つかの例を示しながら説明する。記号<code>≤</code>は、<code>specific ≤ generic</code>を示す。

ここで<code>Cat ≤ Animal</code> とすると、関数<code>Animal->Animal</code>に対する<code>Animal->Cat</code>の代入は、その逆方向よりも安全なので、<code>(Animal->Cat) ≤ (Animal->Animal)</code>が推奨される。パラメータが不変ならば、リターンの派生関係をそのまま関数のそれに反映できる。これは共変である。

パラメータの方は事情が異なり、関数<code>Animal->Animal</code>と<code>Cat->Animal</code>の、どちらを代入先(基底側)にするべきかという疑問が存在した。{{仮リンク|ジョン・レイナルド|en|John C. Reynolds}}と{{仮リンク|ルカ・カーデリ|en|Luca Cardelli}}によって<code>(Animal->Animal) ≤ (Cat->Animal)</code>の方が安全と結論付けられている。これは反変である。

パラメータとリターンのコンビはやや複雑になる。ここでパラメータ型を<code>Ts ≤ Tg</code>とし、リターン型を<code>Ss ≤ Sg</code>とすると、その関数の型では<code>(Ts->Ss) ≤ (Tg->Sg)</code>よりも<code>(Tg->Ss) ≤ (Ts->Sg)</code>の方が、基底側に対する派生側のより安全な代入ができるという結論になっている。

関数の型の共変反変は、ジェネリック関数でも用いられて、<code>S func[-T, +S] (T x, T y) { ... }</code> のように書式される。<code>-</code>は反変記号、<code>+</code>は共変記号である。この関数<code>func</code>の各インスタンスは、与えられる型パラメータに沿った派生関係で結ばれる。

パラメータ型とリターン型の共変反変で安全性が保証された第一級関数の代入(代わりに入れる - substitute)アルゴリズムは、[[Substructural型システム]](英語版)や[[モナド (プログラミング)|モナド]]などで用いられている。<!-- {{正確性|date=2021年11月|section=1|float, double は例として不適切。(変換の演算が絡み、型の包含関係ではない)}} -->
<!-- ↓まるっきりおかしい。たぶん型変換(キャスト等)と混同している。
関数の型(function type)では、共変性と反変性は、型の狭い方から広い方への順序付けと、その間の相互可換性、または特定の状況下 (引数や総称型、戻り値など) での同等性を指している。
* '''共変 ({{en|covariant}}):''' 広い型(例:[[倍精度浮動小数点数|double]])から狭い型(例:[[単精度浮動小数点数|float]])へ変換する(できる)こと。「double->float」「{a,b,c,d}->{a,b}」
* '''反変 ({{en|contravariant}}):''' 狭い型(例:[[単精度|float]])から広い型(例:[[倍精度|double]])へ変換する(できる)こと。「float->double」「{a,b}->{a,b,c,d}」
* '''不変 ({{en|invariant}}):''' 型を変換できないこと。
* '''双変 ({{en|bivariant}}):''' 広い型にも狭い型にも変換できること。

例えば、取り得る値が {a,b,c,d} である型は、取り得る値が {a,b} しかない型より広い。よって、型変換 {a,b,c,d}->{a,b} (double 型の値を float 型を期待している関数に渡した場合など) は共変変換である。同様に、型変換 {a,b}->{a,b,c,d} (float を返す関数を double を返す関数の代わりに呼び出した場合など) は関数の反変変換である (関数の型は戻り値の型)。

複数の同等でない型が、同一の値を取り得ることに注意すること。例えば、取り得る値が {a,b} である型と {b,c} である型は互いに同等でないが、取り得る値が {b} である型とは、それぞれ {b}->{a,b} および {b}->{b,c} となり、いずれも同等である。
-->


==形式的定義==
==形式的定義==

2021年11月2日 (火) 11:39時点における版

コンピュータプログラミング型システムにおいて、共変性反変性(きょうへんせいとはんぺんせい、covariance and contravariance)とは、データ構造関数の型英語版オブジェクトサブタイピングに関連した概念である。プログラミング言語では、継承ジェネリクス第一級関数などの用法に対して共変・反変という言葉が使われている。

  • 共変(covariant)は、specific generic とすると、A ≤ B ならば I<A> ≤ I<B> になる。
  • 反変(contravariant)は、共変のリバースであり、A ≤ B ならば I<B> ≤ I<A> になる。
  • 双変(bivariant)は、互いに適用可能になり、A ≤ B ならば I<A> ≡ I<B> になる。
  • 変性(variant)は、共変・反変・双変のどれかであることを指す。
  • 不変(invariant)は、共変・反変・双変のどれでもないことを指す。

総称型と共変・反変

ジェネリックプログラミングでの共変反変の代表的な用法は、データ要素型の継承関係を、それを内包するデータ構造にどのように反映させるかというものである。ここでのデータ構造はいわゆるコンテナ(List・Set・Mapなど)などである。ここでCatAnimalサブタイプとすると、Cat_listAnimal_listのサブタイプ関係はどうなるのか?という疑問に答える。

  1. 共変(covariant)は、要素型の継承関係をそのままコンテナに反映させる。Cat_listAnimal_listのサブタイプになる。Animal_list型の変数に、Cat_list型のインスタンスを代入できるようになる。
  2. 反変(contravariant)は、要素型の継承関係を反転させてコンテナに反映させる。ただしデータ構造上の反変は非実用的なのでほとんど用いられない。メソッド付きデータ構造(いわゆるクラス)上のメソッドのパラメータ型で反変は用いられるが、その説明は後節に先送りする。
  3. 双変(bivariant)は、Animal_list型の変数にCat_list型のインスタンスを代入可能にするのと同時に、Cat_list型の変数にAnimal_list型のインスタンスを代入可能にもする。
  4. 不変(invariant)は、要素型の継承関係をコンテナに反映しない。従ってAnimal_list型の変数に、Cat_list型のインスタンスを代入することは出来ない。

関数の型と共変・反変

関数の型(function type)での共変反変とは、第一級関数として扱われる各関数のパラメータとリターンにされた値の型の派生関係が、各関数の派生関係にどのような性質で反映されるのかを照会するコンセプトである。その主な目的は、基底側関数に対する派生側関数のより安全な代入(代わりに入れる - substitute)の実現である。

本節では幾つかの例を示しながら説明する。記号は、specific ≤ genericを示す。

ここでCat ≤ Animal とすると、関数Animal->Animalに対するAnimal->Catの代入は、その逆方向よりも安全なので、(Animal->Cat) ≤ (Animal->Animal)が推奨される。パラメータが不変ならば、リターンの派生関係をそのまま関数のそれに反映できる。これは共変である。

パラメータの方は事情が異なり、関数Animal->AnimalCat->Animalの、どちらを代入先(基底側)にするべきかという疑問が存在した。ジョン・レイナルド英語版[1]ルカ・カーデリ英語版[2]によって(Animal->Animal) ≤ (Cat->Animal)の方が安全と結論付けられている。これは反変である。

パラメータとリターンのコンビはやや複雑になる。ここでパラメータ型をTs ≤ Tgとし、リターン型をSs ≤ Sgとすると、その関数の型では(Ts->Ss) ≤ (Tg->Sg)よりも(Tg->Ss) ≤ (Ts->Sg)の方が、基底側に対する派生側のより安全な代入ができるという結論になっている。

関数の型の共変反変は、ジェネリック関数でも用いられて、S func[-T, +S] (T x, T y) { ... } のように書式される。-は反変記号、+は共変記号である。この関数funcの各インスタンスは、与えられる型パラメータに沿った派生関係で結ばれる。

パラメータ型とリターン型の共変反変の適切な用い方で、安全性が保証された第一級関数の代入(代わりに入れる - substitute)は、Substructural型システム(英語版)やモナドなどのアルゴリズムで様々に扱われている。

派生型の振る舞いと共変・反変

振る舞いサブタイピング(Behavioral subtyping)は、リスコフの置換原則で重視されるようになった継承デザインである。振る舞いに焦点を当てたオブジェクトの好ましい継承のガイドラインである。振る舞いとはオブジェクトメソッドを指している。振る舞いサブタイピングでは、継承されるメソッドのパラメータ型とリターン型の仕様に対して、前節の関数の型の共変反変のコンセプトが適用されている。その目的は、派生型オブジェクトが代入される基底型変数の振る舞い(各メソッド)の整合性・堅牢性・安全性を維持することである。

ここでT'Tのサブクラスとすると(Tは基底、T'は派生)このように図式化される。

各OOP言語での共変反変の導入は下図のようになっている。Eiffel(86年発表)のパラメータ型(契約による設計の事前条件)は共変だったようだが、リスコフの置換原則(94年発表)で反変に路線修正されたようになっている。

パラメータ型 リターン型
20世紀の典型OOP言語 不変 不変
Eiffel 共変 共変
C++ (98年から), Java(5.0から), C#(9から), D言語 不変 共変
Scala, Sather 反変 共変

形式的定義

プログラミング言語型システムにおいて、型構築子 (type constructor等が、

  • 型の順序関係を維持する (≤ で順序づけたとき、特殊から一般の順になる)[訳語疑問点] とき、共変である (covariant) という。
  • 型の順序関係を反転させるとき、反変である (contravariant) という。
  • 上記いずれにも該当しないとき、不変である (invariant) という。
  • 共変かつ反変のとき、双変である (bivariant) という。

この区分は、クラス階層におけるメソッドの引数および戻り値の型を検討するときに重要である。C++のようなオブジェクト指向言語においては、クラス B がクラス A の派生型であるとき、B のメンバー関数はいずれも、戻り値の型集合が A のものと同じかより小さくなければならない。すなわち戻り値の型は共変である。一方、B のメンバー関数のとりうる引数の型集合が、A のものと同じかより大きいとき、引数の型は反変である。B のインスタンスにとって問題なのは、どうすれば A のインスタンスを完全に置換可能かということである。型安全性と置換可能性を保証する唯一の方法は、入力に対しては A と同等かより寛容に、出力に対しては A と同等かより厳格に振る舞うことである。ただし、すべてのプログラミング言語があらゆる文脈でこの2つの性質を保証しているわけではなく、不必要に厳格なものもある。つまり、特定の文脈においては共変性や反変性をサポートしないことがある。

典型的な例を示す:

  • 要素型から配列型を構築する構文(型構築子)は、通常、基本型に対し共変または不変とされる。共変とする場合、StringObject ならば ArrayOf(String)ArrayOf(Object) である。ただしこれは配列がイミュータブルである場合に限って正しい (静的型安全である)。配列に対する追加操作 (要素を配列に追加する) と取出操作 (要素を配列から取り出す) が許される場合、取出操作は共変 (例えば ArrayOf(String) から Object を取り出せる) であるのに対し、追加操作は反変 (例えば StringArrayOf(Object) に追加できる) である。このように共変性と反変性が競合するため、ミュータブルな配列は基本型に対して不変とする方が安全である。
  • T 型の引数を持つ関数呼び出し (fun f (x : T) : Integer と定義) は、TS のとき、fun g(x: S) : Integer と定義される関数 g で置換可能である。言い換えると、g は、引数の型に関して f より寛容であり、f と同様に Integer を返すので、f をいつでも置換できる。このように、関数引数を許す言語においては、 gff の引数の型とは反変である。
  • 一般的に、結果の型は共変である。

オブジェクト指向プログラミングにおいては、サブクラスメソッドオーバーライドした場合、置換が暗黙的に行われる。すなわち、元のコードで古いメソッドを呼び出すと、新しいメソッドが代わりに実行される。どのような形式のオーバーライドを許容するか、オーバーライドされたメソッドの型がどのように変化するかは、プログラム言語によって様々である。

クラスにおける型の同等性は、継承の階層関係によって暗黙的に示される (そしてこれこそが、継承を行う正当な理由である)。しかしながら、派生クラスでの変更によってはこの表明に違反する可能性があるため、プログラミング言語のなかには、特定の状況下でのこの暗黙の同等性に関する前提を限定するものもある。

C# 3.0 の総称型パラメータは共変性も反変性もサポートしていない。IEnumerable<TypeDerivedFromA> は IEnumerable<A> に代入できそうにみえるが、代入可能でない。C# 4.0 ではこれがサポートされるようになった。なお、普通の配列型は、.NET の導入以来、常に共変性と反変性をサポートしつづけている (厳密に保証されているわけではない。配列の代入が正当に行われても、実行時に例外が発生する可能性がある)。

用語の由来

これらの用語は数学の圏論に由来する。型システムにおける型が圏 C をなし、派生型関係を表すものとする。派生型関係はリスコフの置換原則に従うものとみなす。すなわち、型 t のいかなる式も、型 sst を満たすならば、型 s の式で置き換えることが可能である。

p を受け取って型 r を返す関数を定義すると、型システムにおいてはその関数名と対応づけられた新たな型 pr を生成したことになる。このような関数の型の構文(型構築子)がすなわち、この新たな型を生成する関手 F : C × CC である。リスコフの置換原則から、この関手は、第1引数においては反変で、第2引数においては共変でなければならない。[3]

関連項目

脚注

  1. ^ Reynolds, John C. (1981). The Essence of Algol. Symposium on Algorithmic Languages. North-Holland.
  2. ^ Cardelli, Luca (1984). A semantics of multiple inheritance (PDF). Semantics of Data Types (International Symposium Sophia-Antipolis, France, June 27–29, 1984). Lecture Notes in Computer Science. Vol. 173. Springer. pp. 51–67. doi:10.1007/3-540-13346-1_2. ISBN 3-540-13346-1Longer version: (February 1988). “A semantics of multiple inheritance”. Information and Computation 76 (2/3): 138–164. doi:10.1016/0890-5401(88)90007-7. 
  3. ^ Luca Cardelli, "A semantics of multiple inheritance", Inf. Comput. 76, pp. 138–164, 1988

参考文献

  • Castagna, Giuseppe (May 1995). “Covariance and contravariance: conflict without a cause”. ACM Transactions on Programming Languages and Systems 17 (3): 431–447. doi:10.1145/203095.203096. 

外部リンク