68

Haskell Wikiは実存型の使い方をうまく説明していますが、私はそれらの背後にある理論を完全には理解していません。

実存型のこの例を考えてみましょう。

data S = forall a. Show a => S a      -- (1)

に変換できるものの型ラッパーを定義しますString。ウィキは、私たちが本当に定義したいのは次のようなタイプであると述べています

data S = S (exists a. Show a => a)    -- (2)

つまり、真の「存在する」型-大まかに言うと、これは「データコンストラクターはインスタンスが存在するSすべての型を取り、それをラップする」と言っていると思います。実際、GADTは次のように書くことができます。Show

data S where                          -- (3)
    S :: Show a => a -> S

私はそれをコンパイルしようとはしていませんが、うまくいくはずです。私にとって、GADTは明らかに私たちが書きたいコード(2)と同等です。

しかし、なぜ(1)が(2)と同等であるのかは私にはまったくわかりません。データコンストラクターを外部に移動すると、がに変わるのはなぜforallですかexists

私が考えることができる最も近いものは、論理のド・モルガンの法則です。ここでは、否定と数量詞の順序を入れ替えると、存在記号が全称記号に変わり、その逆も同様です。

¬(∀x. px) ⇔ ∃x. ¬(px)

しかし、データコンストラクターは、否定演算子とはまったく異なる獣のようです。

forall存在しないタイプの代わりに存在するタイプを定義する機能の背後にある理論は何existsですか?

4

3 に答える 3

60

まず、コンピュータプログラムの型が直観主義論理の公式に対応していると述べている「カリーハワード対応」を見てみましょう。直観主義論理は、学校で学んだ「通常の」論理と同じですが、排中律または二重否定の除去の法則がありません。

  • 公理ではありません:P⇔¬¬P(ただし、P⇒¬¬Pは問題ありません)

  • 公理ではない:P∨¬P

論理の法則

あなたはド・モルガンの法則を順調に進んでいますが、最初にそれらを使用していくつかの新しい法則を導き出します。ド・モルガンの法則の関連するバージョンは次のとおりです。

  • ∀x。P(x)=¬∃x。¬p(x)
  • ∃x。P(x)=¬∀x。¬p(x)

(∀x。P⇒Q(x))=P⇒(∀x。Q(x))を導出できます。

  1. (∀x。P⇒Q(x))
  2. (∀x.¬P∨Q(x))
  3. ¬P∨(∀x。Q(x))
  4. P⇒(∀x.Q)

そして(∀x。Q(x)⇒P)=(∃x。Q(x))⇒P(これは以下で使用されます):

  1. (∀x。Q(x)⇒P)
  2. (∀x.¬Q(x)∨P)
  3. (¬¬∀x.¬Q(x))∨P
  4. (¬∃x。Q(x))∨P
  5. (∃x。Q(x))⇒P

これらの法則は直観主義論理にも当てはまることに注意してください。私たちが導き出した2つの法則は、以下の論文で引用されています。

シンプルタイプ

最も単純なタイプは操作が簡単です。例えば:

data T = Con Int | Nil

コンストラクターとアクセサーには、次の型アノテーションがあります。

Con :: Int -> T
Nil :: T

unCon :: T -> Int
unCon (Con x) = x

型構築子

それでは、型構築子に取り組みましょう。次のデータ定義を取ります。

data T a = Con a | Nil

これにより、2つのコンストラクターが作成されます。

Con :: a -> T a
Nil :: T a

もちろん、Haskellでは、型変数は暗黙的に全称記号であるため、実際には次のようになります。

Con :: ∀a. a -> T a
Nil :: ∀a. T a

そして、アクセサも同様に簡単です。

unCon :: ∀a. T a -> a
unCon (Con x) = x

定量化されたタイプ

存在記号∃を元の型(型コンストラクターなしの最初の型)に追加しましょう。ロジックのように見えない型定義に導入するのではなく、ロジックのように見えるコンストラクター/アクセサー定義に導入します。後で一致するようにデータ定義を修正します。

の代わりにInt、を使用します∃x. t。ここに、tある種の型式があります。

Con :: (∃x. t) -> T
unCon :: T -> (∃x. t)

ロジックのルール(上記の2番目のルール)に基づいて、タイプをCon次のように書き換えることができます。

Con :: ∀x. t -> T

存在記号を外側(冠頭標準形)に移動すると、全称記号に変わりました。

したがって、以下は理論的に同等です。

data T = Con (exists x. t) | Nil
data T = forall x. Con t | Nil

existsHaskellに構文がないことを除いて。

非直観主義論理では、次のタイプから次のことを導き出すことができますunCon

unCon :: ∃ T -> t -- invalid!

これが無効である理由は、そのような変換が直観主義論理で許可されていないためです。したがってunCon、キーワードなしで型を記述するexistsことは不可能であり、型署名を冠頭標準形にすることは不可能です。このような条件で型チェッカーが終了することを保証するのは難しいので、Haskellは任意の存在記号をサポートしていません。

ソース

「型推論による一流のポリモーフィズム」、Mark P. Jones、プログラミング言語の原理に関する第24回ACM SIGPLAN-SIGACTシンポジウムの議事録(web

于 2012-05-25T11:53:21.437 に答える
11

PlotkinとMitchellは、彼らの有名な論文で実存型のセマンティクスを確立しました。これは、プログラミング言語の抽象型と論理の実存型を結び付けました。

ミッチェル、ジョンC .; プロトキン、ゴードンD .; 抽象型には実存型がある、プログラミング言語とシステムに関するACMトランザクション、Vol。10、No. 3、1988年7月、pp。470–502

大まかに言えば、

抽象データ型の宣言は、Ada、Alphard、CLU、MLなどの型付きプログラミング言語で表示されます。この形式の宣言は、識別子のリストを、関連する操作を持つ型にバインドします。これは、データ代数と呼ばれる複合「値」です。2次型付きラムダ計算SOLを使用して、データ代数に型を指定し、パラメーターとして渡し、関数呼び出しの結果として返す方法を示します。その過程で、抽象データ型宣言のセマンティクスについて説明し、型付きプログラミング言語と建設的ロジックの関係を確認します。

于 2012-05-25T12:07:58.130 に答える
10

リンクしたhaskellwikiの記事に記載されています。数行のコードとコメントを借りて、説明しようと思います。

data T = forall a. MkT a

ここにT型コンストラクターを持つ型がありますMkT :: forall a. a -> Tよね?MkTは(大まかに)関数であるため、可能なすべてのタイプaに対して、関数MkTはタイプを持ちa -> Tます。したがって、そのコンストラクターを使用することで、のような値を作成できることに同意します。[MkT 1, MkT 'c', MkT "hello"]これらはすべて、型の値Tです。

foo (MkT x) = ... -- what is the type of x?

しかし、(パターンマッチングなどを介して)内包された値を抽出しようとするとどうなりますTか?その型注釈はT、実際に含まれている値の型への参照なしで、と言うだけです。私たちは、それが何であれ、それが1つの(そして唯一の)タイプを持つという事実にのみ同意することができます。Haskellでこれをどのように述べることができますか?

x :: exists a. a

aこれは単に、にx属する型が存在することを示しています。この時点で、の定義を削除しforall aMkTラップされた値のタイプ(つまり)を明示的に指定するexists a. aことで、同じ結果を達成できることは明らかです。

data T = MkT (exists a. a)

例のように実装された型クラスに条件を追加した場合も、結論は同じです。

于 2012-05-25T11:34:08.837 に答える