Haskell/Understanding monads

There is a certain mystique about monads, and even about the word "monad" itself. While one of our goals of this set of chapters is removing the shroud of mystery that is often wrapped around them, it is not difficult to understand how it comes about. Monads are very useful in Haskell, but the concept is often difficult to grasp at first. Since monads have so many applications, people often explain them from a particular point of view, which can derail your efforts towards understanding them in their full glory.

Historically, monads were introduced into Haskell to perform input and output – that is, I/O operations of the sort we dealt with in the Simple input and output chapter and the prologue to this unit. A predetermined execution order is crucial for things like reading and writing files, and monadic operations lend themselves naturally to sequencing. However, monads are by no means limited to input and output. They can be used to provide a whole range of features, such as exceptions, state, non-determinism, continuations, coroutines, and more. In fact, thanks to the versatility of monads, none of these constructs needed to be built into Haskell as a language; rather, they are defined by the standard libraries.

In the Prologue chapter, we began with an example and used it to steadily introduce several new ideas. Here, we will do it the other way around, starting with a definition of monad and, from that, building connections with what we already know.

Definition
A monad is defined by three things: The function and operator are methods of the  type class and have types
 * a type constructor  ;
 * a function ;
 * an operator  which is pronounced "bind".

and are required to obey three laws that will be explained later on.

For a concrete example, take the  monad. The type constructor is, while   and   are defined like this:

is the monad, and  brings a value into it by wrapping it with. As for, it takes a   value and a   function. If  is , there is nothing to do and the result is. Otherwise, in the  case,   is applied to , the underlying value wrapped in  , to give a   result. Note that this result may or may not be, depending on what   does to. To sum it all up, if there is an underlying value of type  in , we apply   to it, which brings the underlying value back into the   monad.

The key first step to understand how  and   work is tracking which values and arguments are monadic and which ones aren't. As in so many other cases, type signatures are our guide to the process.

Motivation: Maybe
To see the usefulness of  and the   monad, consider the following example: Imagine a family database that provides two functions:

These look up the name of someone's father or mother. In case our database is missing some relevant information,  allows us to return a   value to indicate that the lookup failed, rather than crashing the program.

Let's combine our functions to query various grandparents. For instance, the following function looks up the maternal grandfather (the father of one's mother):

Or consider a function that checks whether both grandfathers are in the database:

What a mouthful! Every single query might fail by returning  and the whole function must fail with   if that happens.

Clearly there has to be a better way to write that instead of repeating the case of  again and again! Indeed, that's what the  monad is set out to do. For instance, the function retrieving the maternal grandfather has exactly the same structure as the  operator, so we can rewrite it as:

With the help of lambda expressions and, we can rewrite the two grandfathers function as well:

While these nested lambda expressions may look confusing to you, the thing to take away here is that  releases us from listing all the  s, shifting the focus back to the interesting part of the code.

To be a little more precise: The result of  is a monadic value (in this case, either   or , depending on whether  's father is in the database). As the  function takes a regular (non-monadic) value, the   feeds  's   to it as a non-monadic value. The result of  is then monadic again, and the process continues.

So,  helps us pass non-monadic values to functions without actually leaving a monad. In the case of the  monad, the monadic aspect is the uncertainty about whether a value will be found.

Type class
In Haskell, the  type class is used to implement monads. It is provided by the module and included in the Prelude. The class has the following methods:

Aside from return and bind, there are two additional methods,  and. Both of them have default implementations, and so you don't need to provide them when writing an instance.

The operator, spelled "then", is a mere convenience and has the default implementation

sequences two monadic actions when the second action does not involve the result of the first, which is a common scenario for monads such as.

The function  handles pattern match failures in   notation. It's an unfortunate technical necessity and doesn't really have anything to do with monads. You are advised not to call  directly in your code.

and
An important thing to note is that  is a superclass of. That has a few consequences worth highlighting. First of all, every  is also a   and an , and so  ,  ,   can all be used with monads. Secondly, actually writing a  instance also requires providing   and   instances. We will discuss ways of doing that later in this chapter. Thirdly, if you have worked through the Prologue, the types and roles of  and   should look familiar...

The only difference between the types of  and   is that the constraint changes from   to. In fact, that is the only difference between the methods: if you are dealing with a  you can always replace   and , and vice-versa. The same goes for /  – in fact, it is not even necessary to implement   if there is an independent definition of   in the   instance, as   is provided as a default definition of.

Notions of Computation
We have seen how  and   are very handy for removing boilerplate code that crops up when using. That, however, is not enough to justify why monads matter so much. Our next step towards that will be rewriting the two-grandfathers function in a quite different-looking style: using  notation with explicit braces and semicolons. Depending on your experience with other programming languages, you may find this very suggestive:

If this looks like a code snippet in an imperative programming language to you, that's because it is. In particular, this imperative language supports exceptions :  and   are functions that might fail to produce results, raising an exception instead; when that happens, the whole  -block will fail, i.e. terminate with an exception (meaning, evaluate to , here).

In other words, the expression, which has type  , is interpreted as a statement in an imperative language that returns a   as the result, or fails.

This is true for all monads: a value of type  is interpreted as a statement in an imperative language   that returns a value of type   as its result; the semantics of this language are determined by the monad.

Under this interpretation, the then operator  is simply an implementation of the semicolon, and    of the semicolon and assignment (binding) of the result of a previous computational step. Just like a  expression can be written as a function application,

let x = foo in (x + 3)       corresponds to      foo  &  (\x -> id (x + 3))      -- v & f = f v

an assignment and semicolon can be written with the bind operator:

x <- foo; return (x + 3)     corresponds to      foo >>= (\x -> return (x + 3))

In case of functions,  and   are trivial; in case of a monad,   and   are substantial.

The  operator combines two pure calculations,   and , while creating a new binding for the variable   to hold  's value, making   available to the second calculational step,.

The bind operator  combines two computational steps,   and , in a manner particular to the monad  , while creating a new binding for the variable   to hold  's result, making   available to the next computational step,. In the particular case of, if   will fail to produce a result, the second step is skipped and the whole combined computation will fail right away as well.

The function  lifts a plain value   to , a statement in the imperative language  , which statement, when executed / run, will result in the value   without any additional effects particular to. This is ensured by Monad Laws,  and  ; see below.

Different semantics of the imperative language correspond to different monads. The following table shows the classic selection that every Haskell programmer should know. If the idea behind monads is still unclear to you, studying each of the examples in the following chapters will not only give you a well-rounded toolbox but also help you understand the common abstraction behind them.

Furthermore, these different semantics need not occur in isolation. As we will see in a few chapters, it is possible to mix and match them by using monad transformers to combine the semantics of multiple monads in a single monad.

Monad Laws
In Haskell, every instance of the  type class (and thus all implementations of bind   and  ) must obey the following three laws:

Return as neutral element
The behavior of  is specified by the left and right unit laws. They state that  doesn't perform any computation, it just collects values. For instance,

is exactly the same as

by virtue of the right unit law.

Associativity of bind
The law of associativity makes sure that (like the semicolon) the bind operator  only cares about the order of computations, not about their nesting; e.g. we could have written   like this (compare with our earliest version without  ):

The associativity of the then operator  is a special case:

Monadic composition
It is easier to picture the associativity of bind by recasting the law as

where  is the monad composition operator, a close analogue of the function composition operator , only with flipped arguments. It is defined as:

There is also, which is flipped version of. When using it, the order of composition matches that of, so that in     comes first.

Monads and Category Theory
Monads originally come from a branch of mathematics called Category Theory. Fortunately, it is entirely unnecessary to understand category theory in order to understand and use monads in Haskell. The definition of monads in Category Theory actually uses a slightly different presentation. Translated into Haskell, this presentation gives an alternative yet equivalent definition of a monad, which can give us some additional insight on the  class.

So far, we have defined monads in terms of  and. The alternative definition, instead, treats monads as functors with two additional combinators:

For the purposes of this discussion, we will use the functors-as-containers metaphor discussed in the chapter on the functor class. According to it, a functor  can be thought of as container, so that   "contains" values of type , with a corresponding mapping function, i.e.  , that allows functions to be applied to values inside it.

Under this interpretation, the functions behave as follows:
 * applies a given function to every element in a container
 * packages an element into a container,
 * takes a container of containers and flattens it into a single container.

With these functions, the bind combinator can be defined as follows:

Likewise, we could give a definition of  and   in terms of   and  :

and Friends
Earlier, we pointed out that every  is an , and therefore also a. One of the consequences of that was  and   being monad-only versions of   and   respectively. It doesn't stop there, though. For one,  defines , a function with a strangely familiar type signature...

As you might suspect,  is merely   implemented with   and , just as we have done in the previous section. and  are therefore interchangeable.

Another  function with an uncanny type is  :

Analogously to the other cases,  is a monad-only version of.

There are quite a few more examples of  functions that have versions specialised to   in   and other base library modules. Their existence is primarily due to historical reasons: several years went by between the introductions of  and   in Haskell, and it took an even longer time for   to become a superclass of , thus making usage of the specialised variants optional. While in principle there is little need for using the monad-only versions nowadays, in practice you will see  and   all the time in other people's code – at this point, their usage is well established thanks to more than two decades of Haskell praxis without   being a superclass of.