Monad
| The bind operator | |
| Maybe as a monad | |
| The three laws | |
| What a monad is not |
The bind operator
A monad is a functor with a way to chain a step that itself returns a wrapped value. Without that, nesting grows one level per step:
class Applicative m => Monad m where
(>>=) :: m a -> (a -> m b) -> m b
Just 2 >>= \n -> Just (n + 1) -- Just 3
Nothing >>= \n -> Just (n + 1) -- Nothing
The shape m a -> (a -> m b) -> m b is the whole idea: take a value out of the context, feed it to a function that puts a new value back, and keep one layer instead of two.
Maybe as a monad
Three lookups in a row would need three nested cases. Bind flattens them and stops at the first failure:
find :: String -> [(String, Int)] -> Maybe Int
find = lookup
total = find "a" prices >>= \pa ->
find "b" prices >>= \pb ->
pure (pa + pb)
A missing "a" short-circuits everything after it. The types stay flat: the result is Maybe Int, not Maybe (Maybe Int).
The three laws
As with functors, the compiler does not check these. Every instance in the standard library obeys them, and yours must too:
-- left identity
pure a >>= f == f a
-- right identity
m >>= pure == m
-- associativity
(m >>= f) >>= g == m >>= (\x -> f x >>= g)
The laws say that chaining groups however you like, which is what lets the compiler and the reader reorder a long chain without changing its meaning.
What a monad is not
A monad is not a way to escape purity and it is not a design pattern for state. It is an interface with three methods and three laws, and Maybe, lists, functions, and IO all happen to implement it:
-- the same operator, three different behaviours
Just 1 >>= \x -> Just (x + 1) -- Just 2
[1,2] >>= \x -> [x, x * 10] -- [1,10,2,20]
getLine >>= \s -> putStrLn s -- reads, then prints
The list instance runs the rest of the chain once per element, which
is exactly what a comprehension does. Lesson 32 rewrites these
examples in the notation designed for them.
Next:
do notation
Article author: Arthur Isaev