Why does it matter that Future is a monad?

by Charles Miller on June 3, 2015

Seen on Twitter:

Either and Promises/Futures are useful and I’ll use them next time they’re appropriate. But outside Haskell does their monad-ness matter?

All code below is written in some made-up Java-like syntax, and inevitably contains bugs/typos. I'm also saying "point/flatMap" instead of "pure/return/bind" because that's my audience. I also use "is a" with reckless abandon. Any correspondance with anything that either be programatically or mathematically useful is coincidental

What is a monad? A refresher.

A monad is something that implements "point" and "flatMap" correctly.

I just made a mathematician scream in pain, but bear with me on this one. Most definitions of monads in programming start with the stuff they can do—sequence computations, thread state through a purely functional program, allow functional IO. This is like explaining the Rubiks Cube by working backwards from how to solve one.

A monad is something that implements "point" and "flatMap" correctly.

So if this thing implements point and flatMap correctly, why do I care it's a monad?

Because "correctly" is defined by the monad laws.

  1. If you put something in a monad with point, that's what comes out in flatMap. point(a).flatMap(f) === f(a)
  2. If you pass flatMap a function that just points the same value into another monad instance, nothing happens. m.flatMap(a -> point(a)) === m
  3. You can compose multiple flatMaps into a single function without changing their behaviour. m.flatMap(f).flatMap(g) === m.flatMap(a -> f(a).flatMap(g))

If you don't understand these laws, you don't understand what flatMap does. If you understand these laws, you already understand what a monad is. Saying "Foo implements flatMap correctly" is the same as saying "Foo is a monad", except you're using eighteen extra characters to avoid the five that scare you.

Because being a monad gives you stuff for free.

If you have something with a working point and flatMap (i.e. a monad), then you know that at least one correct implementation of map() is map(f) = flatMap(a -> point(f(a)), because the monad laws don't allow that function to do anything else.

You also get join(), which flattens out nested monads: join(m) = m.flatMap(a -> a) will turn Some(Some(3)) into Some(3).

You get sequence(), which takes a list of monads of A, and returns you a monad of a list of A's: sequence(l) = l.foldRight(point(List()))((m, ml) -> m.flatMap(x -> ml.flatMap(y -> point(x :: y)))) will turn [Future(x), Future(y)] into Future([x, y]).

And so on.

Knowing that Either is a monad means knowing that all the tools that work on a monad will work on Either. And when you learn that Future is a monad too, all the things you learned that worked on Either because it's a monad, you'll know will work on Future too.

Because how do you know it implements flatMap correctly?

If something has a flatMap() but doesn't obey the monad laws, developers no longer get the assurance that any of the things you'd normally do with flatMap() (like the functions above) will work.

There are plenty of law-breaking implementations of flatMap out there, possibly because people shy away from the M-word. Calling things what they are (is a monad, isn't a monad) gives us a vocabulary to explain why one of these things is not like the other. If you're implementing a flatMap() or its equivalent, you'd better understand what it means to be a monad or you'll be lying to the consumers of your API.

But Monad is an opaque term of art!

So, kind of like "Scrum", "ORM" or "Thread"?

Or, for that matter, "Object"?

In summary:

As developers, we do a better job when we understand the abstractions we're working with, how they function, and how they can be reused in different contexts.

Think of the most obvious monads that have started showing up in every language1 over the last few years: List, Future, Option, Either. They feel similar, but what do they all have in common? Option and Either kind of do similar things, but not really. An Option is kind of like a zero-or-one element list, but not really. And even though Option and Either are kind of similar, and Option and List are kind of similar, that doesn't make Either and List similar in the same way at all! And a Future, well, er…

The thing they have in common is they're monads.


1 Well, most languages. After finding great branding success with Goroutines, Go's developers realised they had to do everything possible to block any proposed enhancement of the type system that would allow the introduction of "Gonads".

Previously: Simplicity isn't simple, a coda.

Next: Fuck Game of Thrones