8

私はあまり詳しくありませんforallが、最近この質問を読みました:Haskell /GHCの`forall`キーワードは何をしますか?

答えの1つは、次の例です。

 {-# LANGUAGE RankNTypes #-}
 liftTup :: (forall x. x -> f x) -> (a, b) -> (f a, f b)
 liftTup liftFunc (t, v) = (liftFunc t, liftFunc v)

説明はよく、ここで何をしているのか理解しforallています。しかし、私は疑問に思っています。これがデフォルトの動作ではない特別な理由がありますか。不利になる時期はありますか?

編集:つまり、デフォルトでforallを挿入できない理由はありますか?

4

2 に答える 2

16

まあ、それは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)
于 2012-04-12T18:00:04.623 に答える
7

上位の型は型推論を決定不能にするため、デフォルトでは有効になっていないと思われます。これは、拡張機能が有効になっている場合でも、キーワードを使用して上位の型を取得する必要がある理由でもありますforall。GHCは、できるだけ多くの型情報を推測するために、特に明記されていない限り、すべての型がランク1であると見なします。

言い換えると、上位の型を推測する一般的な方法はない(forall x. x -> f x) -> (a,b) -> (f a, f b)ため、その型を取得する唯一の方法は、明示的な型シグネチャを使用することです。

編集:上記のVitusのコメントによると、ランク2の型推論は決定可能ですが、上位のポリモーフィズムは決定可能ではありません。したがって、この型アノテーションは技術的に推測できます(ただし、アルゴリズムはより複雑です)。ランク2のポリモーフィック型推論を有効にするという余分な複雑さが価値があるかどうかは議論の余地があります...

于 2012-04-12T20:03:09.553 に答える