Explaining what a monad is has become a classic exercise. One can find hundreds of articles about the topic and this is my take. It is low on theory and focuses on motivation and mechanics. I’m using OCaml for my code examples.

The Problem: Error Handling

Exceptions are a convenient way to handle errors in programs but some critics argue that they encourage laziness and are too hard to reason about. One alternative, recently explored in the Go language, is to make functions return a result together with an error code. This forces a programmer to deal with them at every calls site. Let’s imagine such an approach in OCaml by looking at what programming with lists would look like.

Every function that can fail returns a result value, indicating either the result using Ok, or a problem using Error. This is simple enough for basic list functions.

The code below is presented as a literate program. Named code snippets are introduced using <<name>>= and referred to by <<name>>. This is meta notation and not part of the actual source code. I admit that this can be confusing as monadic code also uses >>= as an infix operator.

Some functions, like null, can’t fail and we still use a result value for consistency, but this is a design choice. We can put these together to implement a length function. Before we do, here is a simple implementation that relies on exceptions:

And here is the implementation that relies on explicit error handling. For demonstration purposes we don’t use pattern matching for deconstructing a list but use null, hd, and tl.

At every step we need to check whether an error has occurred. If so, we return it. Otherwise we take the value and carry on. What used to be simple is now dominated by so much error handling that it is difficult to make out the simple underlying algorithm.

Error Handling Revisited

The key observation to a better solution is that we are composing functions and match the intermediate result as part of the composition. When we abstract this into an infix operator, we regain clarity. This operator is called bind and usually bound to >>=:

Function bind has type ‘a result -> (‘a -> ‘b result) -> ‘b result:

  • bind takes as first argument an intermediate result m which it inspects. If it is Ok(a), it calls f with value a. Otherwise it passes on the error unchanged.
  • bind takes as second argument a function that takes a plain value but returns a result value.

The magic of bind is put to work when used as an infix operator that receives the intermediate result on the left and the function on the right. The length function becomes:

Function null provides a bool result value that is unwrapped by >>= and bound to zero when there is no error. Otherwise (invisibly) the error is passed on. Likewise, tl returns the tail of list xs as ‘a list resultand it is bound to xs’ such that it can be passed to length recursively.

When taking advantage of pattern matching, the code can be further simplified.

In a real program we would still rely on pattern matching rather than using hd, null, and tl (which I used mostly for demonstration purposes) to decompose the argument. Now length would become almost as simple as the classic implementation:

All error handling happens invisibly inside >>= and the recurring theme is left >>= fun x -> right where the left side produces a result that is unwrapped, bound to x, and consumed by right, which in turn will produce a result value. The simplist way to produce such a result is by using return.

The Error Monad

The type result introduced above is a monad and commonly known as the error monad. A monad is defined by ingredients that we already know:

  • The monad ‘a t (we used ‘a result above)
  • Function return that turns a value into a monadic value
  • Function bind to compose computations
  • Additional, domain-specific functions

Repeating the definition of the error monad in the more general notation above, it becomes:

In addtion, return and bind must be well behaved in their interaction. We will not talk about what this means here. Nonetheless, I hope that the error monad demonstrates what a monad is: a clever way to compose functions. For this reason, Monads have also been described as “programmable semikolon”.

One tip: when reading monadic code that contains return and >>= it is usually best not to think about what these functions do in detail and to focus on the other parts precisely because >>= abstracts something that we should not need to know in detail.

Extended Example

What could go wrong? Create a directory with a given set of permissions and ownership. And if it exists already, fix permissions and ownership when necessary or fail otherwise while giving a good reason.

As it turns out, this requires at least 7 system calls: stat, mkdir, getgrnam, getpwnam, rmdir, chmod, chown and each can go wrong. The task concisely outlined above is easily burried in a maze of if-then-else, exception handling, and error reporting.

I am discussing the problem and a monadic solution in a blog post on Medium that extends the theme that we have seen here. The heart of the solution is function mk that employs the error monad:

Function Mkdir.at creates a directory with a given set of permissions and ownership. On success it will return an Ok () value and Error msg otherwise.

Below is the gist of the implementation that we should be able to read even without knowing all the details:

Source Code

This section just pulls all the code snippets above together into one file such that it can be compiled and used interactively. The code is available from https://gist.github.com/lindig/b2a239514711667906e4052031ae20ec.

Notes to Self