Monads in Haskell
Monads…
Yeah, I know — it sounds weird the first time you hear it. When I first started learning Haskell about three years ago, the word itself felt mysterious, almost intimidating. Even in my free YouTube course, I don’t think I ever explained it in a way that truly clicked for everyone.
So this time, I wanted to fix that.
In this video and article, I’ll walk you through what Monads actually are — but not by dropping the term on you out of nowhere. We’ll start from the ground up: Functors, then Applicatives, and only then arrive at Monads.
Each step builds naturally on the one before it — and once you see the pattern, it stops being abstract and starts to make sense.
Step 1 — Functors: applying a function inside something
A Functor is any type that can be “mapped over.”
You already know this idea from other languages:
In Python, you’d do [x * 2 for x in numbers].
In Haskell, it’s fmap (*2) (Just 10) → which gives Just 20.
The wrapper (Maybe) stays the same — you just apply a function inside it.
That’s all a Functor is: a context you can apply a function to, without unwrapping it.
Step 2 — Applicatives: applying a wrapped function to a wrapped value
Applicatives go one step further.
Imagine having both the function and the value wrapped:
Just (2) <> Just 10 → gives Just 20.
It’s basically saying:
“I’ve got a boxed function and a boxed value. Apply one to the other.”
And when something’s missing (Nothing), the whole thing fails gracefully.
Applicative also gives us pure, which just lifts a plain value into a context, like:
pure 5 → Just 5.
Step 3 — Monads: chaining things that return wrapped results
Now comes the big one — Monads.
They let you chain functions that each return their own wrapped result.
For example:
safeDivide :: Float -> Float -> Maybe Float
safeDivide _ 0 = Nothing
safeDivide x y = Just (x / y)
Using the “bind” operator (>>=), we can write:
Just 100 >>= (x -> safeDivide x 2) >>= (y -> safeDivide y 5)
— Just 10.0
If any step returns Nothing, the whole chain stops.
So Monads are basically a clean, composable way to handle context — whether that’s “might fail,” “has side effects,” or “depends on previous results.”
And if you prefer cleaner syntax, do notation does the same thing with less noise.
Wrapping up
Functor → apply a function inside a context
Applicative → apply a wrapped function to a wrapped value
Monad → chain functions that return wrapped values
That’s it — the scary word hides a very simple pattern.
You’ll see it everywhere in Haskell: Maybe, IO, Either, List — all of them are Monads. Once you understand one, you’ve got them all.
If you want to see this concept click into place, check out the full video — I walk through everything live in GHCi with examples and visuals.