Haskell の型システムは厳格でありながら柔軟で、大抵の場合は型を記述しなくてもうまく推論してくれる。
だが、一見すると単純に見えるこのような定義でひっかかってしまった。
f :: (a->a) -> (b,c) -> (b,c) f g (x,y) = (g x, g y)
x
と y
の型が同じである場合は問題ない。
f id (1,2)
問題なのは x
と y
の型が異なる場合だ。
f id (1,'a')
id
の型は a -> a
である (多相型) から、数値に適用しようと文字に適用しようと問題ないはずなのだが、上で定義した関数 f
を通すとエラーになるのである。
id
が g
にマッチしたところで型を具体化しようとするらしいのだ。 と言うより f
の型を確定しようとして (Integer -> Integer) -> (Integer, Char) -> (Integer, Char)
にしても (Char -> Char) -> (Integer, Char) -> (Integer, Char)
にしても矛盾してエラーになるというわけだ。
このような場合に対応するため、 unification を抑制する方法が用意されている。
{-# LANGUAGE Rank2Types #-} f :: (forall a . a->a) -> (b,c) -> (b,c) f g (x,y) = (g x, g y)
こうするともう一段階後まで型の確定を遅らせることが出来る。
残念ながら forall
は GHC の拡張で、規格 (Haskell98) には無いらしい。 ありがちな事例に思えるのだが、 forall
無しで解決する方法はあるのだろうか。
ところで、いきなり話題が変わるが、 forall を表わす記号∀を見るたびに思い出すことがある。 知識工学の講義中に教官が「∀は断じて『ターンエー』とは読まない」としつこく言っていたことだ。 この記号をターンエーと読むのはガンダム∀だけで、要するにあて字のようなものである。
Document ID: e0347a6a9b5d9102e3e0e21f1d53abda