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.
|