私のバックグラウンド: forall や、Haskell がそれらを暗黙的に持っている場合については、よくわかりません。
、 の型を考えてみましょid
うa -> a
。とはa
どういう意味ですか? どこから来たのですか? 値を定義するとき、どこにも定義されていない任意の変数を使用することはできません。最上位の定義、関数の引数、またはwhere
節 &c が必要です。一般に、変数を使用する場合は、どこかにバインドする必要があります。
同じことが型変数にも当てはまり、型変数forall
をバインドする方法の 1 つです。明示的にバインドされていない型変数 (たとえば、クラス定義内でのバインド) が表示される場所では、. によって暗黙的にバインドされclass Foo a where ...
ます。a
forall
したがって、 の型id
は暗黙的にforall a. a -> a
です。これは何を意味するのでしょうか?それが言うことはほとんど。可能なa -> a
すべてのタイプのタイプを取得できますa
。または、別の観点から、特定のタイプを選択すると、「選択したタイプからそれ自体への関数」を表すタイプを取得できます。forall
後者の言い回しは、関数の定義に少し似ているはずです。そのため、型のラムダ抽象化に似ていると考えることができます。
GHC はコンパイル中にさまざまな中間表現を使用し、それが適用する変換の 1 つは関数との類似性をより直接的にすることです: 暗黙的forall
な s は明示的にされ、多相値が特定の型に使用される場合はどこでも、最初に型引数に適用されます。 .
forall
s とラムダの両方を 1 つの式として記述することもできます。ちょっと表記を乱用して、視覚的な一貫性を保つために に置き換えforall a.
ます/\a =>
。id = /\a => \(x::a) -> (x::a)
このスタイルでは、または同様のものを定義できます。したがって、コード内のような式は、代わりid True
に次のようなものに変換されます。id Bool True
もはや意味がありid True
ません。
関数の引数を並べ替えることができるのと同じように、型引数を並べ替えることができますが、型引数はその型の値引数よりも前に来なければならないという (かなり明白な) 制限のみが適用されます。暗黙的forall
な s は常に最外層であるため、GHC はそれらを明示的にするときに任意の順序を選択できる可能性があります。通常の状況では、これは明らかに問題ではありません。
この場合に何が起こっているのか正確にはわかりませんが、コメントに基づいて、明示的な型引数の使用への変換とdo
表記の脱糖は、ある意味でお互いを認識していないため、順序が一貫性を確保するために、型引数の明示的に指定されます。結局のところ、何かが盲目的に 2 つの型引数を式に適用している場合、その式の型がforall a b. m a -> m b -> m b
orであるかどうかが非常に重要になりforall b a. m a -> m b -> m b
ます。