Monads are much easier to recognise by experience than by definition. Come to think of it, that might be why people have such a hard time learning from blog posts! Anyway, here are some examples of Monads you might have used:

  • Optional/Maybe values (a potential value)
  • Future/Deferred (a value at some point in future)
  • Lists (multiple values of the same type)
  • Either (one type of value or another)
  • Writer (the value, plus an append-only log)
  • Result (either the value or an error)
  • I/O (a value you haven't input yet)

Generally the more examples of monads you’ve used, the more you’ll recognise it as a pattern. The main feature of interest is a bind function (>>= in Haskell, .flatMap in Scala) to combine these monads with functions to modify the values inside them.

A good example of this is a Future -- let's say we have some function which increments a number after 5 seconds.

function incrementSoon(number) -> Future Number {
    return Future {
      sleep 5
      return number + 1
    }
}

The syntax isn't important here, but the intent is that the function:

  • Starts a background task to sleep for 5, then increment the number; and
  • Immediately returns a Future which represents that computation and future result.

Let's say you also have a doubleSoon function which sleeps for 10 seconds, then returns the input number doubled. We'd like to be able to sequence these computations somehow -- so after 15 seconds in total, we'll have a number that's been incremented and then doubled.

One way to do this is to keep checking whether the future is complete yet:

num = 1
futureNum2 = incrementSoon(num)
while(!futureNum2.isDone) {
    ...wait...
}
futureNum4 = doubleSoon(futureNum2.returnValue)
while(!futureNum4.isDone) {
   ...wait...
}
print(futureNum4.value)

But this totally gets rid of the benefits of multi-threaded programming -- we've got a busy-loop on the main thread continually asking whether the secondary thread is done yet. It's the worst kind of micromanagement, and it slows everything down.

A better way of doing this would be to sequence the incrementSoon and doubleSoon functions together somehow. Javascript used to do this with callbacks, but functional programmers have known about the dangers of bracket abuse for too long to find callback pyramids appealing (LISP, anyone?).

The solution to this is to add an andThen method on the Future:

num = 1
futureNum2 = incrementSoon(num)
futureNum4 = futureNum2.andThen(x -> doubleSoon(x))

Or, without the intermediate step:

futureNum4 = incrementSoon(num).andThen(x -> doubleSoon(x))

See how convenient that is? We've got a Future value for 4, which is being calculated in the background. And if we change the name of andThen to flatMap, bingo -- we have a Monad!

This is the actually important idea behind Monads: chaining things together in some useful way. You keep the context around a value, and can then chain operations together which return values within the same context. This could be "the value will arrive in future", but it can also be "the value is optional", or "the computation also outputs text", or "the computation might fail". It's up to each individual Monad to decide how it gets chained together -- they all have different flatMap methods. The only invariant is that they must all have one!

(Side note: this configurable chaining-together of operations is why I've heard Monads called "programmable semicolons" in the past. But that's the sort of pithy description that only makes sense once you've actually understood what Monads are good for in the first place, so, I haven't used it myself.)