On Monads

Monads are like lions. It is easy to show what a lion is, but afterwards the students won’t understand lions or be able to work with them.

I will use a Java8-like syntax and terminology for code. A monad consists of a generic type M<T> and two functions–M<T> unit(T t) and M<U> bind(M<T> mt, Function<T,M<U>> f)–that satisfy the monad laws:

  • bind(unit(x),f) == f(x)
  • bind(x,unit) == x
  • bind(bind(x,f),g) == bind(x,y -> bind(f.apply(y),g))

That is all.

To understand monads you need to know why people use them. Programs are usually composed of smaller programs and monads let you redefine how composition works. This way, you can extend all datatypes with new operators, while preserving existing ones.

Take the a typical list monad for example. Instances of the generic type List<T> have methods for working with lists as part of the generic type itself. The function unit sends and object x to a one element list unit(x). That way it makes list operations available on the object. On the other hand, unit(x.anyMethod(y)) turns the result of any method defined on x into a list. Finally the function bind(l,f) applies the list valued function f to every element of the list l and concatenates the results in order into one big list. That way we can apply list valued functions to lists like we can apply ordinary functions to ordinary objects. Hence List<T> has access to both the methods of T and of List and also to every possible combination of them.

You can use the list monad for non deterministic algorithms, where functions can have several different return values. Many other kinds of functions and other methods of composition have their own monads. For example, a futures monad takes care of non blocking asynchronous computations while hiding the callback hell that usually goes with it.

Working with monads is a matter of remembering that you introduce a new form of composition on purpose and that you usually neither have to leave it behind, nor want to do so. Generally you want to keep using the bind and unit functions to keep everything happening “inside the monad” until “the end of the world”, which is the main method of your program. As always, practice makes perfect.