1. The Easy Part
  2. Higher Order Functions
  3. Types
  4. Lists
  5. Complex Data Objects
  6. File IO
  7. Classes
  8. Monads
  9. List Monads
  10. Shared Transactional Memory

Monads

Monad is a special class which looks like this:

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

mon2.hs
1data Moo a = Cow a deriving(Eq,Ord,Show)
2 
3--uncow :: Moo Float -> Float
4uncow (Cow a) = a 
5 
6instance Monad Moo where
7    return m = Cow m
8    f >>= k = k (uncow f)
9 
10plustwo x = Cow (x+2)
11minusone x = Cow (x-1)
12square x = Cow (x*x)
13 
14main = 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
[1 of 1] Compiling Main             ( mon2.hs, mon2.o )
Linking mon2 ...
$ ./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:

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

Here's another variant of the same program:

mon4.hs
1main = do
2    x <- (putStrLn "line 1")
3    x2 <- (putStrLn "line 2") 
4    x3 <- (putStrLn "line 3")
5    return x3
$ ghc --make mon4.hs
[1 of 1] Compiling Main             ( mon4.hs, mon4.o )
Linking mon4 ...
$ ./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.

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

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