The blog post introduces one of the most important typeclasses, Monad, in Haskell.

Recap
In the previous two articles, we covered Functor
, which can execute a function on a value inside a
type constructor with fmap
, and Applicative
, which can execute a function in a functor on a value inside
another functor with pure
, <$>
, and <*>
. If you are not confident about functors and applicative functors,
check out the articles, Road to Haskeller #13 and #14.
Monad
Now, we are finally ready to talk about Monad
. Monad
is a natural evolution of Applicative
, which allows a
function that takes an input of type a
and outputs an applicative with a value in type b
to be performed on an
applicative with a value of type a
. Let's look at the definition of Monad
to clarify what I mean by that.
class Applicative m => Monad (m :: * -> *) where
(>>=) :: m a -> (a -> m b) -> m b
(>>) :: m a -> m b -> m b
return :: a -> m a
fail :: String -> m a
{-# MINIMAL (>>=) #-}
We can see from MINIMAL
that we only need to define (>>=)
or bind for an Applicative
to become a Monad
.
We are passing an Applicative
with a value of type a
and a function that takes an input of type a
and outputs
an Applicative
with a value of type b
to arrive at an Applicative
with a value of type b
. This was not at all
possible with Functors
nor Applicative
because the former only accepts a function (a -> b)
, and the latter
only accepts (a -> b)
in a functor, not (a -> m b)
. To understand Monad
better, let's look at some examples.
Maybe
Maybe
is an instance of Monad
defined as follows.
instance Monad Maybe where
return x = Just x
Nothing >>= f = Nothing
Just x >>= f = f x
fail _ = Nothing
The return
function is the same as pure
, as it wraps a value in Just
. The bind function or >>=
uses pattern
matching to output Nothing
when Nothing
is passed and the result of applying a function to a value inside Just
otherwise.
This is useful because we now can use a function that outputs a monad, meaning we can set up our own condition for Nothing
.
When we were using Applicative
, we could not get Nothing
unless one of the inputs was Nothing
.
This is because the functions had to have an output of type inside of Maybe
and not Maybe
itself. Now that we can
have a function that takes a value of type a
and returns Maybe a
, we can do something like the following:
safediv :: (Eq a, Fractional a) => a -> a -> Maybe a
safediv x y
| y == 0 = Nothing
| otherwise = Just (x / y)
Just 1 >>= (\x -> safediv x 10) --- (Just 0.1)
Just 1 >>= (\x -> safediv x 0) --- (Nothing)
Now, we can apply safediv
of type (a -> a -> m a) to a value inside of Maybe
.
This allows us to have more flexibility with when we return Nothing
in our operations aside from
the case where Nothing
is passed (like safe guarding agaist y == 0
). This can help us make a more robust functions.
Lists
A list is also a valid instance of Monad
defined like the following:
instance Monad [] where
return x = [x]
xs >>= f = concat (map f xs)
fail _ = []
The return
function is the same as the pure
function again, which puts a value in a list. The bind function
maps a function over elements in a list and concatenates the results to arrive at a list. The following
demonstrates how the bind function works for a list:
[3,4,5] >>= \x -> [x,-x]
--- [3,-3,4,-4,5,-5]
The anonymous function \x -> [x, -x]
is applied to each element in a list and the results, [3, -3]
, [4, -4]
, and [5, -5]
are concatenated to make a final list. Aside from Maybe and lists, there are many other type constructors that belong to the Monad typeclass, including IO
.
Do Syntax
When using the bind function to use functions with multiple parameters, such as safediv
, the following is one way of how we can achieve it:
res = Just 1 >>= (\x -> Just 2 >>= (\y -> safediv x y))
However, ut is not as simple to write and read as we want it to be. Hence, Haskell made better syntax for achieving the same thing:
res = do
x <- Just 1 --- x = 1
y <- Just 2 --- y = 2
return $ safediv x y --- Just 0.5
Wait, have we not seen something like this somewhere else? Yes, this is the notation we saw in
Road to Haskeller #12 - Hello, World (IO actions)! <-
is actually the same thing as >>=
(same name "bind"!),
and do
is for eliminating parentheses. Some of you might have noticed this, but we have seen <-
notation
somewhere else too. It actually appeared in the article, Road to Haskeller #3 - Lists & Tuples, when we learned
about list comprehensions!
--- List Comprehension
res = [2*x | x <- [1,2,3]] --- res = [2,4,6]
--- Do Notation
res = do
x <- [1,2,3]
return $ 2 * x --- res = [2,4,6]
--- <<= Notation
res = [1,2,3] >>= (\x -> return $ 2 * x) --- res = [2,4,6]
Monad Laws
There are laws that a type needs to abide by for it to be a valid Monad
. The rules are the following:
-
Left Identity: This states that putting a value into a monad and applying a function using bind should result in the same output as passing the value directly to the function. (
return x >>= f == f x
) -
Right Identity: This states that passing a value inside of a monad to
return
using bind should just result in the same monad with the same value. (m >>= return == m
) -
Associativity: This states that the result should be the same regardless of how nested a chain of functions made with bind is. (
m >>= f >>= g == (m >>= f) >>= g
)
Exercises
This is an exercise section where you can test your understanding of the material introduced in the article. I highly recommend solving these questions by yourself after reading the main part of the article. You can click on each question to see its answer.
Resources
- Philipp, Hagenlocher. 2020. Haskell for Imperative Programmers #17 - Monads. YouTube.
- NA. A Fistful of Monads. Learn You a Haskell for Great Good.