Introduction to Haskell, Part 3: Monads
Pages: 1, 2
For example, the value of square 3
is valid, because 3
is of type Int
and Int
is a member of type class Num
. Evaluating this expression causes the Haskell compiler to look under the covers to see how multiplication is defined for Int
values and replace the invocation of *
with the actual implementation of multiplication for Int
, or the function mulInt
. Evaluating the expression square 2.5
also works, because the value 2.5
is of type Double
, and Double
is a member of type class Num
. In this case, the compiler invokes the version of *
for the Double
type, or the function named mulDouble
in this example.
The expression square "this"
is invalid because square
is defined only over numbers, and the value "this"
is a string, and strings are not numbers. Attempting to evaluate this expression in the interpreter produces the following error:
$ ghci
...
Prelude> let square x = x * x
Prelude> :t square
square :: (Num a) => a > a
Prelude> square "this"
<interactive>:1:0:
No instance for (Num [Char])
arising from use of `square' at <interactive>:1:012
Possible fix: add an instance declaration for (Num [Char])
In the expression: square "this"
In the definition of `it': it = square "this"
Here is proof that this one definition of square
works on different kinds of numbers:
Prelude> square 3
9
Prelude> square 2.5
6.25
Monads
Now that you've seen how type classes group together a series of related types, it's time to return to the topic of monads. Type classes are important, because, at the most fundamental level of implementation, monads are merely containers that are instances of the type class Monad
:
class Monad m where
(>>=) :: m a > (a > m b) > m b
(>>) :: m a > m b > m b
return :: a > m a
fail :: String > m a
Here is what this type class declaration means:
 A type m is a monad if it implements four operations: bind (the
>>=
operator), then (the>>
operator), return, and fail.  Monads are a higher order type. That is, a monad m is something that acts as a container over some other type (expressed as
m a
orm b
in this definition).  The bind operation takes a container (
m a
), and a function (a > m b
) and returns a new container (m b
). This function must accept the value from the first container and produce the second container.  The then operation works just like bind, except the value in the first container is ignored, and the second function is replaced by a simple container.
 The return operation takes a simple value and turns it into a container.
 The fail operation takes a string and returns a container. (This operation may be used to "fail fast," so once a failure is encountered, all subsequent operations are skipped.)
These ideas come from category theory, which also defines a few important properties that monadic operators must provide:
 The expression
return a >>= f
is equivalent tof a
.  The expression
m >>= return
, is equivalent tom
. (This means that thereturn
function used on its own is a noop.return
exists to inject values into a container.)  The expression
(m >>= f) >>= g
, wherem
is a container andf
andg
are functions, is equivalent to the expressionm >>= (\x > f x >>= g)
. That is, the bind operation always works from lefttoright.
These definitions are quite formal and may be somewhat confusing the first few times you read them, but the goal here is to define a series of simple, mechanical operations for connecting containers together. Monads provide a way to chain together a sequence of operations in a manner that works using just pure functions. And because they are defined in terms of pure functions, the Haskell compiler can understand them and handle the busywork for you.
Thinking about these operations in the abstract can be a little difficult. Here is a simple example. These two functions operate within the IO monad, the mechanism Haskell uses to communicate with the outside world.
getLine :: IO String
putStrLn :: String > IO ()
Because all IO operations are monadic, the IO monad acts as a container that isolates external side effects in a Haskell program. The function getLine
reads a line of input from the console and returns that string inside the IO container. The function putStrLn
accepts a plain string value, prints that string to the console (with a trailing newline), and returns an empty value, also contained within the IO monad.
Here is how these functions are connected together, using the monad operators:
echo :: IO ()
echo = getLine >>= putStrLn
In this example, the monadic action getLine
is evaluated and produces an IO String
result. The bind function (>>=
) gets this result, extracts the string from its IO container, and sends it to the monadic action putStrLn
. Finally, the action putStrLn
consumes this plain string, sends it to the console, and returns the empty value within the IO container (the IO ()
type). This final result is returned by the echo
function, so echo
must have the type IO ()
as well.
Using the monad operators in this fashion may be powerful, but it is very cumbersome. Thankfully, Haskell allows for an alternate means to describe monadic operations, the "monadic do" notation. Here is an equivalent way of writing the echo
function:
echo :: IO ()
echo = do str < getLine
putStrLn str
Here, a monadic function is a series of statements, aligned according to the layout rule. The function getLine
returns a value, so that value can be captured in the symbol str
and used later within this function. (Remember, str
is not a variable, because its value cannot change.) Because this value is extracted from the monadic container, the value of str
here is of type String
, not IO String
. As a result, this value can be passed to the function putStrLn
, which is expecting a String
as its first argument.
Because these actions are performed lefttoright (or toptobottom in "monadic do" notation), monads enforce an order of execution on their statements. With pure functions, subexpressions may be evaluated in any order without changing their meaning. With monadic functions, the order of execution is very important. For example, it is meaningless to evaluate putStrLn
in this function before receiving its input from getLine
.
Conclusion
The IO monad is not the only monad in Haskell, but it is one of the most frequently used. Other monads offer different kinds of containers and code structures. For example, lists are simple data structures, but they may also be used as monads. The GHC standard library contains series of useful monads that are simple, generic, and useful in a variety of contexts. One of these is Parsec, a very convenient library for writing parsers.
A lot of exciting new work in Haskell is being done using monads. For example, transactional memory and nested data parallelism are two useful techniques that help when distributing a Haskell program across multiple CPUs. The portions of the program that use these features are strictly segregated from the portions of a program that are purely functional or deal with IO. This makes it easy for the compiler to isolate the portions of a program that are transactional or parallel from the parts that aren't. Furthermore, the type system enforces this separation and highlights bugs in code where monads are improperly used and don't make any sense.
All of these structures share one thing in common—they are container types that are declared to be instances of the Monad
type class. Each monad type defines its version of the basic operations of bind (>>=
), then (>>
), return, and fail. The Haskell compiler can then compose a sequence of monadic actions into a single, larger monadic action. The compiler also replaces these generic operations with the specific implementations needed for a given monad, just as it replaces the generic numeric multiplication operation with the specific operation needed to multiply Int
or Double
values. Additionally, because all monads use the same basic operators, complicated monadic actions can be expressed using a simple "monadic do" notation, making these operations easier to understand.
Articles in This Series
 An Introduction to Haskell, Part 1: Why Haskell
 Introduction to Haskell, Part 2: Pure Functions

More monad introduction
20070803 11:16:09 wneumann [View]
Adam Turoff is a software developer with more than a dozen years of experience in building web backend systems with open source tools. His current project uses a mixture of Perl, Ruby, Rails, Haskell, and other tools.
Return to ONLamp.com.