Haskell Language RankNTypes


Imagine the following situation:

foo :: Show a => (a -> String) -> String -> Int -> IO ()
foo show' string int = do
   putStrLn (show' string)
   putStrLn (show' int)

Here, we want to pass in a function that converts a value into a String, apply that function to both a string parameter and and int parameter and print them both. In my mind, there is no reason this should fail! We have a function that works on both types of the parameters we're passing in.

Unfortunately, this won't type check! GHC infers the a type based off of its first occurrence in the function body. That is, as soon as we hit:

putStrLn (show' string)

GHC will infer that show' :: String -> String, since string is a String. It will proceed to blow up while trying to show' int.

RankNTypes lets you instead write the type signature as follows, quantifying over all functions that satisfy the show' type:

foo :: (forall a. Show a => (a -> String)) -> String -> Int -> IO ()

This is rank 2 polymorphism: We are asserting that the show' function must work for all as within our function, and the previous implementation now works.

The RankNTypes extension allows arbitrary nesting of forall ... blocks in type signatures. In other words, it allows rank N polymorphism.