| Prev: Meet the Monads | TOC: Contents | Next: The monad laws |
The discussion in this chapter involves the Haskell type class system. If you are not familiar with type classes in Haskell, you should review them before continuing.
In Haskell, there is a standard Monad class that defines the names
and signatures of the two monad functions return and
>>=. It is not strictly necessary to make your monads
instances of the Monad class, but it is a good idea. Haskell has
special support for Monad instances built into the language and
making your monads instances of the Monad class will allow
you to use these features to write cleaner and more elegant code. Also,
making your monads instances of the Monad class communicates
important information to others who read the code and failing to do so can
cause you to use confusing and non-standard function names. It's easy to do
and it has many benefits, so just do it!
The standard Monad class definition in Haskell looks
something like this:
class Monad m where
(>>=) :: m a -> (a -> m b) -> m b
return :: a -> m a
|
Continuing the previous example, we will
now see how the Maybe type constructor fits into the Haskell monad
framework as an instance of the Monad class.
Recall that our Maybe monad used the Just
data constructor to fill the role of the monad return function
and we built a simple combinator to fill the role of the monad >>=
binding function. We can make its role as a monad explicit
by declaring Maybe as an instance of the Monad class:
instance Monad Maybe where
Nothing >>= f = Nothing
(Just x) >>= f = f x
return = Just
|
Once we have defined Maybe as an instance of the
Monad class, we can use the standard monad operators to
build the complex computations:
-- we can use monadic operations to build complicated sequences maternalGrandfather :: Sheep -> Maybe Sheep maternalGrandfather s = (return s) >>= mother >>= father fathersMaternalGrandmother :: Sheep -> Maybe Sheep fathersMaternalGrandmother s = (return s) >>= father >>= mother >>= mother |
In Haskell, Maybe is defined as an instance of the Monad
class in the standard prelude, so you don't need to do it yourself.
The other monad we have seen so far, the list constructor, is also defined as an
instance of the Monad class in the standard prelude.
When writing functions that work with monads, try to make use of the
Monad class instead of using a specific monad instance.
A function of the type
doSomething :: (Monad m) => a -> m bis much more flexible than one of the type
doSomething :: a -> Maybe bThe former function can be used with many types of monads to get different behavior depending on the strategy embodied in the monad, whereas the latter function is restricted to the strategy of the
Maybe monad.
Using the standard monadic function names is good, but another advantage
of membership in the Monad class is the Haskell support for
"do" notation. Do notation is an expressive shorthand for building up
monadic computations, similar to the way that list comprehensions are an
expressive shorthand for building computations on lists. Any instance of
the Monad class can be used in a do-block in Haskell.
In short, the do notation allows you to write monadic computations using
a pseudo-imperative style with named variables. The result of a
monadic computation can be "assigned" to a variable using a left
arrow <- operator. Then using that variable in a
subsequent monadic computation automatically performs
the binding. The type of the expression to the right of the arrow is
a monadic type m a. The expression to the left of the arrow
is a pattern to be matched against the value inside the monad.
(x:xs) would match against Maybe [1,2,3], for example.
Here is a sample of do notation using the Maybe monad:
| Code available in example2.hs |
|---|
-- we can also use do-notation to build complicated sequences
mothersPaternalGrandfather :: Sheep -> Maybe Sheep
mothersPaternalGrandfather s = do m <- mother s
gf <- father m
father gf
|
fathersMaternalGrandmother written above without
using do notation.
The do block shown above is written using the layout rule to define the extent of the block. Haskell also allows you to use braces and semicolons when defining a do block:
mothersPaternalGrandfather s = do { m <- mother s; gf <- father m; father gf }
|
Notice that do notation resembles an imperative programming language, in which a computation is built up from an explicit sequence of simpler computations. In this respect, monads offer the possibility to create imperative-style computations within a larger functional program. This theme will be expanded upon when we deal with side-effects and the I/O monad later.
Do notation is simply syntactic sugar. There is nothing that can be done using do notation that cannot be done using only the standard monadic operators. But do notation is cleaner and more convenient in some cases, especially when the sequence of monadic computations is long. You should understand both the standard monadic binding notation and do notation and be able to apply each where they are appropriate.
The actual translation from do notation to standard monadic operators is roughly
that every expression matched to a pattern, x <- expr1, becomes
expr1 >>= \x ->and every expression without a variable assignment,
expr2 becomes expr2 >>= \_ ->All do blocks must end with a monadic expression, and a let clause is allowed at the beginning of a do block (but let clauses in do blocks do not use the "in" keyword). The definition of
mothersPaternalGrandfather above would be
translated to:
mothersPaternalGrandfather s = mother s >>= \m ->
father m >>= \gf ->
father gf
|
Haskell provides built-in support for monads. To take advantage of Haskell's
monad support, you must declare the monad type constructor to be an instance
of the Monad class and supply definitions of the return and
>>= (pronounced "bind") functions for the monad.
A monad that is an instance of the Monad class can be used
with do-notation, which is syntactic sugar that provides a simple,
imperative-style notation for describing computations with monads.
| Prev: Meet the Monads | TOC: Contents | Next: The monad laws |