Short overview of monads
(or, a few different views of monads)
So, what is a monad?
Monads are just burritos
http://chrisdone.com/posts/monads-are-burritos
Monad is an interface
Fits well into modeling some problems
passing some additional context around: IO, (mutable) state,
exceptions, concurrency (async code and futures)
Components
datatype and encapsulation
composable functions
monad laws
Actions
sequential (daisy) chaining
provides do notation
Can model imperative aspects of programming
Can combine pure and unpure code
Main part of the monad definition
Bind
class Monad m where
(>>=) :: m a -> (a -> m b) -> m b
*Pictures taken from very nice pictorial explanations of monads given in this blog post
Maybe/Option
Maybe type
as a monad
wrap a computation inside it
half x = if even x
then Just (x `div` 2)
else Nothing
cannot be applied directly
half $ Just 3
Maybe monad, bind
We need bind
instance Monad Maybe where
Nothing >>= func = Nothing
Just val >>= func = func val
Handles both cases
of Maybe
Maybe, chaining
Successive applications of our computation
> Just 20 >>= half >>= half >>= half
Nothing
Handling unpure code
Haskell is a purely functional language
Mechanism for handling unpure code
side effects
state and mutations
Solution? Encapsulate unpure things
into a monad
use bind to effectively combine it with pure code
Once you have unpure code, you cannot escape
contrast with, e.g. Maybe monad
the concept of escaping a monad
IO monad
Like others, but also special
Provided operations
getLine :: IO String
readFile :: FilePath -> IO String
putStrLn :: String -> IO ()
unit type
getLine >>= readFile >>= putStrLn (eff. no value)
foo = do
filename <- getLine
contents <- readFile filename
putStrLn contents
Benefits of the interface
data Expr = Val Int | Div Expr Expr > eval (Div (Val 4) (Val 2))
2
we want to write
this evaluator
Benefits of the interface
data Expr = Val Int | Div Expr Expr > eval (Div (Val 1) (Val 0))
*** Exception: divide by zero
eval :: Expr -> Int
eval (Val n) = n
eval (Div x y) = eval x div eval y we want to
avoid this error
simple
implementation
Benefits of the interface
data Expr = Val Int | Div Expr Expr > eval (Div (Val 1) (Val 0))
*** Exception: divide by zero
eval :: Expr -> Int
eval (Val n) = n safediv :: Int -> Int -> Maybe Int
eval (Div x y) = eval x div eval y safediv _ 0 = Nothing
safediv n m = Just (n div m)
eval :: Expr -> Maybe Int
eval (Val n) = Just n Our evaluator with a safe
eval (Div x y) = case eval x of division, as a monad
Nothing -> Nothing
Just n -> case eval y of
Nothing -> Nothing
Just m -> safediv n m
> eval (Div (Val 1) (Val 0))
Nothing
Benefits of the interface
data Expr = Val Int | Div Expr Expr > eval (Div (Val 1) (Val 0))
*** Exception: divide by zero
eval :: Expr -> Int
eval (Val n) = n safediv :: Int -> Int -> Maybe Int
eval (Div x y) = eval x div eval y safediv _ 0 = Nothing
safediv n m = Just (n div m)
eval :: Expr -> Maybe Int
eval (Val n) = Just n
eval (Div x y) = case eval x of
Nothing -> Nothing eval :: Expr -> Maybe Int
Just n -> case eval y of eval (Val n) = Just n
Nothing -> Nothing eval (Div x y) =
Just m -> safediv n m do n <- eval x
m <- eval y
> eval (Div (Val 1) (Val 0)) safediv n m
Nothing do notation makes it
a bit more concise
Rest of the interface
Full Interface (>>=) :: forall a b. m a -> (a -> m b) -> m b
(>>) :: forall a b. m a -> m b -> m b
return :: a -> m a
fail :: String -> m a
Pattern matching
might fail
do { action1 x1
; mk_action3 x1 x2 }
do { True <- action1 x1
; x2 <- action2 do { putString(Hello)
; action3 x1 x2 } putString(World)
return () }
Fail is called
action1 x1 >>= f >>
where similar to bind
f True = do { x2 <- action2; mk_action3 x1 x2 } but does not use value
f_ = fail "..." -- A compiler-generated message.
Monad laws
Left identity: return a >>= k =ka
Right identity:
m >>= return =m
which gives us
do { x <- return x; f x } do { f x }
identity law says two are equivalent
Associativity
gives us m >>= (\x -> k x >>= h) = (m >>= k) >>= h
do do
y <- do
x <- m x <- m
equivalent due to
y <- k x <=> k x
h y h y associativity law
Note that developers need to ensure laws hold
Monad types
Writer monad
(similar to the one given in lectures)
Reader monad
State monad
Writer monad
Example of using writer monad
data Writer w a = Writer { runWriter :: (a, w) }
half :: Int -> Writer String Int
half x = do
tell ("I just halved " ++ (show x) ++ "!")
return (x `div` 2)
runWriter $ half 8 >>= half
=> (2, "I just halved 8!I just halved 4!")