The most common complaint I hear from students of Haskell is that monads are hard to comprehend. This is a problem, because monads are as fundamental to modern Haskell as procedures are to C, or classes to Ruby. They are the primary method of sequencing computation in what is otherwise a declarative, lazily evaluated language.
I believe that persistent failure to understand monads is not due to the subject matter, but to the manner in which it is taught. Monads are derived from a mathematics known as category theory; most existing tutorials, such as Wikibooks Haskell/Category theory, attempt to describe them in terms of other CT constructs. A few tutorials, drawing from the _why school of thought, use more concrete examples — Of monads and spacesuits is an infamous and baffling case.
This article is written for somebody who understands the concept of an anonymous closure (AKA block, function literal, lambda), and is at least somewhat familiar with Haskell's typeclass mechanism. Real World Haskell, Chapter 6 provides an overview of of typeclasses. No attempt is made to make the code presented here elegant or efficient — rather, it tries to illustrate examples using minimum concepts.
Another monad tutorial along the same line of thought is Understanding Haskell Monads.
The Monad typeclass defines two fundamental operations. Note that my description of these operations is very generic; that is because the actual behavior is determined entirely by the type's instance.
bind, creates a single wrapped value by applying a function to a previously wrapped value.
With these definitions in place, we can also define some variants for special purposes:
One of the most common design mistakes in popular languages is an untyped null value. Named NULL, nil, null, or undefined, null values are intended to represent that some input does not have a corresponding output. Unfortunately, being untyped means there is no way to prevent a null value from being passed to a function or procedure.
Haskell avoids this by defining a new type, Maybe, which can contain a value. Using a custom data type prevents null value errors, which manifest as segmentation faults or unexpected exceptions in other languages.
Often, Maybe values will be used to represent whether an entire sequence of computations succeed. For example, lets define a function which returns its input if a test passes, or Nothing if it fails:
We'd like to re-use this function for more complex tests; for example, that two integers are positive, and the first is smaller than the second. Note that this example makes use of partial application — (>= 0) is equivalent to (\x -> x >= 0).
As demonstrated here, manually chaining Maybes together is awkward and verbose.
The pattern being used here can be simplified by using monads. Our monad instance for Maybe will behave like the case statements above; if Nothing is returned at any step, the results of the entire computation will be Nothing.
We'll use this to write a new positiveAndSmaller. If you've forgotten the syntax for anonymous functions in Haskell, know that this function multiplies its parameters: (\x y -> x * y). Here's positiveAndSmaller2:
Although still ugly, this notation is sufficiently generic that the compiler can generate it from an imperative notation. Lets examine one more example; this time, using the imperative-style do notation. Note that although this is imperative, it is not impure:
Maybe is useful, but sometimes more information about the source of an error is required. For this, we can use Either. Lets look at the definition and monad instance:
This is almost the same as Maybe, except that we can include additional data in the error case.
At first glance, it's not immediately obvious how a list can be a monad. Both examples we've looked at so far have contained at most one value, but lists may contain an arbitrary amount of values. The trick is that the >>= operator is sufficiently generic that it doesn't care how you build the output.
First, we'll define the list type and some helpful functions. I'm using English words for the constructors, rather than the customary : and [] symbols, so that it's easier for students to understand. This is a standard, LISP-style linked list.
You've probably figured out already what the monad instance is going to look like; there's only one function there which matches up with the type signature for >>=.
It's not immediately clear what this instance could be used for, but it's great whenever one needs to build or convert lists. Here's an implementation of filter, which reduces a list to items matching some predicate:
State's a big deal in Haskell; much thought has been put into the best way to cleanly write stateful algorithms in a language which discourages them. The simplest way discovered so far is to simply define each step of the algorithm as a state transformer. That is, it takes a state as input, and returns it (plus a result value) as output. Lets look at a stateful way to sum a set of numbers; each call returns the current sum, and a state to get the next sum:
We can also define a function to set the current sum to anything; this function uses the () type, which is the Haskell equivalent of void. Note that this function ignores its input state.
We can see that both functions have a similar type signature; we'll represent this similarity using a new data type, State:
Note that State values contain a function; each State is a state transformer. Lets define how State should behave as a monad:
We can use monadic state to clean up statefulSum and setSum:
Monads can be understood in terms of existing, well-known concepts. By gaining an intuitive grasp of the purpose and use of monads, a student of Haskell can write effective programs well before fully understanding the category theory behind their code. Students are, of course, encouraged to go farther if interested — but you don't need to know what an endofunctor is before taking advantage of Haskell.