What is the difference between these?
{-# LANGUAGE RankNTypes #-}
f :: forall a. a -> Int
f _ = 1
g :: (forall a. a) -> Int
g _ = 1
In
f
is just an ordinary polymorphic Haskell98 function, except the forall
is written out explicitly. So all the type variables in the signature are parameters the caller gets to choose (without any constraints); in your case it's resolved a ~ ()
.
g
OTOH has a rank-2 type. It requires that its argument has the polymorphic type forall a . a
. ()
does not have such a type, it is monomorphic. But undefined
has this type (in fact, only undefined, and error etc.), if we add the explicit forall
again.
Perhaps it becomes clearer with a less trivial Rank2 function:
h :: (forall a . (Show a, Num a) => a) -> String
h a = show a1 ++ " :: Double\n"
++ show a2 ++ " :: Int"
where a1 :: Double; a2 :: Int
a1 = a; a2 = a
GHCi> putStrLn $ h 4
4.0 :: Double
4 :: Int
but I can't do
GHCi> putStrLn $ h (4 :: Integer)
<interactive>:4:15:
Could not deduce (a ~ Integer)
from the context (Show a, Num a)
bound by a type expected by the context: (Show a, Num a) => a
at <interactive>:4:12-27
`a' is a rigid type variable bound by
a type expected by the context: (Show a, Num a) => a
at <interactive>:4:12
In the first argument of `h', namely `(4 :: Integer)'
In the second argument of `($)', namely `h (4 :: Integer)'
In the expression: putStrLn $ h (4 :: Integer)