What is the difference between these?
{-# LANGUAGE RankNTypes #-}
f :: forall a. a -> Int
f _ = 1
g :: (forall a. a) -> Int
g _ = 1
In
Think of forall
as an anonymous type function. All data types in Haskell which have type variables in their type signature implicitly have a forall
. For example consider:
f :: a -> Int
f _ = 1
The above function f
takes an argument of any type and returns an Int
. Where does the a
come from? It comes from the forall
quantifier. Hence it's equivalent to:
f :: (forall a . a -> Int)
f _ = 1
The forall
quatifier can be used for any data type, not just functions. For example consider the types of the following values:
() :: ()
10 :: Int
pi :: Floating a => a
Here ()
and 10
are monomorphic (i.e. they can only be of one concrete type). On the other hand pi
is polymorphic with a typeclass constraint (i.e. it can be of any type as long as that type is a instance of Floating
). The type of pi
written out explicitly is:
pi :: (forall a . Floating a => a)
Again the forall
quantifier acts like a type function. It provides you the type variable a
. Now consider the type of function g
:
g :: (forall a . a) -> Int
g _ = 1
Here g
expects an argument of type forall a . a
and returns an Int
. This is the reason g ()
doesn't work: ()
is of type ()
, not of type forall a . a
. In fact the only value of type forall a . a
is undefined
:
undefined :: a
Written out explicitly with forall
:
undefined :: (forall a . a)
If you noticed I've always put parentheses around the forall
quantifications. The reason I did this is to show you that when you use a forall
quantification on a function the quantification extends all the way to the right. This is just like a lambda: if you don't put parentheses around the lambda Haskell will extend the lambda function all the way to the right. Hence the type of f
is (forall a . a -> Int)
and not (forall a . a) -> Int
.
Remember in the first case Haskell expects the type of the argument to be a
(i.e. anything). However in the second case Haskell expects the type of the argument to be (forall a . a)
(i.e. undefined
). Of course if you try to evaluate undefined
then your program will immediately halt with an error. Fortunately you're not trying to evaluate it.