Monads

Monad is a special class which looks like this:

file: mon.hs
1 -- basic Monad instance! yeah!
2 data Moo a = Cow a deriving(Eq,Ord,Show)
3 
4 --uncow :: Moo Float -> Float
5 uncow (Cow a) = a 
6 
7 instance Monad Moo where
8     return m = Cow m
9     f >>= k = k (uncow f)
10 
11 plustwo x = Cow (x+2)
12 minusone x = Cow (x-1)
13 square x = Cow (x*x)
14 
15 main = putStrLn(show(Cow 3 >>= plustwo >>= minusone >>= square))
> ghc --make mon.hs; ./mon
[1 of 1] Compiling Main             ( mon.hs, mon.o )
Linking mon ...
Cow 16

Basically, the >>= is a special operator which takes a data type (in this case "Moo a"), obtains the value supplied to its constructor (in this case "a") and a function (we have three examples here, plustwo, minusone, square) and constructs a new type based on that value.

It turns out that this is the magic that makes "do" work.

file: mon2.hs
1 data Moo a = Cow a deriving(Eq,Ord,Show)
2 
3 --uncow :: Moo Float -> Float
4 uncow (Cow a) = a 
5 
6 instance Monad Moo where
7     return m = Cow m
8     f >>= k = k (uncow f)
9 
10 plustwo x = Cow (x+2)
11 minusone x = Cow (x-1)
12 square x = Cow (x*x)
13 
14 main = putStrLn(show(do
15     x <- Cow 3 
16     x2 <- plustwo x
17     x3 <- minusone x2
18     x4 <- square x3
19     return x4))
> ghc --make mon2.hs; ./mon2
[1 of 1] Compiling Main             ( mon2.hs, mon2.o )
Linking mon2 ...
Cow 16

The <- keyword is a special thing that extracts the constructor from our base type. Each function we apply creates a new type of "Moo."

Now let's see that done with IO:

file: mon3.hs
1 main = (putStrLn "line 1") >>= 
2     \x -> (putStrLn "line 2") >>=
3     \x -> (putStrLn "line 3")
> ghc --make mon3.hs; ./mon3
[1 of 1] Compiling Main             ( mon3.hs, mon3.o )
Linking mon3 ...
line 1
line 2
line 3

Here's another variant of the same program:

file: mon4.hs
1 main = do
2     x <- (putStrLn "line 1")
3     x2 <- (putStrLn "line 2") 
4     x3 <- (putStrLn "line 3")
5     return x3
> ghc --make mon4.hs; ./mon4
[1 of 1] Compiling Main             ( mon4.hs, mon4.o )
Linking mon4 ...
line 1
line 2
line 3

The sequence of statements in a "do" block translate to a sequence of applications of the >>= function. This, essentially, creates the artificial data dependency that the compiler needs in order to apply our putStrLn calls in sequence.

The Monad has other uses than IO, however. Here we create a sort of exception system.

file: divz.hs
1 data DValt a = DVal a | Fail String
2     deriving(Eq, Ord, Show)
3 
4 instance Monad DValt where
5     return a = DVal a
6     (DVal k) >>= f = f k
7     (Fail s) >>= f = Fail s
8 
9 invert x =
10     if x == 0.0 then
11         Fail "Div by zero"
12     else
13         DVal (1.0/x)
14 
15 main = putStrLn(show(
16     (DVal 3.4) >>=
17     \x -> (DVal (x-3.4)) >>=
18     invert >>=
19     \x -> (DVal (x*x))
20     ))
> ghc --make divz.hs; ./divz
[1 of 1] Compiling Main             ( divz.hs, divz.o )
Linking divz ...
Fail "Div by zero"

Once a "Fail" shows up in the sequence of operations, that is all that can show up.