Haskell/GHCの「forall」キーワードは何をしますか?
質問
私はどのように理解し始めています forall
キーワードは、このようないわゆる「実存タイプ」で使用されます。
data ShowBox = forall s. Show s => SB s
しかし、これはどのようにサブセットです forall
使用されており、私は単にこのようなことでその使用に心を包むことはできません:
runST :: forall a. (forall s. ST s a) -> a
または、これらが異なる理由を説明してください:
foo :: (forall a. a -> a) -> (Char, Bool)
bar :: forall a. ((a -> a) -> (Char, Bool))
または全体 RankNTypes
もの...
私は、学術環境で正常な言語の種類よりも、明確で専門用語のない英語を好む傾向があります。これについて読み込もうとしている説明のほとんど(検索エンジンを通じて見つけることができるもの)には、次の問題があります。
- それらは不完全です。彼らは、このキーワードの使用の一部(「実存タイプ」など)を説明しています。
runST
,foo
とbar
その上)。 - 彼らは、今週人気がある別の数学、カテゴリ理論、または抽象的な代数の分野で私が最新のものを読んだという仮定が密集しています。 (「紙を参照してください」という言葉を読んだことがないなら なんでもいい 実装の詳細については、「繰り返しますが、早すぎるでしょう。」
- それらは、頻繁に単純な概念を曲がりくねって骨折した文法とセマンティクスに頻繁に変える方法で書かれています。
それで...
実際の質問に。誰かが完全に説明できます forall
明確でプレーンな英語のキーワード(または、どこかに存在する場合は、私が見逃したような明確な説明を指しています)。
追加するために編集:
以下の高品質の回答から2つの傑出した答えがありましたが、残念ながら私は1つしか選択できません。 ノーマンの答え 詳細で便利で、の理論的基礎を示す方法で物事を説明しています forall
同時に、それの実際的な意味のいくつかを見せてくれます。 Yairchuの答え 他の誰も言及されていない領域(スコープ型変数)をカバーし、コードとGHCIセッションですべての概念を説明しました。両方を最適に選択することができたでしょうか。残念ながら、両方の答えをよく見た後、Yairchuが説明と添付の説明のためにNormanのわずかにエッジを外すことに決めました。しかし、これは少し不公平です。なぜなら、これを理解するために両方の回答が必要だったからです forall
タイプの署名でそれを見たとき、私に恐ろしい感覚を残しません。
解決
コードの例から始めましょう:
foob :: forall a b. (b -> b) -> b -> (a -> b) -> Maybe a -> b
foob postProcess onNothin onJust mval =
postProcess val
where
val :: b
val = maybe onNothin onJust mval
このコードは、プレーンハスケル98にコンパイル(構文エラー)をコンパイルしません。 forall
キーワード。
基本的に、3があります 違う の一般的な用途 forall
キーワード(または少なくともそうです 思われる)、それぞれに独自のHaskell拡張機能があります。 ScopedTypeVariables
, RankNTypes
/Rank2Types
, ExistentialQuantification
.
上記のコードは、有効なもののいずれかで構文エラーを取得しませんが、タイプチェックのみを使用して ScopedTypeVariables
有効になっています。
スコープ型変数:
スコープされたタイプ変数は、内部のコードのタイプを指定するのに役立ちます where
条項。それはになります b
の val :: b
と同じもの b
の foob :: forall a b. (b -> b) -> b -> (a -> b) -> Maybe a -> b
.
紛らわしいポイント: :省略したときにそれを聞くかもしれません forall
タイプから、それは実際にはまだ暗黙的にそこにあります。 (ノーマンの答えから:「通常、これらの言語は多型のタイプからのものを省略します」)。この主張は正しい、 しかし の他の用途を指します forall
, 、そしてではありません ScopedTypeVariables
使用する。
rank-n-types:
それから始めましょう mayb :: b -> (a -> b) -> Maybe a -> b
に相当します mayb :: forall a b. b -> (a -> b) -> Maybe a -> b
, それ外 ときのために ScopedTypeVariables
有効になっています。
これは、それがすべてのために機能することを意味します a
と b
.
このようなことをしたいとしましょう。
ghci> let putInList x = [x]
ghci> liftTup putInList (5, "Blah")
([5], ["Blah"])
これのタイプは何でなければなりません liftTup
?これは liftTup :: (forall x. x -> f x) -> (a, b) -> (f a, f b)
. 。理由を確認するには、コーディングしてみましょう。
ghci> let liftTup liftFunc (a, b) = (liftFunc a, liftFunc b)
ghci> liftTup (\x -> [x]) (5, "Hello")
No instance for (Num [Char])
...
ghci> -- huh?
ghci> :t liftTup
liftTup :: (t -> t1) -> (t, t) -> (t1, t1)
「うーん..なぜGHCは、タプルに同じタイプの2つが含まれている必要があると推測するのですか?
-- test.hs
liftTup :: (x -> f x) -> (a, b) -> (f a, f b)
liftTup liftFunc (t, v) = (liftFunc t, liftFunc v)
ghci> :l test.hs
Couldnt match expected type 'x' against inferred type 'b'
...
うーん。したがって、ここではGHCが応募しません liftFunc
の上 v
なぜなら v :: b
と liftFunc
欲しい x
. 。私たちは本当に私たちの関数が可能なことを受け入れる関数を取得したいです x
!
{-# LANGUAGE RankNTypes #-}
liftTup :: (forall x. x -> f x) -> (a, b) -> (f a, f b)
liftTup liftFunc (t, v) = (liftFunc t, liftFunc v)
だからそうではありません liftTup
それはすべてのために機能します x
, 、それが得る機能です。
実存的な定量化:
例を使用してみましょう:
-- test.hs
{-# LANGUAGE ExistentialQuantification #-}
data EQList = forall a. EQList [a]
eqListLen :: EQList -> Int
eqListLen (EQList x) = length x
ghci> :l test.hs
ghci> eqListLen $ EQList ["Hello", "World"]
2
それはRANK-Nタイプとどう違うのですか?
ghci> :set -XRankNTypes
ghci> length (["Hello", "World"] :: forall a. [a])
Couldnt match expected type 'a' against inferred type '[Char]'
...
rank-n-typesで、 forall a
あなたの表現は可能な限りすべてに適合しなければならないことを意味しました a
s。例えば:
ghci> length ([] :: forall a. [a])
0
空のリストは、あらゆるタイプのリストとして機能します。
したがって、実存的な定量化で、 forall
s in data
定義には、値が含まれることを意味します できる of どれか 適切なタイプではなく、そうではありません しなければならない of すべて 適切なタイプ。
他のヒント
誰でもできます 完全に forallキーワードを明確で平易な英語で説明しますか?
いいえ。 (まあ、たぶんドン・スチュワートはできます。)
これが、簡単で明確な説明の障壁です。 forall
:
それは数量装置です。普遍的なまたは実存的な量子を見て、少なくとも少し論理(述語計算)が必要です。述語の計算を見たことがない場合、または量子に慣れていない場合(そして、快適でない博士課程の予選試験中に学生を見たことがあります)、あなたのために、簡単な説明はありません
forall
.それは タイプ 数量詞。見たことがないなら システムf そして、多型のタイプを書くいくつかの練習をしました、あなたは見つけるつもりです
forall
紛らわしい。 HaskellまたはMLの経験は十分ではありません。通常、これらの言語はforall
多型から。 (私の考えでは、これは言語設計の間違いです。)特にハスケルでは、
forall
私が混乱を感じる方法で使用されています。 (私はタイプ理論家ではありませんが、私の仕事は私に連絡を取ります 多く タイプ理論の、そして私はそれに非常に満足しています。)私にとって、混乱の主な原因はforall
私自身が書きたいタイプをエンコードするために使用されますexists
. 。それは、量子と矢を含む型の型の型の型のビットによって正当化されており、私がそれを理解したいたびに、私は物事を調べて、自分自身の同型を解決しなければなりません。型同型のアイデアに満足していない場合、またはタイプの同型について考える練習がない場合、この使用の使用
forall
あなたをstymieするつもりです。の一般的な概念
forall
常に同じです(タイプ変数を導入するためのバインディング)、さまざまな用途の詳細は大きく異なります。非公式の英語は、バリエーションを説明するための非常に良いツールではありません。何が起こっているのかを本当に理解するには、数学が必要です。この場合、関連する数学はベンジャミンピアスの入門テキストにあります タイプとプログラミング言語, 、これはとても良い本です。
あなたの特定の例については、
runST
したほうがいい 頭を傷つけます。より高いランクのタイプ(矢印の左側から)は、野生ではめったに見られません。紹介した論文を読むことをお勧めしますrunST
: 「怠zyな機能状態スレッド」. 。これは本当に良い論文であり、それはあなたにより良い直感を与えますrunST
特に、一般的に高ランクタイプの場合。説明にはいくつかのページがかかりますが、それは非常によくできており、私はここでそれを凝縮しようとはしません。検討
foo :: (forall a. a -> a) -> (Char,Bool) bar :: forall a. ((a -> a) -> (Char, Bool))
私が電話した場合
bar
, 、あらゆるタイプを選ぶことができますa
私が好きで、タイプから関数を渡すことができますa
入力しa
. 。たとえば、関数を渡すことができます(+1)
または関数reverse
. 。あなたはそれを考えることができますforall
「今すぐタイプを選ぶことができます」と言っています。 (タイプを選択するための技術的な言葉はです インスタンス化.)呼び出しの制限
foo
はるかに厳しいです:への議論foo
しなければならない 多型機能である。そのタイプで、私が渡すことができる唯一の関数はfoo
それはid
または、常に分岐したり、エラーが発生したりする関数undefined
. 。その理由は、それですfoo
, 、forall
矢の左側にあるので、foo
私は何を選ぶことができませんa
それは - それがそうです 実装 のfoo
それは何を選ぶようになりますa
は。なぜならforall
のように矢の上ではなく、矢の左側にありますbar
, 、インスタンス化は、コールサイトではなく関数の本体で行われます。
概要: a 完了 の説明 forall
キーワードには数学が必要であり、数学を勉強した人によってのみ理解できます。部分的な説明でさえ、数学なしでは理解するのは難しいです。しかし、多分私の部分的な非現実的な説明は少し助けになります。 LaunchBuryとPeyton Jonesを読んでください runST
!
補遺: jargon "obs"、 "nower"、 "の左側も。これらはとは何の関係もありません テキスト 方法の種類は書かれており、抽象系の木に関係しています。抽象的構文では、a forall
タイプ変数の名前を取得し、その後、「下」のフルタイプがあります。矢印は2つのタイプ(引数と結果タイプ)を取り、新しいタイプ(関数タイプ)を形成します。引数タイプは、「左側」です。それは抽象系の木の矢の左の子供です。
例:
の
forall a . [a] -> [a]
, 、forallは矢の上にあります。矢の左側は何ですか[a]
.の
forall n f e x . (forall e x . n e x -> f -> Fact x f) -> Block n e x -> f -> Fact x f
括弧内のタイプは、「矢印の左側のすべて」と呼ばれます。 (私が取り組んでいるオプティマイザーでこのようなタイプを使用しています。)
私の元の答え:
誰もが明確で平易な英語でforallキーワードを完全に説明できますか
ノーマンが示すように、タイプ理論から技術用語の明確で明確な英語の説明を与えることは非常に困難です。私たちは皆、努力しています。
「forall」について覚えておくべきことは本当に1つだけです。 タイプを何らかの範囲に結合します. 。それを理解したら、すべてがかなり簡単です。タイプレベルの「ラムダ」(または「let」の形式)に相当するものです。ノーマンラムジーは、「左」/「上」の概念を使用して、この同じ範囲の概念を伝えます。 彼の優れた答え.
「forall」のほとんどの用途は非常にシンプルであり、で紹介されていることがわかります GHCユーザーマニュアル、S7.8。、 特に 優れたS7.8.5 「forall」のネストされた形式。
Haskellでは、通常、タイプが普遍的に定量化されている場合、タイプのバインダーをオフにします。
length :: forall a. [a] -> Int
に相当します:
length :: [a] -> Int
それでおしまい。
タイプ変数を何らかのスコープにバインドできるため、トップレベル以外のスコープを使用できます(」普遍的に定量化されています")、最初の例のように、タイプ変数がデータ構造内でのみ表示される場合。これにより、非表示の型が可能になります("実存タイプ")または私たちは持つことができます 任意のネスティング バインディングの( "ランクnタイプ")。
タイプシステムを深く理解するには、いくつかの専門用語を学ぶ必要があります。それがコンピューターサイエンスの性質です。ただし、上記のように、単純な用途は、値レベルの「Let」との類推により、直感的に把握できるはずです。素晴らしい紹介です LaunchBuryとPeyton Jones.
彼らは、今週人気がある別の数学、カテゴリ理論、または抽象的な代数の分野で私が最新のものを読んだという仮定が密集しています。 (「実装の詳細については、紙を参照してください」という言葉をもう一度読んだことがないなら、それは早すぎるでしょう。)
ER、そして単純な一次ロジックはどうですか? forall
それに関連しています 普遍的な定量化, 、そしてその文脈で用語 実存 より理にかなっていますが、 exists
キーワード。定量化が効果的に普遍的であるか実存するかは、関数矢印のどちら側で変数が使用されているかについての定量因子の配置に依存します。
したがって、それが役に立たない場合、またはあなたが象徴的な論理が好きではない場合、より機能的なプログラミングっぽい視点から、タイプ変数をただ(暗黙的)と考えることができます タイプ 関数のパラメーター。この意味でタイプパラメーターを取る関数は、何らかの理由で首都ラムダを使用して伝統的に書かれています。 /\
.
だから、それを考えてください id
働き:
id :: forall a. a -> a
id x = x
Lambdasとして書き換えて、「タイプパラメーター」をタイプの署名から移動し、インラインタイプの注釈を追加できます。
id = /\a -> (\x -> x) :: a -> a
これが同じことです const
:
const = /\a b -> (\x y -> x) :: a -> b -> a
だからあなた bar
関数は次のようなものかもしれません:
bar = /\a -> (\f -> ('t', True)) :: (a -> a) -> (Char, Bool)
与えられた関数のタイプに注意してください bar
議論が依存するように bar
'のタイプパラメーター。代わりにこのようなものがあったかどうかを検討してください:
bar2 = /\a -> (\f -> (f 't', True)) :: (a -> a) -> (Char, Bool)
ここ bar2
関数をタイプのものに適用しています Char
, 、だから与える bar2
以外の任意のタイプパラメーター Char
タイプエラーが発生します。
一方、ここに何がありますか foo
見えるかもしれません:
foo = (\f -> (f Char 't', f Bool True))
ようではない bar
, foo
実際には、タイプのパラメーターをまったく取得していません!それは機能を取ります 自体 タイプパラメーターを取得し、その機能を2つに適用します 違う タイプ。
だからあなたが見るとき forall
タイプの署名で、それを タイプシグネチャのラムダ式. 。通常のラムダと同じように、の範囲 forall
可能な限り右に、括弧の囲いまで、そして通常のラムダに結合した変数と同様に、型変数は forall
定量化された式内の範囲内です。
Scriptumを投稿します: :たぶん、あなたは疑問に思うかもしれません - 今、私たちはタイプパラメーターを取得する関数について考えているのではないか、なぜそれらのパラメーターをタイプの署名に入れるよりも、これらのパラメーターでもっと面白いことをすることができないのですか?答えは、私たちができるということです!
タイプ変数をラベルと一緒に配置し、新しいタイプを返す関数は タイプコンストラクター, 、これを次のように書くことができます:
Either = /\a b -> ...
しかし、そのようなタイプが書かれている方法は、まったく新しい表記が必要だろう Either a b
, 、すでに「関数を適用してください Either
これらのパラメーターに」。
一方、タイプパラメーターの「パターンマッチング」のような関数は、さまざまなタイプの異なる値を返す関数です。 タイプクラスの方法. 。私へのわずかな拡張 /\
上記の構文は、次のことを示唆しています:
fmap = /\ f a b -> case f of
Maybe -> (\g x -> case x of
Just y -> Just b g y
Nothing -> Nothing b) :: (a -> b) -> Maybe a -> Maybe b
[] -> (\g x -> case x of
(y:ys) -> g y : fmap [] a b g ys
[] -> [] b) :: (a -> b) -> [a] -> [b]
個人的には、Haskellの実際の構文が好きだと思います...
そのタイプパラメーターを「パターン」と「一致」し、任意の既存のタイプを返す関数は タイプファミリー また 機能的依存関係- 前者の場合、それはすでに関数定義のようにかなりのように見えます。
ここに、あなたがすでによく知っている可能性が高い明白な言葉で迅速で汚い説明があります。
forall
キーワードは、実際にはHaskellで1つの方法でのみ使用されます。あなたがそれを見るとき、それは常に同じことを意味します。
普遍的な定量化
a 普遍的に定量化されたタイプ フォームのタイプです forall a. f a
. 。そのタイプの値は 機能 それはにかかります タイプ a
その議論と戻るように 価値 タイプの f a
. 。 Haskellでは、これらのタイプの引数はタイプシステムによって暗黙的に渡されます。この「関数」は、どのタイプが受信しても同じ値を与える必要があるため、値は 多型.
たとえば、タイプを考慮してください forall a. [a]
. 。そのタイプの値は別のタイプを取ります a
同じタイプの要素のリストを返します a
. 。もちろん、可能な実装は1つだけです。空のリストを提供する必要があります a
絶対にあらゆるタイプである可能性があります。空のリストは、要素タイプ(要素がないため)の多型である唯一のリスト値です。
またはタイプ forall a. a -> a
. 。そのような関数の発信者は両方のタイプを提供します a
タイプの値 a
. 。実装は、同じタイプの値を返す必要があります a
. 。可能な実装は1つだけです。与えられたのと同じ値を返す必要があります。
実存的な定量化
an 実存的に定量化されたタイプ フォームがあります exists a. f a
, 、Haskellがその表記法をサポートした場合。そのタイプの値は ペア (または「製品」)タイプで構成される a
タイプの値 f a
.
たとえば、タイプの値がある場合 exists a. [a]
, 、ある種の要素のリストがあります。それはどんなタイプかもしれませんが、たとえそれが何であるかわからなくても、そのようなリストにできることがたくさんあります。それを逆転させることも、要素の数を数えたり、要素の種類に依存しない他のリスト操作を実行したりすることもできます。
さて、ちょっと待ってください。 Haskellが使用する理由 forall
次のような「実存」タイプを示すために?
data ShowBox = forall s. Show s => SB s
混乱する可能性がありますが、実際に説明しています データコンストラクターのタイプ SB
:
SB :: forall s. Show s => s -> ShowBox
構築されたら、タイプの値を考えることができます ShowBox
2つのことで構成されています。それはタイプです s
タイプの値と一緒に s
. 。言い換えれば、それは実存的に定量化されたタイプの値です。 ShowBox
本当に書くことができます exists s. Show s => s
, 、Haskellがその表記法をサポートした場合。
runST
と友達
それを考えると、これらはどのように違うのですか?
foo :: (forall a. a -> a) -> (Char,Bool)
bar :: forall a. ((a -> a) -> (Char, Bool))
最初に取ってみましょう bar
. 。タイプが必要です a
タイプの関数 a -> a
, 、およびタイプの値を生成します (Char, Bool)
. 。選択できます Int
として a
タイプの関数を与えます Int -> Int
例えば。だが foo
異なります。の実装が必要です foo
必要なタイプに渡すことができるようになります。だから私たちが合理的に与えることができる唯一の関数は id
.
これで、タイプの意味に取り組むことができるはずです runST
:
runST :: forall a. (forall s. ST s a) -> a
それで runST
タイプの値を生成できる必要があります a
, 、どんなタイプを与えても a
. 。そうするためには、タイプの引数が必要です forall s. ST s a
ボンネットの下ではタイプの関数にすぎません forall s. s -> (a, s)
. 。その機能は、タイプの値を生成できる必要があります (a, s)
どんなタイプの実装であっても runST
ASを与えることを決定します s
.
わかりました、それで何?利点は、これが発信者に制約をかけることです runST
そのタイプ a
タイプを含めることはできません s
まったく。タイプの値を渡すことはできません ST s [s]
, 、 例えば。それが実際に意味することは、 runST
タイプの値で変異を自由に実行できます s
. 。タイプシステムは、この突然変異がの実装に局所的であることを保証します runST
.
タイプの runST
の例です ランク2の多型タイプ その引数のタイプにはaが含まれているためです forall
数量詞。タイプの foo
上記はランク2です。 bar
, 、ランク1ですが、引数の種類がポリ型である必要がある場合はランク2になります。 forall
数量詞。また、関数がランク2引数を取得する場合、そのタイプはランク3などです。一般に、ランクの多型の議論を取るタイプ n
ランクがあります n + 1
.
このキーワードにさまざまな用途がある理由は、実際には少なくとも2つの異なるタイプシステム拡張機能で使用されているためです:高ランクタイプと実存。
「forall」が両方で同時に適切な構文である理由を説明しようとするのではなく、これらの2つのことを個別に読んで理解するだけで、おそらく最善です。
誰もが明確でプレーンな英語でforallキーワードを完全に説明できますか(または、それがどこかに存在する場合、私が見逃したような明確な説明を指摘します)。
私は意味と多分の適用だけを説明しようとします forall
Haskellとそのタイプシステムのコンテキストで。
しかし、あなたが理解する前に、私はあなたをRunar Bjarnasonによる非常にアクセスしやすく素敵な話に導きたいと思っています」制約は解放され、自由が制約します"。この講演には、現実世界のユースケースの例と、この声明をサポートするためのScalaの例がいっぱいですが、言及していませんが forall
. 。説明しようとします forall
以下の視点。
CONSTRAINTS LIBERATE, LIBERTIES CONSTRAIN
この声明を消化して信じることは非常に重要です。次の説明を進めることができるので、講演(少なくともその一部)を見ることをお勧めします。
Haskellタイプシステムの表現力を示す非常に一般的な例は、このタイプの署名です。
foo :: a -> a
このタイプの署名を考えると、このタイプを満たすことができる関数は1つだけであると言われています。 identity
機能またはより一般的に知られていること id
.
Haskellを学んでいる私の最初の段階で、私はいつも以下の機能を疑問に思っていました:
foo 5 = 6
foo True = False
彼らは両方とも上記のタイプの署名を満たします、そしてなぜHaskellの人々はそれがそうであると主張するのですか id
タイプの署名を満たすのはどれですか?
それは暗黙があるからです forall
タイプの署名に隠されています。実際のタイプは次のとおりです。
id :: forall a. a -> a
それで、今、私たちは声明に戻りましょう: 制約は解放され、自由が制約します
それをタイプシステムに変換すると、このステートメントは次のようになります。
タイプレベルでの制約は、用語レベルでの自由になります
と
タイプレベルでの自由は、用語レベルで制約になります
最初の声明を証明してみましょう。
タイプレベルでの制約。
したがって、タイプの署名に制約を付けます
foo :: (Num a) => a -> a
期間レベルで自由になりますこれらすべてを書く自由または柔軟性を私たちに与えます
foo 5 = 6
foo 4 = 2
foo 7 = 9
...
制約することで同じことが観察できます a
他のタイプクラスなど
だから今、このタイプの署名: foo :: (Num a) => a -> a
翻訳は次のとおりです。
∃a , st a -> a, ∀a ∈ Num
これは実存的な定量化として知られています。 が存在します のいくつかのインスタンス a
タイプの何かを供給したときの関数 a
同じタイプの何かを返し、それらのインスタンスはすべて数字のセットに属します。
したがって、制約を追加することがわかります(それ a
数字のセットに属する必要があります)、用語レベルを解放して、複数の可能な実装を持っています。
今、2番目の声明と実際に説明を伝えるものに来ます forall
:
タイプレベルでの自由は、用語レベルで制約になります
次に、タイプレベルで関数を解放しましょう。
foo :: forall a. a -> a
これは次のとおりです。
∀a , a -> a
つまり、このタイプの署名の実装は、 a -> a
あらゆる状況で。
したがって、これは期間レベルで私たちを制約し始めます。もう書くことができません
foo 5 = 7
この実装は、私たちが置くと満たされないからです a
として Bool
. a
a Char
またはa [Char]
またはカスタムデータ型。すべての状況では、同様のタイプの何かを返す必要があります。タイプレベルでのこの自由は、普遍的な定量化として知られているものであり、これを満たすことができる唯一の関数は
foo a = a
これは一般に知られています identity
働き
したがって、 forall
aです liberty
タイプレベルでは、その実際の目的は constrain
特定の実装に対する期間レベル。
実存的な実存はどうですか?
実存的な質問で、
forall
s indata
定義には、値が含まれることを意味します できる of どれか 適切なタイプではなく、そうではありません しなければならない of すべて 適切なタイプ。 - Yachiruの答え
理由の説明 forall
の data
定義は同型です (exists a. a)
(Pseudo-Haskell)はにあります Wikibooksの「Haskell/実存的に定量化されたタイプ」.
以下は簡単な逐語的な要約です。
data T = forall a. MkT a -- an existential datatype
MkT :: forall a. a -> T -- the type of the existential constructor
パターンマッチング/分解の場合 MkT x
, 、タイプは何ですか x
?
foo (MkT x) = ... -- -- what is the type of x?
x
任意のタイプにすることができます(で述べられているように forall
)、そしてそれはタイプです:
x :: exists a. a -- (pseudo-Haskell)
したがって、以下は同型です。
data T = forall a. MkT a -- an existential datatype
data T = MkT (exists a. a) -- (pseudo-Haskell)
all for all forall
これらすべての私の簡単な解釈は、それです」forall
本当に「すべてのために」を意味します。 forall
に 意味 対function 応用.
a forall
意味を意味します 意味 値または関数の多型でなければなりません。
定義されているものが多型である場合 価値, 、次に、値が適切なすべてに有効でなければならないことを意味します a
, 、これは非常に制限的です。
定義されているものが多型である場合 働き, 、次に、関数が適切なすべてに有効でなければならないことを意味します a
, 、関数が多型であるからといって、パラメーターがあることを意味しないため、これはそれほど制限的ではありません 適用 多型である必要があります。つまり、関数がすべてに有効である場合 a
, 、逆に どれか 適切 a
可能です 適用 関数に。ただし、パラメーターのタイプは、関数定義で1回しか選択できません。
もし forall
関数パラメーターのタイプ内にあります(すなわち、 Rank2Type
)次に、次のことを意味します 適用 パラメーターはそうでなければなりません 本当に 多型、のアイデアと一致する forall
意味 意味 多型です。この場合、パラメーターのタイプは、関数定義で複数回選択できます(ノーマンが指摘したように、「そして、関数の実装によって選ばれます」)
したがって、実存的な理由 data
定義により許可されます どれか a
データコンストラクターが多型であるためです 働き:
MkT :: forall a. a -> T
MKTの一種:: a -> *
それは何かを意味します a
関数に適用される場合があります。たとえば、多型とは対照的です 価値:
valueT :: forall a. [a]
一種のValuet :: a
つまり、 意味 Valuetの多型でなければなりません。この場合、 valueT
空のリストとして定義できます []
すべてのタイプの。
[] :: [t]
違い
の意味であっても forall
で一貫しています ExistentialQuantification
と RankNType
, 、実存的なものには違いがあります data
コンストラクターは、パターンマッチングで使用できます。で文書化されているように GHCユーザーガイド:
パターンマッチングの場合、各パターンマッチは、各実存タイプ変数に新しい、個別のタイプを導入します。これらのタイプは、他のタイプと統合することも、パターンマッチの範囲から逃れることもできません。