Hacker News new | ask | show | jobs
by selbekk 2390 days ago
I've always struggled to grok the concept of monads. Worth the 2 minute read.
3 comments

Except this “simple” metaphor isn’t too useful. For monads like IO and State, “monad as a container” is a stretch. And some don’t fit at all — the list monad often is imagined as a computational context with nondeterminism, not simply a container.

https://byorgey.wordpress.com/2009/01/12/abstraction-intuiti...

I don't really understand your point of view. Especially the list monad is literally a list. The shape of bind for the list monad is [a] -> (a -> [b]) -> [b]. If that's not a container, I don't know what is. The fact that it can be used to solve problems where the list represents a non-deterministic result is really secondary. I mean, I suppose you can think of it as being different, but it seems a lot more complicated in my mind.

I think some "containers" a definitely hard to envision. A partially applied function is also a monad (i.e. it's trivial to write a meaningful bind for it). It may be hard to think of a partially applied function as "containing" the applied parameters, but I still think that's easier than any other way of envisioning it. But maybe it's a matter of horses for courses.

For IO, the monad is a "box" you put the entire mutable universe in so you can pretend the rest of your program is stateless.
A monad is for composing computations with effects: let's say you have some function f:: a -> b returning some b, but now you need the computation to happen with some extra effect (throwing exception, changing state, futures, etc).

A way to model effects is to have f :: a -> Tb, where T is a type constructor encapsulating a value of type b produced with some effect. Let's call this new f a T-program, then a monad is exactly what you need to compose T-programs nicely.

The monad "return" is the simplest T-program, and the monad "bind" helps you compose T-programs: given another T-program g :: b -> Tc, you cannot simply compose g with f since the types don't match. However, the bind operator >>= :: (b -> Tc) -> Tb -> Tc gives you a function (>>= g) :: Tb -> Tc which does compose with f, producing a new T-program: a -> Tc. So now you can compose T-programs, yay! The monad laws ensure that T-programs form a category, which is to say that composition works the right way.

So a monad is just the plumbing you need to compose computations with effects in a nice way.

> A monad is for composing computations with effects

To nitpick: that's only one particular use of some monads.

It's pretty close to how both Moggi and Wadler formulated it in their respective seminal papers which introduced monads for representing different notions of computations in functional languages.
Well of course, but arguably there's not much space between "monads-as-containers/burritos" and "monoids-in-the-category-of-endofunctors" presentations...

If you know a better one in between I'm all ears :)

Another way to say it: a monad is a generic type with the sufficient API to do binding sequences like

    x <- fetch "foo"
    y <- frob x
    return (x + y)
The meaning of binding/sequencing is decided by the particular monad instance. This is why it's a useful formalism to represent things like asynchronous I/O (you make the sequencing mean promise chaining), abortable computations (you make the sequencing cancel when it sees failure values), combinatorial/nondeterministic programming (you make it so one binding can happen several times).

Monads are also closely related to continuation-passing style or delimited continuation capture, and those techniques can also be used to implement everything monads can.