"Monad" has a fearsome reputation, but the idea is gentle and you have already met it in disguise. A monad is a design pattern for sequencing steps that each carry a little extra baggage — the possibility of failure, a list of results, an effect — while keeping the plumbing for that baggage out of your way. You write the happy path; the monad threads the baggage through automatically.
Concretely, a monad is a container type plus two operations:
>>=): take a
contained value, run a function that produces a new container, and flatten the result so
you don't end up with a container-of-a-container.That's it. Everything else is examples. Let's meet two friendly ones.
Maybe is a
Just x (success, carrying x) or
Nothing (failure, carrying nothing). It replaces error-prone null with
something the type-checker forces you to handle. The magic is flatMap: chain steps that
might fail, and the moment one returns Nothing, the rest are skipped — no cascade of
if (x !== null) checks.
Notice you never wrote "if the first division failed, skip the second." flatMap did the
short-circuiting for you. That is the monad earning its keep: the failure plumbing is
hidden inside flatMap, and your code reads like the happy path.
The very same pattern, with a different baggage: a list monad models a computation that can
return many answers. Here of is "a one-element list," and
flatMap runs a function that returns a list for each element, then flattens —
exactly JavaScript's built-in Array.prototype.flatMap. It automatically explores every
combination.
flatMap here is doing the nested-loop bookkeeping — "for every a, for every b" — behind
one clean expression. Same two operations (of and flatMap), completely
different behaviour: that reusability is exactly why the abstraction is worth naming.
Maybe, List, and the lazy world of
flatMap(of(x), f) is just f(x) —
wrapping then immediately binding does nothing extra.flatMap(m, of) is just m —
binding with the plain wrapper changes nothing.a then b then c
gives the same result however you bracket the binds.
These laws are why you can build long pipelines with confidence. And they're why languages add
special sugar for monads — Haskell's do-notation, and JavaScript's
async/await, which is really flatMap for the Promise monad ("a value that
will arrive later"). Every time you await, you're binding a monad.
Maybe), multiplicity
(List), asynchrony (Promise), state, logging — all sequenced by the same
flatMap shape. Learn it once, recognise it everywhere.Maybe<number> announces it might fail), you can't forget to deal with it — the
purity of the surrounding code is preserved.A few honest cautions as you start:
a => Maybe<B>), use flatMap. Using plain map
leaves you with a Maybe<Maybe<B>> — a nested box you then have to
unwrap by hand. Flattening is the whole point of flatMap.flatMap is to
avoid pattern-matching on Just/Nothing at every step. If you find
yourself unwrapping and re-wrapping constantly, you're fighting the abstraction.of and a law-abiding flatMap. You've been using Promises and
arrays' flatMap all along.