Monads
Monad is a special class which looks like this:
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
[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 |
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
[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 |
1 | main = (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 |
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
[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 |
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
[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.
|