# Yet Another Monad Guide

I was tired of seeing all these guides to understanding monads on
the Internet, so I wrote another (mainly because I helped teach a
class on Haskell and needed some materials). There will be no
presents, no monsters, no cookies, but instead some motivating
examples and the abstraction which relates them all: the monad type.
You can also download monads.hs to run it directly in
`ghci`. Then, you can run any of the example functions from
the interpreter (such as `ex2`).

On this page, monads.hs is modified to be more like literate programming and has more explanation. We start with some imports.

import IO -- (<-- we'll talk about this later) import Monad -- (<-- contains a function called 'guard')Before we get into monads, let’s first look at some problems we might want to solve, and see if we can simplify what we’re doing (hint: it’s going to involve discovering monads).

## 1. Problem 1

Let’s say we want to tag data so we know where it came from (the creator), but we also want to apply functions on the data, keeping the provenance intact.We can represent tagged data with the following type.

data TaggedDataType x = TaggedData { creator :: String, innerData :: x } deriving ShowSo, if we want to create tagged data, we invoke the constructor:

ex1 = TaggedData "I made this." (2 + 3)

We can get the data out, too:

ex2 = do putStr "Stuff in ex1." putStr (" creator=" ++ show (creator ex1)) putStrLn (" innerData=" ++ show (innerData ex1))

Now, let’s say we want to make a function which takes an tagged
integer (which is of type `TaggedDataType Integer`) and adds
5 to it, while keeping creator the same. We can do the following:

taggedAddFive :: TaggedDataType Integer -> TaggedDataType Integer taggedAddFive x = TaggedData ((creator x) ++ " Then added 5.") (5 + (innerData x))

For a more complicated example, let’s say we want to make a function which then doubles the tagged value after adding five to it

taggedAddFiveDoub :: TaggedDataType Integer -> TaggedDataType Integer taggedAddFiveDoub x = let x2 = taggedAddFive x in TaggedData ((creator x2) ++ " Then doubled.") (2 * (innerData x2))

We can do better. Let’s make a new function, let’s call it
`bindTagged` because it binds the inside of a
`TaggedDataType` to be an argument to a given function,
keeping the creators intact (it’s like making “pipework”):

bindTagged :: TaggedDataType x -> (x -> TaggedDataType y) -> TaggedDataType y taggedValue `bindTagged` f = let x2 = f (innerData taggedValue) in TaggedData ((creator taggedValue) ++ (creator x2)) (innerData x2)We use back quotes (

```) so we can use

`bindTagged`as an operator, which is just notational convenience.

With this, we can rewrite our previous two functions as

taggedAddFive2 :: TaggedDataType Integer -> TaggedDataType Integer taggedAddFive2 x = x `bindTagged` (\v -> TaggedData " Then added 5." (v + 5)) taggedAddFiveDoub2 x = (taggedAddFive2 x) `bindTagged` (\v -> TaggedData " Then doubled." (v * 2))The benefit is here is that we do not have to explicitly deal with concatenating the creator strings. Not that bad, but we’ll do better later.

## 2. Problem 2

Let’s say we are doing computations, but we might run into an error and need to throw an exception. For example, perhaps we have associated lists of type [(Integer, String)] and we want to search for a string given an integer. We could try the following.

assocSearch :: Integer -> [(Integer, String)] -> String assocSearch key ((i,s):xs) = if key == i then s else assocSearch key xsBut what if key never appears? We could return a sentinel such as the empty string, but perhaps we want to have associated lists which contain the empty string.

We can create a new datatype. Let’s call it Perhaps to reflect the idea of perhaps having a value.

data Perhaps x = Yep x | Nope deriving Showso if we are searching for

`5`, and we find

`(5,"a")`, we return

`Yep "a"`, otherwise

`Nope`.

assocSearch2 :: Integer -> [(Integer, String)] -> Perhaps String assocSearch2 key ((i,s):xs) = if key == i then Yep s else assocSearch2 key xs assocSearch2 _ [] = NopeWe can use this as follows (the argument to

`ex3`being an integer).

ex3 k = let al = [(1,"a"), (2,"b"), (3,"c")] in case (assocSearch2 k al) of Nope -> "Didn't find that key" Yep x -> "Found " ++ x -- ex3 1 ==> "Found a" -- ex3 5 ==> "Didn't find that key"

Let’s say we also have a function which adds “good” to the end of
a string only if it’s not an `a`, otherwise it’s an error
(who knows—perhaps it could be useful).

not_a :: String -> Perhaps String not_a s | s == "a" = Nope | otherwise = Yep (s ++ "good")

And, for whatever reason, we’ve decided it’s necessary to take the
output of `assocSearch2` and run it through `not_a`.

ex4 k = let al = [(1,"a"), (2,"b"), (3,"c")] in case (assocSearch2 k al) of Nope -> "Didn't find that key" Yep x -> case (not_a x) of Nope -> "Boo. It was an a." Yep s -> "Found "++s -- ex4 2 => "Found bgood" -- ex4 1 => "Boo. It was an a."

But, this is still a bit ugly. We have to keep nesting these case
statements! Let’s make a helper function called
`bindPerhaps` which binds the inside value of a
`Perhaps` to a function only if it is not a `Nope`,
otherwise it just passes on `Nope`.

bindPerhaps :: Perhaps x -> (x -> Perhaps y) -> Perhaps y v `bindPerhaps` f = case v of Nope -> Nope Yep x -> f x

Then, we can rewrite `ex4` as `ex5`.

ex5 x = let al = [(1,"a"), (2,"b"), (3,"c")] ret = (assocSearch2 x al) `bindPerhaps` not_a in case ret of Nope -> "Didn't find key, or we got an 'a'!" Yep s -> "Found " ++ s -- ex5 2 => "Found bgood" -- ex5 10 => "Didn't find key, or we got an 'a'!" -- ex5 1 => "Didn't find key, or we got an 'a'!"This lets us essentially compose the functions rather than have nested

`case`blocks.

## 3. Problem 3

Sometimes we want to handle multiple values from a function, such as a square root (the positive and negative roots). One way we can handle this is by returning a list of all possibilities.

multiSqrt x | x > 0 = [ -(sqrt x), sqrt x ] | x == 0 = [ 0 ]

Let’s say we want to pass the output of this to a function
`f`.

ex6 = let f x = 5 + x in map f (multiSqrt 4)But, what if

`f`, too, wants to use the

`multiSqrt`function?

ex7 = let f x = multiSqrt (10 + x) in concat (map f (multiSqrt 4))Again, we can do better. Let’s define

`bindMulti`to take a function and apply it to every element of a list, concatenating the results.

bindMulti :: [x] -> (x -> [y]) -> [y] bindMulti vals f = concat (map f vals)With this we may rewrite

`ex7`as

ex8 = let f x = multiSqrt (10 + x) in (multiSqrt 4) `bindMulti` fSo, we only have to write

``bindMulti``instead of using both

`concat`and

`map`.

## 4. Did somebody say monads?

Let’s look at the type signatures of our bind operators and see if we notice a pattern.

bindTagged :: TaggedDataType x -> (x -> TaggedDataType y) -> TaggedDataType y bindPerhaps :: Perhaps x -> (x -> Perhaps y) -> Perhaps y bindMulti :: [x] -> (x -> [y]) -> [y]They are all of type

`M x -> (x -> M y) -> M y`, where

`M`is some type constructor! (Remember that

`[x]`is shorthand for

`[] x`).

Here’s the secret: a monad is just something which has such a
binding operator with the above type, and one other thing we haven’t
talked directly about which is something which converts a value into
such a monad, hence having type `x -> M x`. We call such an
operator `return`. For example, we could write

returnTagged x = TaggedData "" x returnPerhaps x = Yep x returnMulti x = [x]

Note that `((returnM a) `bindM` f)` is the same as `f a`.

To abstract this behavior, Haskell has a Monad class.

class Monad m where (>>=) ... return ...

So, let’s turn our objects into Monads.

instance Monad TaggedDataType where (>>=) = bindTagged return = returnTagged instance Monad Perhaps where (>>=) = bindPerhaps return = returnPerhaps

In fact, there already is a monad with the functionality of Perhaps, and that’s

data Maybe x = Just x | NothingAnd, it turns out lists are already instances of class Monad with the same implementation we gave for

`bindMulti`and

`returnMulti`.

Haskell provides a nice syntax to aid in writing expressions using monads. Instead of writing

taggedAddFive3 x = x >>= (\v -> TaggedData " Then added 5." (v + 5)) taggedAddFiveDoub3 x = (taggedAddFive3 x) >>= (\v -> TaggedData " Then doubled." (v * 2))which one invokes with an expression like

`taggedAddFiveDoub3 (return 2)`, we can write, as shorthand,

taggedAddFiveDoub4 x = do v <- taggedAddFive3 x TaggedData " Then doubled." (v*2)the last expression in a

`do`statement is the return value, which must be of the same type as the first expression.

Likewise, instead of writing

ex9 x = (assocSearch2 x al) >>= not_a where al = [(1,"a"), (2,"b"), (3,"c")]you could instead choose to name intermediate expressions in a

`do`statement

ex10 x = do search <- assocSearch2 x al ret <- not_a search return ret where al = [(1,"a"), (2,"b"), (3,"c")]

and,

ex11 = let f x = multiSqrt (10 + x) in do v1 <- (multiSqrt 4) f v1

For an awesome example of the list monad letting you deal with multiple values,

pyth_triples = do x <- [1..100] y <- [1..100] z <- [1..1000] guard (x^2 + y^2 == z^2) -- 'guard' requires the Monad module return (x,y,z)For the list monad, one can equivalently use list comprehension notation.

pyth_triples2 = [(x,y,z) | x <- [1..100], y <- [1..100], z <- [1..1000], x^2 + y^2 == z^2]

## 5. IO

One of the things which Monads let us do is order expressions.
Since `IO` has side effects, the order in which `IO`
operations execute needs to be explicitly ordered. The `IO`
monad keeps track of the state of `IO` devices.
Incidentally, IO was the motivation for monads in Haskell in the
first place.

One constraint with using `IO` is that, if a value of type
`X` was calculated when using `IO`, it must in the end
be of type `IO X`. A way to think of this is that, for
example, `IO String` is an IO-derived string. It’s not a
normal string, but an IO string.

Let’s make a function which asks for your name, prints it, and returns it.

getName :: IO String getName = do putStr "What is your name? " x <- getLine putStrLn ("Hello, " ++ x) return x

Then, executing `getName` in `ghci`:

*Main> getName What is your name? Kyle Hello, Kyle "Kyle"The final line is the return value.

`ghci`can handle values of type

`IO String`since all interactions are being done from within the

`IO`monad.

Let’s say you want to make a function which then takes someone’s name, and tells a little story based on the argument. This is an error:

doStory x = (getName) ++ " walked to the store to get some " ++ x ++ "."since getName returns an

`IO String`, not a

`String`. Instead, one would write

doStory x = do name <- getName return $ name ++ " walked to the store to get some " ++ x ++ "."since bind takes the

`IO String`from

`getName`and puts the

`String`in

`name`. Running this,

*Main> doStory "OJ" What is your name? Kyle Hello, Kyle "Kyle walked to the store to get some OJ."note that the type of the function

`doStory`is

`String -> IO String`.

An aside: when dealing with IO on the terminal, the output to stdout
may not have caught up when there’s a request for stdin (due to the
output buffer). One solution is to call `hFlush stdout`.
Otherwise, you may choose to alter the buffering for stdout by
writing `hSetBuffering stdout NoBuffering`. Of course, these
return a value of type `IO ()` (the empty tuple representing
that the function doesn’t have any particular return value).