Let’s get started with a *monomorphic* function -

```
incr :: Int -> Int
incr = (+1)
```

The function `incr`

works on values of type `Int`

and nothing else. We can make
our function *polymorphic* by using type parameters in place of concrete types -

```
incr' :: Num a => a -> a
incr' = (+1)
```

The modified function `incr'`

can work with any type that has a `Num`

instance.
Now let us move on and look at a simple *higher order* function -

```
foo :: (a -> a) -> a -> a
foo g x = g x
intval = foo (+1) 1 -- 2
listval = foo (++[1]) [1] -- [1, 1]
```

The first argument to `foo`

is a function of type `a -> a`

bound to `g`

. Is the
function `g`

polymorphic here? The answer is **No!** The `a`

type is being used
polymorphically here, however, `a`

is bound to whatever is the type of `x`

when
`g`

is applied to `x`

i.e., `g`

is monomorphic!

You don’t believe me? Try this

```
badfoo :: Show a => (a -> a) -> String
badfoo g = (show (g 1000)) ++ (g "? what!")
```

Or this function -

```
badbar :: Show a => (a -> a) -> IO ()
badbar g = do
print $ g 100
print $ g "why?"
```

The compiler will not be happy with this code. If `g`

were polymorphic, we
could apply it to both numeric and string values!

## Polymorphic Functions

We make our functions polymorphic by using type parameters instead of fixing the
types. When the function is s applied to a value, the type parameter(s) are
bound to actual type(s). We say the function is *instantiated* with the given
types. The actual types (of type variables) are set to the types of values
passed to the function.

Actual type of a polymorphic function is decided where the function is used, not where the function is defined, i.e., the user of the function decides the type.

Same applies to higher order functions. If the types of the argument function are parameterised, the actual types are decided when the argument function is used.

Coming back to `badfoo`

function, the type of the argument function is `(a -> a)`

. The type for type variable `a`

in `(a -> a)`

is bound when this function
is applied to a value (when we call `g 1`

). This means, for the reminder of this
function body, `g`

is monomorphic.

What we wanted, though, is for `g`

to be polymorphic. We want the type of `g`

to
be bound when user calls `badfoo`

- not when `g`

is used within `badfoo`

.

## Rank

Whenever we want to pass a polymorphic function `g`

as an argument to another
function `f`

, we want the type of `g`

to be decided not within the body of `f`

but at the caller site - one level up so to say. We say, `f`

is ranked one level
higher than `g`

.

All monomorphic functions are **Rank 0**. All polymorphic functions are Rank 1.
A rank-2 polymorphic function takes as an argument a rank-1 polymorphic
function. A rank-3 functions takes as an argument a rank-2 polymorphic function,
and so on.

If a higher order function takes as argument a rank (N-1) function, then it is said to be

Rank-Npolymorphic function.

## RankNTypes GHC Extension

Haskell is based on the Hindley-Milner type system - and while it allows us to write polymorphic functions, it doesn’t allow us to write functions which take polymorphic functions as arguments.

Higher Ranked Types are provided in GHC as a language extension. It can be
enabled via the `RankNTypes`

extension. Practically speaking, enabling
higher-ranked types lets us introduce new variable scopes inside of type
signatures.

Here’s the implementation we want:

```
{-# LANGUAGE RankNTypes #-}
goodfoo :: (forall a. Show a => a -> a) -> String
goodfoo g = (show (g 1)) ++ (g "Works!")
```

Higher Ranked Types make polymorphic Haskell functions *first class*.