Here's my current stab at it:
Monads are bucket brigades:
- Each operation is a person standing in line; i.e. there's an unambiguous sequence in which the operations take place.
- Each person takes one bucket as input, takes stuff out of it, and puts new stuff in the bucket. The bucket, in turn, is passed down to the next person in the brigade (through the bind, or
>>=
, operation).
- The
return
operation is simply the operation of putting stuff in the bucket.
- In the case of sequence (
>>
) operations, the contents of the bucket are dumped before they're passed to the next person. The next person doesn't care what was in the bucket, they're just waiting to receive it.
- In the case of monads on
()
, a ticket is being passed around inside the bucket. It's called "the Unit", and it's just a blank sheet of paper.
- In the case of IO monads, each person says something aloud that's either utterly profound or utterly stupid – but they can only speak when they're holding the bucket.
Hope this helps. :-)
Edit: I appreciate your support, but sadly, the Monad Tutorial curse has struck again. What I've described is just function application with containers, not monads! But I'm no nihilist – I believe the Monad Tutorial curse can be broken! So here's a somewhat more, um, complicated picture that I think describes it a bit better. You decide whether it's worth taking to your friends.
Monads are a bucket brigade with project managers. The project managers stand behind all but the first member of the brigade. The members of the bucket brigade are seated on stools, and have buckets in front of them.
The first person receives some stuff, does something with it, and puts it in a bucket. That person then hands off – not to the next person in the brigade, that would be too easy! :-) – but to the project manager standing behind that person.
The project manager (her name is bind, or >>=
) takes the bucket and decides what to do with it. She may decide to take the first person's stuff out of the bucket and just hand it to the person in front of her without further ado (that's the IO monad). She may choose to throw the bucket away and end the brigade (that's fail
). She may decide to just bypass the person in front of her and pass the bucket to the next manager in the brigade without further ado (that's what happens with Nothing
in the Maybe
monad). She may even decide to take the stuff out of the bucket and hand it to the person in front of her a piece at a time! (That's the List monad.) In the case of sequence (>>
) she just taps the shoulder of the person in front of her, instead of handing them any stuff.
When the next person makes a bucket of stuff, the person hands it to the next project manager. The next project manager figures out again what to do with the bucket she's given, and hands the stuff in the bucket to her person. At the end, the bucket is passed back up the chain of project managers, who can optionally do stuff with the bucket (like the List
monad assembling all the results). The first project manager produces a bucket of stuff as the result.
In the case of the do
syntax, each person is actually an operation that's defined on the spot within the context of everything that's gone before – as if the project manager passes along not just what's in the bucket, but also the values (er, stuff) that have been generated by the previous members of the brigade. The context building in this case is much easier to see if you write out the computation using bind and sequence instead of using the do
syntax – note each successive "statement" is an anonymous function constructed within the operation that's preceded that point.
() values, IO monads, and the return
operation remain described as above.
"But this is too complicated! Why can't the people just unload the buckets themselves?" I hear you ask. Well, the project manager can do a bunch of work behind the scenes that would otherwise complicate the person's work. We're trying to make it easy on these brigade members, so they don't have to do too much. In the case of the Maybe monad, for example, each person doesn't have to check the value of what they're given to see if they were given Nothing – the project manager takes care of that for them.
"Well, then, if you're realliy trying to make each person's job easier, why not go all the way – have a person just take stuff and hand off stuff, and let the project manager worry about the bucketing?" That's often done, and it has a special name called lifting the person (er, operation) into the monad. Sometimes, though, you want a person that has something a bit more complicated to do, where they want some control over the bucket that's produced (e.g. whether they need to return Nothing
in the case of the Maybe
monad), and that's what the monad in full generality provides.
The points being:
- The operations are sequenced.
- Each person knows how to make buckets, but not how to get stuff out of buckets.
- Each project manager knows how to deal with buckets, and how to get stuff out of them, but doesn't care what's in them.
Thus ends my bedtime tutorial. :-P