まあ、それはHaskell 2010標準の一部ではないので、デフォルトではオンになっておらず、代わりに言語拡張として提供されています。なぜそれが標準にないのかというと、ランクnタイプは、標準のHaskellが持っているプレーンなランク1タイプよりも実装がかなり難しいです。また、それらはそれほど頻繁に必要とされるわけではないので、委員会は、言語と実装の単純さの理由から、それらを気にしないことを決定した可能性があります。
もちろん、それはランクnタイプが役に立たないという意味ではありません。それらは非常にそうであり、それらがなければ、ST
モナドのような価値のあるツールはありません(これは、効率的でローカルな可変状態を提供します。たとえば、 sIO
を使用するだけですIORef
)。しかし、それらは言語にかなりの複雑さを追加し、一見良性のコード変換を適用するときに奇妙な動作を引き起こす可能性があります。たとえば、一部のランクnタイプのチェッカーは、2つの式がランクnタイプなしでは常に同等であっても、許可しますrunST (do { ... })
が拒否します。それが引き起こす可能性のある予期しない(そして時には迷惑な)振る舞いの例については、このSOの質問runST $ do { ... }
を参照してください。
sepp2kが尋ねるように、代わりにforall
、一般性を高めるために型シグネチャに明示的に追加する必要がある理由を尋ねている場合、問題は、(forall x. x -> f x) -> (a, b) -> (f a, f b)
実際にはより制限的な型であるということです(x -> f x) -> (a, b) -> (f a, f b)
。後者の場合、x -> f x
(任意のf
およびx
)の形式の任意の関数を渡すことができますが、前者の場合、渡す関数はすべてのに対して機能する必要があります x
。したがって、たとえば、型の関数String -> IO String
は2番目の関数に対して許容される引数になりますが、最初の関数には許可されません。a -> IO a
代わりにタイプが必要です。後者が自動的に前者に変換された場合、かなり混乱します!それらは2つの非常に異なるタイプです。
forall
暗黙のsを明示的にすると、より意味があるかもしれません。
forall f x a b. (x -> f x) -> (a, b) -> (f a, f b)
forall f a b. (forall x. x -> f x) -> (a, b) -> (f a, f b)