2年前にExistential QuantificationとPolymorphic Componentsの違いというのを書いていたのを検索したら見つけてしまったので追記しておきます。その時には、PoとPo2の違いがわからないと書いてありますが、Poはデータコンストラクタがrank-2になるのに対して、Po2はなりません。例えば、
data Fun = Fun (forall a. a -> a) f :: Fun -> Bool -> Int -> Bool f (Fun g) b n = g b && g n /= 0
はコンパイルできますが、
data Fun2 a = Fun2 (a -> a) f2 :: Fun a -> Bool -> Int -> Bool f2 (Fun g) b n = g b && g n / = 0
はコンパイルできません。これは、
h :: (forall a. a -> a) -> Bool h f = f True && f 1 /= 0
のfがコンパイルできるのに対して、
h2 :: (a -> a) -> Bool h2 f = f True && f 1 /= 0
がコンパイルできない*1のと同じことです。
*1 h2ではfの型がh2全体で具象型として決まる必要があるので、前半ではBool -> Boolとして呼び出し、後半ではInt -> Intとして呼び出すことができない
関数にしてしまうと、元ネタの形が失われるので、こっちの方が例としては良いかも。
data V = V (forall a. Num a => a) g (V a) = showInt a ++ ", " ++ showDouble a data Num a => V2 a = V2 a --g2 (V2 a) = showInt a ++ ", " ++ showDouble a showInt :: Int -> String showInt = show showDouble :: Double -> String showDouble = show
gはVがPoを使っているのでコンパイルできますが、g2はV2が普通の型なのでコンパイルできません。
IntでありつつDoubleであるというちょっと不思議な感じ。こうやって呼び出します。
g (V $ fromInteger 1)