Functors, Applicatives and Monads

The main type classes is what I think is the hardest part to understand in Haskell. Its difficult to understand the purpose of each type class e.g. Monads, Applicatives and Functors. And its difficult to understand why the laws of each type class should apply.

There are many good references which explain this much better than I do here. This is just for me ...

Problem 1: I want to apply a function of arbitrary length on values that have a context. In this example the arbitrary length of the function add3 is 3.

let add3 = \a b c -> a + b + c
p0 = Just 1
p1 = Just 2
p2 = Just 3

:t add3
add3 :: Integer -> Integer -> Integer -> Integer

If I had pure values the calculation would be trivial.

add3 1 2 3
=> 6

Just 1 is a Maybe type class which is an instance of Functor, Applicative and Monad.

So lets try to perform the calculation using the Functor type class. Functor has the ability to map a function (fmap) on a single value in an context and return the result in the same context.

:t fmap
fmap :: Functor f => (a -> b) -> f a -> f b

:t fmap add3 (Just 1)
fmap add3 (Just 1) :: Maybe (Integer -> Integer -> Integer)

So fmap is able to partially apply add3 to 1 and embed it in a Maybe context.

fmap takes a pure functions. So to be able to apply the partial function to next argument (Just 2) we would need to extract the pure function from the Maybe context again. This could of course be done.

let fromJust (Just x) = x

So this could be used to apply fmap again

:t fmap (fromJust $ fmap add3 (Just 1)) (Just 2) 
fmap (fromJust $ fmap add3 (Just 1)) (Just 2) :: Maybe (Integer -> Integer)

And again

fmap (getPure $ fmap (getPure $ fmap add3 (Just 1)) (Just 2)) (Just 3)
Just 6

But, this isn't especially readable. Applicative have a much more convenient function which would simplify things radically. <*> does specifically what we want. It takes a function in a context and applies that to a value in a context.

:t (<*>)
(<*>) :: Applicative f => f (a -> b) -> f a -> f b

Because the partially applied function we got from the first application of fmap on the Maybe value is in a context this can be used directly.

:t fmap add3 (Just 1) <*> (Just 2)
fmap add3 (Just 1) <*> (Just 2) :: Maybe (Integer -> Integer)

This returns the second partial application of add3 in a context so it can be used again to finalize the calculation. Note that <*> is left assiciative.

fmap add3 (Just 1) <*> (Just 2) <*> (Just 3)
Just 6

This is much simpler than alternating between fromJust and fmap to get to the result!

Applicative also provides a function to put a primitive value in context called pure.

:t pure
pure :: Applicative f => a -> f a 

So if we use pure to put add3 in contect we have an alternative way to perform the calculation.

pure add3 <*> (Just 1) <*> (Just 2) <*> (Just 3)
Just 6

The fact that pure add3 <*> (Just 1) = fmap add3 (Just 1) is actually one of the laws of applicative functors.

pure f <*> x = fmap f x

Because pure f <*> x is so commonly used, a helper function is defined which does both these operation in one go.

:t (<$>)
(<$>) :: Functor f => (a -> b) -> f a -> f b

(<$>) f x = pure f <*> x     -- OR
(<$>) f x = fmap f x

:t add3 <$> (Just 1)
add3 <$> (Just 1)      :: Maybe (Integer -> Integer -> Integer)

:t fmap add3 (Just 1)
fmap add3 (Just 1)     :: Maybe (Integer -> Integer -> Integer)

:t pure add3 <*> (Just 1)
pure add3 <*> (Just 1) :: Maybe (Integer -> Integer -> Integer)

So the same expression can be written in a even more readable way.

add3 <$> (Just 1) <*> (Just 2) <*> (Just 3)
Just 6

From what we have seen, using Applicatives we can perform a set of computations in a pre-defined order i.e. the partial application of add3 on each of the values in context (from left to right). What if the sequence of operations isn't known from the beginning i.e. if one of the computations return different results (e.g. because its impure).

Instead of the pre-defined values (like Just 1), I have defined an IO operation (getNum) that takes a string as input from the user and convert that string to an integers. Lets try it.

getNum
1
1

getNum
text
*** Exception: Prelude.read: no parse

Yes, it chokes if I dont enter an integer.

So how is getNum defined? getLine takes user input and return IO String. IO String is a monad type class to encapsulate IO in a pure context. An IO String value is non-determined until the program executes and the actual value is retrieved from the user.

:t getLine
getLine :: IO String

getNum should return the number and not the string so I need to convert the input. The pure function read does that for us. Read convert a sting to any type a. To make it understand what to convert to we need to supply a type signature for the expected result.

:t read
read :: Read a => String -> a

:t (read :: String -> Integer)
(read :: String -> Integer) :: String -> Integer

A monad type class is similar to applicatives in that it holds a value in a context. The different context is specified by its specific set of functions and laws that must apply for every monad. Functors, Applicatives and Monads are all related. All Monads are Applicatives which in turn are Functors.

One important function of the monad class is the bind function (>>=).

:t (>>=)
(>>=) :: Monad m => m a -> (a -> m b) -> m b

As you can see it is quite similar to fmap and <*>. While fmap takes a pure function applied to a value with context and <*> takes the same function in a context and applies to the same value in context, >>= takes a function which takes a pure value and return the result in a context and apply that function to a value with a context. The order of arguments is also reversed.

So how do we create a function that takes a pure value and return that value in a context. Using pure would be one way.

let f a = pure a
Prelude Control.Applicative> :t f
f :: Applicative f => a -> f a

Monad type class define return which is synonymous to pure.

:t return
return :: Monad m => a -> m a

So, if we use getLine it will generate a string in a IO context. If we define a function that takes that pure string value, convert it to an integer and return that integer in a new IO context, we should be able to use >>= to bind these two operations together as a single computation.

:t (return . (read :: String -> Integer))
(return . (read :: String -> Integer)) :: Monad m => String -> m Integer

let getNum = getLine >>= (return . (read :: String -> Integer))

:t getNum
getNum :: IO Integer

getNum is similar to Just 1 as they are both values in a context, however somewhat different contexts.

Lets see how the IO monad works as applicatives.

Prelude Control.Applicative> add3 <$> getNum <*> getNum <*> getNum
1
2
3
6

Yes, that works as expected.

This is the essence on how impure functions can be mixed with pure functions. It is still the pure add3 functions that we use to calculate the sum of the impure results from getNum. It is getNum that must be validated with extra concern (as it is impure), not the whole composed operation.

So far we assume that user always input is 3 integers and nothing else. What if we were to execute completely different results depending on user input. This is not very uncommon (and trivial in an imperative language).

Problem 2: I want collect numbers from the user and sum them, until user enter q

This impossible to do with applicatives or functors. Using monads it is possible.

The bind function can return different monadic values depending on the value entering the monadic function or the result of the computation itself.

To be able do perform IO computations the monadic values must be of type IO a. The result of this computation is a sum of integers, so the monadic type should hold the sum in an IO context (IO Integer).

Hence, the monadic computation takes the current sum as input and retrieves a new integer from the user. If user enter q the current sum is returned. If a number is entered, current monadic function is bound to itself with the updated sum as input.

getNum :: Integer -> IO Integer
getNum acc = do
               s <- getLine
               if s == "q" then return acc
                           else getNum $ acc + (read s :: Integer)

The main function is trivial. We just call getNum with sum set to 0 and print the result. Note that getNum 0 will result in an unknown set of computations.

main = do 
         res <- getNum 0
         putStrLn $ show res

To see how do maps to the bind (>>=) function we can de-sugar the main function.

main = getNum 0 >>= (\res -> putStrLn $ show res)

This composition of getNum depending on user input, cannot be implemented using applicatives. With applicatives the execution of each computation is ordered, but the number of computations must be explicit by the code. With monadic values and the composition using bind this is not necessary.

The result of the computation can be a sum of 0 or more computations depending on user input.

./sum_test 
1
2
3
q
6

Some other poking around

> :t (<$>)
(<$>) :: Functor f => (a -> b) -> f a -> f b
> (+1) <$> Just 1
Just 2

> :t (<*>)
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
> Just (+1) <*> Just 1
Just 2

> [(+1),(+2)] <*> [1,2]
[2,3,3,4]

> (+) <$> [1,2] <*> [10,20]
[11,21,12,22]
> fmap (+) [1,2] <*> [10,20]
[11,21,12,22]

instance Applicative ((->) r) where  
    pure x = (\_ -> x)  
    f <*> g = \x -> f x (g x) 

let ap f g = \x -> f x (g x)
> :t ap
ap :: (t2 -> t1 -> t) -> (t2 -> t1) -> t2 -> t
> ap (+) (+2) 1
4

Note f in ap f g takes two parameters and g one. So the calculation is

(+) 1 ((+) 2 1) = 4

> replicate 3 1 >>= replicate 2
[1,1,1,1,1,1]
> [1] >>= replicate 3 >>= replicate 2
[1,1,1,1,1,1]

I don't understand where this is defined. There is a pattern match fail in the second example. Why exception in the translated case?

> do { [a] <- Just [2]; return a } :: Maybe Int
Just 2
> do { [a] <- Just [1,2]; return a } :: Maybe Int
Nothing

> Just [1] >>= (\[a] -> return a) :: Maybe Int
Just 1
> Just [1,2] >>= (\[a] -> return a) :: Maybe Int
*** Exception: <interactive>:104:17-32: Non-exhaustive patterns in lambda

References