Programming with Effects III
============================
> {-# LANGUAGE NoImplicitPrelude #-}
> module Monads3 where
> import Prelude hiding (getLine, putStrLn, sequence, (>>), getLine)
Announcements
* HW 4 due next Tuesday
* I'll be away Monday/Tuesday. Marco will be giving the lecture
* Complete the end of [Monads2](Monads2.lhs) on your own
(Using a Generic State Transformer).
Random Numbers
--------------
See [RandomGen](RandomGen.html)
The IO Monad
------------
Recall that interactive programs in Haskell are written using the
type `IO a` of "actions" that return a result of type `a`, but may
also perform some input/output. A number of primitives are
provided for building values of this type, including:
~~~~~{.haskell}
return :: a -> IO a
(>>=) :: IO a -> (a -> IO b) -> IO b
getChar :: IO Char
putChar :: Char -> IO ()
~~~~~
The use of return and `>>=` means that `IO` is monadic, and hence
that the do notation can be used to write interactive programs.
For example, the action that reads a string of characters from
the keyboard can be defined as follows:
> getLine :: IO String
> getLine = undefined
It is interesting to note that the `IO` monad can be viewed as a
special case of the state monad, in which the internal state is
a suitable representation of the "state of the world":
~~~~~{.haskell}
type World = ...
type IO a = World -> (a,World)
~~~~~
That is, an action can be viewed as a function that takes the
current state of the world as its argument, and produces a value
and a modified world as its result, in which the modified world
reflects any input/output performed by the action. In reality,
Haskell systems such as Hugs and GHC implement actions in a more
efficient manner, but for the purposes of understanding the
behavior of actions, the above interpretation can be useful.
Arbitrary monads
----------------
An important benefit of abstracting out the notion of a monad into
a single typeclass, is that it then becomes possible to define a
number of useful functions that work in an arbitrary monad.
We've already seen this in the `pairs` function
> pairs xs ys = do
> x <- xs
> y <- ys
> return (x, y)
What do you think the type of the above is ? (I left out an annotation
deliberately!)
~~~~~{.haskell}
ghci> :type pairs
~~~~~
It takes two monadic values and returns a single *paired* monadic value.
Be careful though! The function above will behave differently depending
on what specific monad instance it is used with! If you use the `Maybe`
monad
~~~~~{.haskell}
ghci> pairs (Nothing) (Just 'a')
ghci> pairs (Just 42) (Nothing)
ghci> pairs (Just 2) (Just 'a')
~~~~~
this generalizes to the list monad
~~~~~{.haskell}
ghci> pairs [] ['a']
ghci> pairs [42] []
ghci> pairs [2] ['a']
ghci> pairs [1,2] "ab"
~~~~~
However, the behavior is quite different with the `IO` monad
~~~~~{.haskell}
ghci> pairs getChar getChar
~~~~~
Other common generic operations can be adapted for monadic
programming. For example, the `map` function on lists can be
generalized as follows:
> liftM :: Monad m => (a -> b) -> m a -> m b
> liftM f = undefined
Similarly, `concat` on lists generalizes to:
> join :: Monad m => m (m a) -> m a
> join mmx = undefined
As a final example, we can define a function that transforms
a list of monadic expressions into a single such expression that
returns a list of results, by performing each of the argument
expressions in sequence and collecting their results:
> sequence :: Monad m => [m a] -> m [a]
> sequence = undefined
See the library
[Control.Monad](http://www.haskell.org/ghc/docs/latest/html/libraries/base/Control-Monad.html)
for *many* more general purpose monad operations.
Monads As Programmable Semicolon
--------------------------------
It is sometimes useful to sequence two monadic expressions,
but discard the result value produced by the first:
f (3);
g (3);
> (>>) :: Monad m => m a -> m b -> m b
> mx >> my = undefined
For example, in the state monad the `>>` operator is just normal
sequential composition, written as `;` in most languages. Without
using layout for the `do` notation, sequencing is a semicolon too.
> hello :: IO ()
> hello = putChar 'H' >> putChar 'e' >> putChar 'l' >> putChar 'l' >> putChar 'o'
> hi = do
> putChar 'H'
> putChar 'i'
> return ()
Indeed, in Haskell the entire `do` notation with or without `;` is
just [syntactic sugar][4] for `>>=` and `>>`. For this reason, we can
legitimately say that Haskell has a [*programmable semicolon*][5].
Exercise
--------
- Define `liftM` and `join` more compactly by using `>>=`.
- Explain the behavior of sequence for the maybe monad.
The monad laws
==============
Earlier we mentioned that the notion of a monad requires that the
return and `>>=` functions satisfy some simple properties. The
first two properties concern the link between return and `>>=`:
~~~~~{.haskell}
return x >>= f = f x -- (1)
mx >>= return = mx -- (2)
~~~~~
Intuitively, equation (1) states that if we return a value `x` and
then feed this value into a function `f`, this should give the same
result as simply applying `f` to `x`. Dually, equation (2) states
that if we feed the results of a computation `mx` into the function
return, this should give the same result as simply performing `mx`.
Together, these equations express --- modulo the fact that the
second argument to `>>=` involves a binding operation --- that
return is the left and right identity for `>>=`.
The third property concerns the link between `>>=` and itself, and
expresses (again modulo binding) that `>>=` is associative:
~~~~~{.haskell}
(mx >>= f) >>= g = mx >>= (\x -> (f x >>= g)) -- (3)
~~~~~
Note that we cannot simply write `mx >>= (f >>= g)` on the right
hand side of this equation, as this would not be type correct.
As an example of the utility of the monad laws, let us see how they
can be used to prove a useful property of the `liftM` function above,
namely that it distributes over the composition operator for
functions, in the sense that:
~~~~~{.haskell}
liftM (f . g) = liftM f . liftM g
~~~~~
This equation generalizes the familiar distribution property of
map from lists to an arbitrary monad. In order to verify this
equation, we first rewrite the definition of `liftM` using `>>=`:
~~~~~{.haskell}
liftM f mx = mx >>= \x -> return (f x)
~~~~~
Now the distribution property can be verified as follows:
~~~~~{.haskell}
(liftM f . liftM g) mx
= ...
= liftM (f . g) mx
~~~~~
Exercise
--------
Show that the maybe monad satisfies equations (1), (2) and (3).
Other topics
------------
The subject of monads is a large one, and we have only scratched
the surface here. If you are interested in finding out more,
two suggestions for further reading would be to look at "monads
with a zero and plus" (which extend the basic notion with two
extra primitives that are supported by some monads), and "monad
transformers" (which provide a means to combine monads.) For
example, see sections 3 and 7 of the following article, which
concerns the monadic nature of [functional parsers][3]
For a more in-depth exploration of the IO monad, see Simon Peyton
Jones' excellent article on the ["awkward squad"][2]
[1]: http://en.wikipedia.org/wiki/Gofer_(software) "Gofer Language"
[2]: http://research.microsoft.com/Users/simonpj/papers/marktoberdorf/ "Awkward Squad"
[3]: http://www.cs.nott.ac.uk/~gmh/monparsing.pdf "Functional Parsers"
[4]: http://book.realworldhaskell.org/read/monads.html#monads.do
[5]: http://donsbot.wordpress.com/2007/03/10/practical-haskell-shell-scripting-with-error-handling-and-privilege-separation/