A monad tutorial

Haskell Monads

Monad is a type class that declares the function return and the operator >>= (called bind). Monadic types are instances of the monad type class. Examples of monadic types are [a], Maybe a and IO a.

return

The return function takes a value and returns it inside the minimal context of the monadic type thus preserving that value. For example return 1, when in the List monad, returns [1] - Why [1]? Well, [] would be minimal, but does not preserve the value, whereas [1,1], [1,1,1], ... [1..] are not minimal.

To be "in the List monad" just means that we are using return or bind when working with values of type [a]. return and bind are overloaded; their type-specific behaviour is defined in their type class instance.

Similarly, when in the Maybe monad, return 1, returns Just 1 since alternatively Nothing does not preserve the value.

return wraps a value in a monadic value and returns it. It's a function and not a statement and is not to be confused with the "return" common to imperative languages.

bind

The bind operator >>= has the following type signature.

Monad m => m a -> a -> m b -> m b

or equivalently:

Monad m => m a -> ( a -> m b ) -> m b

As you can see, the first argument of the bind operator is a monadic value, the second a function that takes a plain value and returns a monadic value, and the result of the bind operator is the value returned by that function. In other words, bind allows us to apply a function that expects a plain value to a monadic value.

Why is bind useful?

It allows us to chain functions (like the . operator for ordinary functions) whilst preserving  the context of the computation.

For example, when chaining functions in the Maybe monad the context is one of computational failure akin to Exceptions. A failure is indicated by the value Nothing. If we chain two or more functions and any one of them fails the result ought to be Nothing.

Let's say we perform two DB lookups. The first takes a primary key and returns Just table1record, where table1record contains some foreign key. The second lookup takes that foreign key to lookup another record in table2. Clearly either the first or the second lookup can fail (i.e. return Nothing) in which case the chained function calls should also return Nothing. (Only if both calls find matching records the chained calls return a value of Just table2record.)

The bind operator for the Maybe monad knows that and handles Nothing for us so that we don't have to check at every step whether a computation failed (i.e. returned Nothing). This is nice, because it saves us from typing the repeated check manually.

So the context of the Maybe monad is one of failure. What's the context of the List and IO monads?

For lists it's indeterminism, i.e. a computation that returns a list of zero, one or more results. (Remember school: What's the square root of four? It's both 2 and -2.) When we bind functions under the list monad, we expect the computation to return no results if any one computation returns none and one or more results otherwise. For example, if some computation A returns two results and applying those two results in turn to some computation B that for the first result returns 3 and for the second returns 4 results we expect the chained operation to return a total of 7 results. This makes intuitive sense: If some computation or process has an uncertain outcome and we apply that outcome to yet another process that is also uncertain, overall uncertainty with respect to the final result grows quickly. The list monad factors this behaviour out, just as the Maybe monad factors out Exception bubbling.

The context for the IO monad is side effects.

do notation

Another benefit of monads is Do notation. Do notation is syntactic sugar (i.e. a convenient notation that is just expanded to nested binds and returns) that makes it easy to write a sequence of computations, separated by semicolons. In the case of the Maybe monad, as explained above, this means that if one computation fails the chained/overall computation fails. Since the same do notation is used with different monads some call this the programmable semicolon. It's programmed in the bind implementation of the monad type class instance.

Next up

Part II to follow...

Nothing quite helps me to understand a topic better than trying to explain it in a writeup. This is my understanding of monads but I am only a beginner. Please correct me.

 

 

 



blog comments powered by Disqus
10 October 1990 - 10:11:12 10 October 1990