It is said that at some point in their life, every programmer will try to explain monads. It is also said that on that day, they will fail spectacularly.

Well, guess what day it is!

class Monad m where
  (>>=)  :: m a -> (  a -> m b) -> m b
  (>>)   :: m a ->  m b         -> m b
  return ::   a                 -> m a
  fail   :: String -> m a

Clear as mud, right?

Here’s the thing: I can’t explain monads to non-programmers, but I can probably explain them to JavaScript programmers in about four words.

You know how sometimes you have to do something asynchronously and then handle failure or success later? For example, you’ve probably used an XHR library that looks something like this:

fetch('https://example.org/api')
  .then(res => { ... })
  .catch(err => { ... })

Yeah? Okay. Here’s your four-word explanation:

Promise is a monad.*

*: Well… almost, anyway. It’s close enough to convey the idea, which is that “monad” is really best thought of as a pattern for handling various cases in a consistent way. Specifically, by bulding function pipelines that encapsulate data in a context which expresses which case you’re in at any point in time.

In JavaScript, you write code like this because blocking causes problems. In most languages where the word “monad” gets used, you’re doing it because of something a bit different: side-effects aren’t allowed. The syntax is different, and the cases are more common, but the idea is the same.

But, like, why though?

Okay, let’s imagine you want to make a counter function to generate unique IDs for things.

var CURRENT_ID = 0;
/**
 * Increment the ID counter and return the next result.
 */
function getId() {
  return ++CURRENT_ID;
}

getId(); // -> 1
getId(); // -> 2
getId(); // -> 3

What’s the problem here? Well, in a mathematical sense, getId() is not a function. That is to say, it’s not what we call a pure function. It produces side-effects. (Specifically, it is mutating the global variable CURRENT_ID.

Now, obviously this is a bad practice, but encapuslating the data in an object and having getId() be an instance method on that object wouldn’t change the fact that it’s producing side-effects. And in some languages, like Haskell, producing side-effects isn’t just bad. It’s impossible. So how would you make a counter like this if you couldn’t increment a variable?

Easy. You just need to pass the state with the function call:

function getId(lastId) {
  return lastId + 1;
}

const id1 = getId(0); // -> 1
const id2 = getId(id1); // -> 2
const id3 = getId(id2); // -> 3

This is very special-purpose, though. We can do something more general if we invert the control here and pass a function into the state instead of passing the state into the function.

// define a "box" to hold a value we want to keep track of
class Box {
  constructor(value) {
    // box up a value
    this.value = value;
  }
  flatten() {
    // unbox a value
    return this.value;
  }
  map(fn) {
    // map over a function and box up the result
    return new Box(this.flatMap(fn));
  }
  flatMap(fn) {
    // map over a function and return the result unboxed.
    return fn(this.flatten());
  }
}

new Box(0) // -> Box(0)
  .map(v => v + 2) // -> Box(2)
  .flatMap(v => v * 6); // -> 12

Oh look, it’s the identity monad! But wait a second… doesn’t that mean…

const names = $("div")
  .not(":hover")
  .find(".warning")
  .map(e => e.name);

…that this is the jQuery monad??

Sadly, no, like with Promise, jQuery isn’t quite a monad, as much as I wish I could say that. It doesn’t have the right set of methods. But this pattern of chaining calls that take functions as parameters, this is exactly what monads are for. So while this may not convey the idea precisely, I hope I’ve done a decent job of demystifying the things.