While working my way through GHC extensions, I came across RankNTypes at the School of Haskell, which had the following example:
main = print $ rankN (+1)
r
In the rankN case f has to be a polymorphic function which is valid for all numeric types n.
In the rank1 case f only has to be defined for a single numeric type.
Here is some code which illustrates this:
{-# LANGUAGE RankNTypes #-}
rankN :: (forall n. Num n => n -> n) -> (Int, Double)
rankN = undefined
rank1 :: forall n. Num n => (n -> n) -> (Int, Double)
rank1 = undefined
foo :: Int -> Int -- monomorphic
foo n = n + 1
test1 = rank1 foo -- OK
test2 = rankN foo -- does not type check
test3 = rankN (+1) -- OK since (+1) is polymorphic
Update
In response to @helpwithhaskell's question in the comments...
Consider this function:
bar :: (forall n. Num n => n -> n) -> (Int, Double) -> (Int, Double)
bar f (i,d) = (f i, f d)
That is, we apply f to both an Int and a Double. Without using RankNTypes it won't type check:
-- doesn't work
bar' :: ??? -> (Int, Double) -> (Int, Double)
bar' f (i,d) = (f i, f d)
None of the following signatures work for ???:
Num n => (n -> n)
Int -> Int
Double -> Double