web-dev-qa-db-ja.com

Haskellにおける存在型と普遍的に定量化された型

これらの違いは何ですか?私は、存在型がどのように機能するか理解していると思います、それらはOOにダウンキャストする方法のない基本クラスを持っているようなものです。ユニバーサル型はどう違うのですか?

54
MFlamer

ここでの「普遍的」および「存在的」という用語は、 述語論理 の同様の名前の数量詞に由来しています。

niversal quantification は通常asとして記述され、「for all」と読むことができ、おおまかに言って、次のように聞こえます。 「...」のは、定量化されているもののセットから選択できるすべての「x」に当てはまります。

存在する数量化 は通常asと書かれ、「存在する」と読むことができ、「。x。..」に似た論理ステートメントでは、「.. 。 "は、定量化されているもののセットから取得された、指定されていない" x "の一部に当てはまります。

Haskellでは、定量化されるものは型(少なくとも特定の言語拡張を無視)であり、論理ステートメントも型であり、「true」ではなく「実装可能」について考えます。

したがって、_forall a. a -> a_のような普遍的に数量化された型は、あらゆる可能な型 "a"に対して、型が_a -> a_である関数を実装できることを意味します。そして、実際にできること:

_id :: forall a. a -> a
id x = x
_

aは普遍的に数値化されているため、それについては何も知りません。したがって、引数を検査することはできません。したがって、idは、その型の唯一の可能な関数です(1)

Haskellでは、普遍的な数量化が「デフォルト」です。シグネチャ内のすべての型変数は暗黙的に普遍的に数量化されます。そのため、idの型は通常_a -> a_としてのみ記述されます。これは parametric polymorphism とも呼ばれ、Haskellでは「ポリモーフィズム」と呼ばれることが多く、「ジェネリック」として知られる他の言語(C#など)でも呼ばれます。

_exists a. a -> a_のようなexistentially数量化されたタイプは、特定のタイプ "a"に対して、関数を実装できることを意味しますタイプは_a -> a_です。任意の関数で十分なので、次のいずれかを選択します。

_func :: exists a. a -> a
func True = False
func False = True
_

...これはもちろんブール値の「not」関数です。しかし、問題は、タイプ "a"について知っているのはそれが存在することだけであるため、それをそのようにuseできないことです。 whichタイプに関する情報は破棄されました。つまり、funcをどの値にも適用できません。

これはあまり役に立ちません。

では、funcできることは何ですか?まあ、それは入力と出力が同じ型の関数であることを知っているので、たとえばそれ自体で構成することができます。基本的に、存在する型を持つもので実行できる唯一のことは、型の存在しない部分に基づいて実行できることです。同様に、タイプ_exists a. [a]_を指定すると、その長さを見つけたり、それ自体に連結したり、一部の要素をドロップしたり、リストに対して実行できるその他のことを行うことができます。

この最後のビットにより、普遍的な量指定子に戻ることができ、Haskellが(2) 存在型を直接持っていない(上記のexistsは完全に架空のものです):存在量で数量化された型を持つものは、普遍的に数量化された型を持つ操作でのみ使用できるため、_exists a. a_をforall r. (forall a. a -> r) -> r--つまり、すべての結果タイプrに対して、すべてのタイプaがタイプaの引数を取り、タイプrの値を返す関数を指定すると、タイプrの結果を取得できます。

それらがほぼ同等である理由が明確でない場合は、全体の型がaに対して普遍的に数量化されているのではなく、それ自体がaに対して普遍的に数量化されている引数を取ります。引数は、選択した特定の型で使用できます。 。


余談ですが、Haskellには通常の意味でのサブタイピングの概念は実際にはありませんが、量化子をサブタイピングの形式を表現するものとして扱うことができます。タイプ_forall a. a_の何かを他のタイプに変換できるため、すべてのサブタイプと見なすことができます。一方、すべての型は_exists a. a_型に変換され、すべての親型になります。もちろん、前者は不可能です(エラーを除いて、タイプ_forall a. a_の値はありません)。後者は役に立たない(タイプ_exists a. a_では何もできません)が、紙の上で類似しています。少なくとも。 :]

存在型と普遍的に数量化された引数の間の同等性は、関数入力で variance が反転するのと同じ理由で機能することに注意してください。


つまり、基本的には、普遍的に数量化された型はどの型でも同じように機能するものを表し、存在型は特定の未知の型で機能するものを表すというものです。


1:まあ、そうではありません-_notId x = undefined_などのエラーを引き起こす関数を無視する場合のみです。 _loopForever x = loopForever x_として。

2:まあ、GHC。拡張機能がないと、Haskellには暗黙のユニバーサル数量詞しかなく、実存型について実際に話す方法はまったくありません。

91
C. A. McCann