簡単に言うと、型推論は常に上位の型で機能するとは限りません。この場合、 の型を推測することはできませんが(.)
、明示的な型注釈を追加するかどうかを型チェックします。
> :m + Control.Monad.ST
> :set -XRankNTypes
> :t (((.) :: ((forall s0. ST s0 a) -> a) -> (a -> forall s1. ST s1 a) -> a -> a) runST return) $ True
(((.) :: ((forall s0. ST s0 a) -> a) -> (a -> forall s1. ST s1 a) -> a -> a) runST return) $ True :: Bool
($)
独自のバージョンに置き換えると、最初の例でも同じ問題が発生します。
> let app f x = f x
> :t runST `app` (return `app` True)
<interactive>:1:14:
Couldn't match expected type `forall s. ST s t0'
with actual type `m0 t10'
Expected type: t10 -> forall s. ST s t0
Actual type: t10 -> m0 t10
In the first argument of `app', namely `return'
In the second argument of `app', namely `(return `app` True)'
繰り返しますが、これは型注釈を追加することで解決できます。
> :t (app :: ((forall s0. ST s0 a) -> a) -> (forall s1. ST s1 a) -> a) runST (return `app` True)
(app :: ((forall s0. ST s0 a) -> a) -> (forall s1. ST s1 a) -> a) runST (return `app` True) :: Bool
ここで起こっていることは、GHC 7 には標準($)
演算子にのみ適用される特別な型付け規則があるということです。Simon Peyton-Jones は、GHC ユーザー メーリング リストへの返信で、この動作について説明しています。
これは、非予測型を処理できる型推論の動機付けとなる例です。のタイプを考えてみましょう($)
:
($) :: forall p q. (p -> q) -> p -> q
p
この例では、 でインスタンス化する必要があります。これが(forall s. ST s a)
非予測的ポリモーフィズムの意味です。つまり、型変数をポリモーフィック型でインスタンス化することです。
悲しいことに、[これ] を補助なしで型チェックできる合理的な複雑さのシステムを私は知りません。複雑なシステムはたくさんあり、私は少なくとも 2 つの論文の共著者でしたが、GHC に住むにはあまりにも複雑すぎます。boxy 型の実装はありましたが、新しい型チェッカーを実装するときに取り出しました。誰もそれを理解していませんでした。
しかし、人々は非常に頻繁に書いています
runST $ do ...
GHC 7 では、($)
. 明らかな型付け規則を備えた新しい構文形式と考え(f $ x)
てください。
のようなルールがないため、2 番目の例は失敗します(.)
。