NEMJan 2007: I recently got around to writing up a much clearer explanation of these ideas at Monads. Various new 8.5 features make the result much easier to digest (hopefully), as well as more performant (although still no speed demon). I'll leave this page here for the curious. The implementation of algebraic pattern matching is possibly of some value.

NEM18Mar2005 Been playing around with Haskell a bit recently, and in particular, looking at monads. (See [1] for an introduction). This is a first stab at writing monads using TOOT. It's a bit ad-hoc at the moment, but I think it demonstrates the basic approach. Also, the sharp-eyed among you may notice an implementation of algebraic pattern-matching in this code...

Step 1: TOOT

As I haven't actually got round to releasing TOOT yet, here is a hacked up definition of types that TOOT provides. This version supports algebraic pattern matching (much like Algebraic Types):

Now, let's define our first monad: The Maybe monad. Useful for when you might return a value, or might not. (e.g. taking the head of an empty list might return Nothing, whereas a non-empty list would return Just the first element):

These do indeed all evaluate to true, showing that our monad satisfies the laws, at least, for these tests. (It should also work for any other test, but I don't have a proof of correctness).

To make this a bit more convenient, here's a first cut at Haskell's "do" notation, which adds some syntactic sugar (see Salt and Sugar) for the bind (>>=) operations. This version isn't entirely correct, but it illustrates the point: monads + a little sugar are extremely powerful.

NEM Here's a more complicated example, using an Error monad, which perhaps illustrates things a bit better. Imagine a world where Tcl didn't have built-in exception handling. In this world you might have operations which could either return a result, or might not in some error condition. One way to get around this is to return a return code and a potential result as a tuple (list). This is essentially what Tcl does at the C-level: the int return code signals TCL_OK, TCL_ERROR, etc, and the actual result (or error message) is stored in the interpreter. Now, what this means at the C-level is that you have a lot of code which does:

if (Tcl_SomeFunc(interp, ...) != TCL_OK) {
return TCL_ERROR;
}

which gets a bit tedious to keep typing, but generally works ok. What a monad allows you to do is to package up this boiler-plate code into an Error monad which does the right thing. Now, all you need to do is sequence your actions using the Error monad operations, and use throw/catch and everything should work ok. To demonstrate:

Note, in particular, that the printevery function doesn't have to know anything about the Error monad. So long as it uses the >>=, and >> sequence operators to string things together, then it will work fine. More subtlely, you could replace the Error monad with a different monad (for instance, one which does tracing, or one which does result caching), and the same code will keep working. Monads encapsulate methods of combining computations into larger computations.

One of the key points about monads is that operations which use the monad sequencing operations will generalise over different types of monad. To illustrate this, recall our "mmap" function (a sort of map/filter combined, much like Jim's lmap):

We used this function to map the "double" function over lists, using the Maybe monad to allow us to both perform a transformation (map), and filter. Well, we can also do exactly the same using the Error monad, without changing the mmap function. To show this, let's define an edouble function that does the same as double, but throws an error (using the Error monad) for numbers greater than 999: