0% found this document useful (0 votes)
64 views43 pages

Monads

Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
64 views43 pages

Monads

Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 43

Monads

Stephen A. Edwards

Columbia University

Fall 2022
Motivating Example: lookup3
The Monad Type Class
The Maybe Monad
do Blocks
The Either Monad
Monad Laws
The List Monad
List Comprehensions as a Monad
The MonadPlus Type Class and guard
The Writer Monad
Some Monadic Functions: liftM, ap, join, filterM, foldM, mapM, sequence
Functions as Monads
The State Monad
An Interpreter for a Simple Imperative Language
Motivating Example: Chasing References in a Dictionary
In Data.Map, lookup :: Ord k => k -> Map k a -> Maybe a

Say we want a function that uses a key to look up a value, then treat that
value as another key to look up a third key, which we look up and return, e.g.,
lookup3 :: Ord k => k -> Map.Map k k -> Maybe k

Prelude> import qualified Data.Map.Strict as Map


Prelude Map> myMap = Map.fromList [("One","Two"),("Two","Three"),
Prelude Map| ("Three","Winner")]
Prelude Map> Map.lookup "One" myMap
Just "Two"
Prelude Map> Map.lookup "Two" myMap
Just "Three"
Prelude Map> Map.lookup "Three" myMap
Just "Winner"
A First Attempt
lookup3 :: Ord k => k -> Map.Map k k -> Maybe k −− First try
lookup3 k1 m = case Map.lookup k1 m of
Nothing -> Nothing
Just k2 -> case Map.lookup k2 m of
Nothing -> Nothing
Just k3 -> Map.lookup k3 m

Too much repeated code, but it works.

*Main Map> lookup3 "Three" myMap


Nothing
*Main Map> lookup3 "Two" myMap
Nothing
*Main Map> lookup3 "One" myMap
Just "Winner"
What’s the Repeated Pattern Here?
Nothing -> Nothing
Just k2 -> case Map.lookup k2 m of ...

“Pattern match on a Maybe. Nothing returns Nothing, otherwise, strip out


the payload from the Just and use it as an argument to a lookup lookup.”
lookup3 :: Ord k => k -> Map.Map k k -> Maybe k −− Second try
lookup3 k1 m = (helper . helper . helper) (Just k1)
where helper Nothing = Nothing
helper (Just k) = Map.lookup k m

This looks a job for a Functor or Applicative Functor...


class Functor f where
fmap :: (a -> b) -> f a -> f b −− Apply function to data in context
class Functor f => Applicative f where
(<*>) :: f (a -> b) -> f a -> f b −− Apply a function in a context

..but these don’t fit because our steps take a key and return a key in context.
Even Better: An “ifJust” Function
ifJust :: Maybe k -> (k -> Maybe k) -> Maybe k
ifJust Nothing _ = Nothing −− Failure: nothing more to do
ifJust (Just k) f = f k −− Success: pass k to the function

lookup3 :: Ord k => k -> Map.Map k k -> Maybe k


lookup3 k1 m = ifJust (Map.lookup k1 m)
(\k2 -> ifJust (Map.lookup k2 m)
(\k3 -> Map.lookup k3 m))

It’s cleaner to write ifJust as an infix operator:


lookup3 :: Ord k => k -> Map.Map k k -> Maybe k
lookup3 k1 m = Map.lookup k1 m `ifJust`
\k2 -> Map.lookup k2 m `ifJust`
\k3 -> Map.lookup k3 m
The Monad Type Class: It’s All About That Bind

infixl 1 >>=
class Applicative m => Monad m where
(>>=) :: m a -> (a -> m b) -> m b −− “Bind”
return :: a -> m a −− Wrap a result in the Monad

Bind, >>=, is the operator missing from the Functor and Applicative Functor
type classes. It allows chaining context-producing functions
pure :: b -> f b −− Put value in context
fmap :: (a -> b) -> f a -> f b −− Apply function in context
(<*>) :: f (a -> b) -> f a -> f b −− Function itself is in context
">>=" :: (a -> f b) -> f a -> f b −− Apply a context-producing func.
Actually, Monad is a little bigger
infixl 1 >> >>=
class Monad m where
−− The bind operator: apply the result in a Monad to a Monad producer
(>>=) :: m a -> (a -> m b) -> m b

−− Encapsulate a value in the Monad


return :: a -> m a

−− Like >>= but discard the result; often m () -> m b -> m b


(>>) :: m a -> m b -> m b
x >> y = x >>= \_ -> y −− The default, which usually suffices

−− Internal: added by the compiler to handle failed pattern matches


fail :: String -> m a
fail msg = error msg
Maybe is a Monad

class Monad m where


return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
fail :: String -> m a

instance Monad Maybe where −− Standard Prelude defintion


return x = Just x −− Wrap in a Just

Just x >>= f = f x −− Our “ifjust” function


Nothing >>= _ = Nothing −− “computation failed”

fail _ = Nothing −− fail quietly


The Maybe Monad in Action
Prelude> :t return "what?"
return "what?" :: Monad m => m [Char]

Prelude> return "what?" :: Maybe String


Just "what?"

Prelude> Just 9 >>= \x -> return (x*10)


Just 90

Prelude> Just 9 >>= \x -> return (x*10) >>= \y -> return (y+5)
Just 95

Prelude> Just 9 >>= \x -> Nothing >>= \y -> return (x+5)


Nothing

Prelude> Just 9 >> return 8 >>= \y -> return (y*10)


Just 80

Prelude> Just 9 >>= \_ -> fail "darn" >>= \x -> return (x*10)
Nothing
lookup3 using Monads
instance Monad Maybe where
return x = Just x

Just x >>= f = f x −− Apply f to last (successful) result


Nothing >>= _ = Nothing −− Give up

lookup3 :: Ord k => k -> Map.Map k k -> Maybe k


lookup3 k1 m = Map.lookup k1 m >>=
\k2 -> Map.lookup k2 m >>=
\k3 -> Map.lookup k3 m

Or, equivalently,
lookup3 :: Ord k => k -> Map.Map k k -> Maybe k
lookup3 k1 m = Map.lookup k1 m >>= \k2 ->
Map.lookup k2 m >>= \k3 ->
Map.lookup k3 m
Monads and the do Keyword: Not Just For I/O

Monads are so useful, Haskell provides do notation to code them succintly:


lookup3 :: Ord k => lookup3 :: Ord k =>
k -> Map.Map k k -> Maybe k k -> Map.Map k k -> Maybe k
lookup3 k1 m = do lookup3 k1 m =
k2 <- Map.lookup k1 m Map.lookup k1 m >>= \k2 ->
k3 <- Map.lookup k2 m Map.lookup k2 m >>= \k3 ->
Map.lookup k3 m Map.lookup k3 m

These are semantically identical. do inserts the >>=’s and lambdas.


Note: each lambda’s argument moves to the left of the expression
k2 <- Map.lookup k1 m Map.lookup k1 m >>= \k2 ->
Like an Applicative Functor
Prelude> (+) <$> Just 5 <*> Just 3
Just 8
Prelude> do fail is called when a pattern match fails
Prelude| x <- Just (5 :: Int) Prelude> do
Prelude| y <- return 3 Prelude| (x:xs) <- Just "Hello"
Prelude| return (x + y) Prelude| return x
Just 8 Just 'H'
Prelude> :t it Prelude> :t it
it :: Maybe Int it :: Maybe Char
The Monad’s type may change;
Prelude> do
“Nothing” halts and forces Maybe
Prelude| (x:xs) <- Just []
Prelude> do
Prelude| return x
Prelude| x <- return 5
Nothing
Prelude| y <- return "ha!"
Prelude| Nothing
Prelude| return x
Nothing
Like Maybe, Either is a Monad

data Either a b = Left a | Right b −− Data.Either

instance Monad (Either e) where


return x = Right x

Right x >>= f = f x −− Right: keep the computation going


Left err >>= _ = Left err −− Left: something went wrong

Prelude> do Prelude> do
Prelude| x <- Right "Hello" Prelude| Right "Hello"
Prelude| y <- return " World" Prelude| x <- Left "failed"
Prelude| return $ x ++ y Prelude| y <- Right $ x ++ "darn"
Right "Hello World" Prelude| return y
Left "failed"
Monad Laws

Left identity: applying a wrapped argument with >>= just applies the function
return x >>= f = f x

Right identity: using >>= to unwrap then return to wrap does nothing
m >>= return = m

Associative: applying g after applying f is like applying f composed with g


(m >>= f) >>= g = m >>= (\x -> f x >>= g)
The List Monad: “Nondeterministic Computation”
Intuition: lists represent all possible results
instance Monad [] where
return x = [x] −− Exactly one result
xs >>= f = concat (map f xs) −− Collect all possible results from f
fail _ = [] −− Error: “no possible result”

Prelude> [10,20,30] >>= \x -> [x-3, x, x+3]


[7,10,13,17,20,23,27,30,33]

“If we start with 10, 20, or 30, then either subtract 3, do nothing, or add 3, we
will get 7 or 10 or 13 or 17 or ..., or 33”
[10,20,30] >>= \x -> [x-3, x, x+3]
= concat (map (\x -> [x-3, x, x+3]) [10,20,30])
= concat [[7,10,13],[17,20,23],[27,30,33]]
= [7,10,13,17,20,23,27,30,33]
The List Monad

Everything needs to produce a list, but the lists may be of different types:
Prelude> [1,2] >>= \x -> ['a','b'] >>= \c -> [(x,c)]
[(1,'a'),(1,'b'),(2,'a'),(2,'b')]

This works because -> is at a lower level of precedence than >>=


[1,2] >>= \x -> ['a','b'] >>= \c -> [(x,c)]
= [1,2] >>= (\x -> (['a','b'] >>= (\c -> [(x,c)]) ))
= [1,2] >>= (\x -> (concat (map (\c -> [(x,c)]) ['a','b'])))
= [1,2] >>= (\x -> [(x,'a'),(x,'b')] )
= concat (map (\x -> [(x,'a'),(x,'b')] ) [1,2])
= concat [[(1,'a'),(1,'b')],[(2,'a'),(2,'b')]]
= [(1,'a'),(1,'b'),(2,'a'),(2,'b')]
The List Monad, do Notation, and List Comprehensions
[1,2] >>= \x -> ['a','b'] >>= \c -> return (x,c)

[1,2] >>= \x ->


['a','b'] >>= \c ->
return (x,c)

do x <- [1,2] −− Send 1 and 2 to the function that takes x and


c <- ['a','b'] −− sends ’a’ and ’b’ to the function that takes c and
return (x, c) −− wraps the pair (x, c)

[ (x,c) | x <- [1,2], c <- ['a','b'] ]

each produce

[(1,'a'),(1,'b'),(2,'a'),(2,'b')]
class Monad m => MonadPlus m where −− In Control.Monad
mzero :: m a −− “Fail,” like Monoid’s mempty
mplus :: m a -> m a -> m a −− “Alternative,” like Monoid’s mappend

instance MonadPlus [] where


mzero = []
mplus = (++)

guard :: MonadPlus m => Bool -> m ()


guard True = return () −− In whatever Monad you’re using
guard False = mzero −− “Empty” value in the Monad

Prelude Control.Monad> guard True :: [()]


[()]
Prelude Control.Monad> guard False :: [()]
[]
Prelude Control.Monad> guard True :: Maybe ()
Just ()
Prelude Control.Monad> guard False :: Maybe ()
Nothing
Using Control.Monad.guard as a filter
guard uses mzero to terminate a MonadPlus computation (e.g., Maybe, [], IO)

It either succeeds and returns () or fails. We never care about (), so use >>
[1..50] >>= \x ->
guard (x `rem` 7 == 0) >> −− Discard any returned ()
return x

do x <- [1..50]
guard (x `rem` 7 == 0) −− No <- makes for an implicit >>
return x

[ x | x <- [1..50], x `rem` 7 == 0 ]

each produce
[7,14,21,28,35,42,49]
The Control.Monad.Writer Monad

For computations that return a value and accumulate a result in a Monoid,


e.g., logging or code generation. Just a wrapper around a (value, log) pair
In Control.Monad.Writer,
newtype Writer w a = Writer { runWriter :: (a, w) }

instance Monoid w => Monad (Writer w) where


return x = Writer (x, mempty) −− Append nothing
Writer (x, l) >>= f = let Writer (y, l') = f x in
Writer (y, l `mappend` l') −− Append to log

a is the result value w is the accumulating log Monoid (e.g., a list)

runWriter extracts the (value, log) pair from a Writer computation


The Writer Monad in Action

import Control.Monad.Writer

logEx :: Int -> Writer [String] Int −− Type of log, result


logEx a = do
tell ["logEx " ++ show a] −− Just log
b <- return 42 −− No log
tell ["b = " ++ show a]
c <- writer (a + b + 10, ["compute c"]) −− Value and log
tell ["c = " ++ show c]
return c

*Main> runWriter (logEx 100)


(152,["logEx 100","b = 100","compute c","c = 152"])
Verbose GCD with the Writer
*Main> mapM_ putStrLn $ snd $ runWriter $ logGCD 9 3
logGCD 9 3
a > b
logGCD 6 9
a < b import Control.Monad.Writer
logGCD 6 3
a > b logGCD :: Int -> Int -> Writer [String] Int
logGCD 3 6 logGCD a b = do
a < b tell ["logGCD " ++ show a ++ " " ++ show b]
logGCD 3 3 if a == b then writer (a, ["finished"])
finished else if a < b then do
tell ["a < b"]
logGCD a (b - a)
else do
tell ["a > b"]
logGCD (a - b) a
Control.Monad.{liftM, ap}: Monads as Functors
fmap :: Functor f => (a -> b) -> f a -> f b −− a.k.a. <$>
(<*>) :: Applicative f => f (a -> b) -> f a -> f b −− “apply”

In Monad-land, these have alternative names


liftM :: Monad m => (a -> b) -> m a -> m b
ap :: Monad m => m (a -> b) -> m a -> m b

and can be implemented with >>= (or, equivalently, do notation)


liftM f m = do x <- m −− Get the argument from inside m
return (f x) −− Apply the argument to the function

ap mf m = do f <- mf −− Get the function from inside mf


x <- m −− Get the argument from inside m
return (f x) −− Apply the argument to the function

Operations in a do block are ordered: ap evaluates its arguments left-to-right


liftM and ap In Action
liftM :: Monad m => (a -> b) -> m a -> m b
ap :: Monad m => m (a -> b) -> m a -> m b

Prelude> import Control.Monad


Prelude Control.Monad> liftM (map Data.Char.toUpper) getLine
hello
"HELLO"

Evaluate (+10) 42, but keep a log:


Prelude> :set prompt "> "
> :set prompt-cont "| "
> import Control.Monad.Writer
> :{
| runWriter $
| ap (writer ((+10), ["first"])) (writer (42, ["second"]))
| :}
(52,["first","second"])
Lots of Lifting: Applying two- and three-argument functions
In Control.Applicative, applying a normal function to Applicative arguments:
liftA2 ::
Applicative f => (a -> b -> c) -> f a -> f b -> f c
liftA3 ::
Applicative f => (a -> b -> c -> d) -> f a -> f b -> f c -> f d
In Control.Monad,
liftM2 :: Monad m => (a -> b -> c) -> m a -> m b -> m c
liftM3 :: Monad m => (a -> b -> c -> d) -> m a -> m b -> m c -> m d

Example: lift the pairing operator (,) to the Maybe Monad:


Prelude Control.Monad> liftM2 (,) (Just 'a') (Just 'b')
Just ('a','b')
Prelude Control.Monad> liftM2 (,) Nothing (Just 'b')
Nothing
join: Unwrapping a Wrapped Monad/Combining Objects
join :: Monad m => m (m a) -> m a −− in Control.Monad
join mm = do m <- mm −− Remove the outer Monad; get the inner one
m −− Pass it back verbatim (i.e., without wrapping it)

join is boring on a Monad like Maybe, where it


merely strips off a “Just”
Prelude Control.Monad> join (Just (Just 3))
Just 3

For Monads that hold multiple objects, join lives up


to its name and performs some sort of concatenation
> join ["Hello", " Monadic", " World!"]
"Hello Monadic World!"

join (liftM f m) is the same as m >>= f

“Apply f to every object in m and collect the results in the same Monad”
sequence: “Execute” a List of Actions in Monad-Land

Change a list of Monad-wrapped objects into a Monad-wrapped list of objects


sequence :: [m a] -> m [a]
sequence_ :: [m a] -> m ()

Prelude> sequence [print 1, print 2, print 3]


1
2
3
[(),(),()]
Prelude> sequence_ [putStrLn "Hello", putStrLn "World"]
Hello
World

Works more generally on Traversable types, not just lists


mapM: Map Over a List in Monad-Land
mapM :: Monad m => (a -> m b) -> [a] -> m [b]
mapM_ :: Monad m => (a -> m b) -> [a] -> m () −− Discard result

Add 10 to each list element and log having seen it:


> p10 x = writer (x+10, ["saw " ++ show x]) :: Writer [String] Int
> runWriter $ mapM p10 [1..3]
([11,12,13],["saw 1","saw 2","saw 3"])

Printing the elements of a list is my favorite use of mapM_:


> mapM_ print ([1..3] :: [Int])
1
2
3

Works more generally on Traversable types, not just lists


Control.Monad.foldM: Left-Fold a List in Monad-Land
foldl :: (a -> b -> a) -> a -> [b] -> a

In foldM, the folding function operates and returns a result in a Monad:


foldM :: Monad m => (a -> b -> m a) -> a -> [b] -> m a

foldM f a1 [x1, x2, ..., xm] = do a2 <- f a1 x1


a3 <- f a2 x2
...
f am xm

Example: Sum a list of numbers and report progress


> runWriter $ foldM (\a x -> writer (a+x, [(x,a)])) 0 [1..4]
(10,[(1,0),(2,1),(3,3),(4,6)])

“Add value x to accumulated result a; log x and a”


\a x -> writer (a+x, [(x,a)])
Control.Monad.filterM: Filter a List in Monad-land
filter :: (a -> Bool) -> [a] -> [a]
filter p = foldr (\x acc -> if p x then x : acc else acc) []

filterM :: Monad m => (a -> m Bool) -> [a] -> m [a]


filterM p = foldr (\x -> liftM2 (\k -> if k then (x:)
else id) (p x)) (return [])

filterM in action: preserve small list elements; log progress


isSmall :: Int -> Writer [String] Bool
isSmall x | x < 4 = writer (True, ["keep " ++ show x])
| otherwise = writer (False, ["reject " ++ show x])

> fst $ runWriter $ filterM isSmall [9,1,5,2,10,3]


[1,2,3]
> snd $ runWriter $ filterM isSmall [9,1,5,2,10,3]
["reject 9","keep 1","reject 5","keep 2","reject 10","keep 3"]
An Aside: Computing the Powerset of a List

For a list [x1 , x2 , . . .], the answer consists of two kinds of lists:
£ ¤
[x1 , x2 , . . .], . . . , [x1 ], [x2 , x3 , . . .], . . . , []
| {z } | {z }
start with x1 do not start with x1

powerset :: [a] -> [[a]]


powerset [] = [[]] −− Tricky base case: 2; = {;}
powerset (x:xs) = map (x:) (powerset xs) ++ powerset xs

*Main> powerset "abc"


["abc","ab","ac","a","bc","b","c",""]
The List Monad and Powersets
powerset (x:xs) = map (x:) (powerset xs) ++ powerset xs

Let’s perform this step (i.e., possibly prepending x and combining) using the
list Monad. Recall liftM2 applies Monadic arguments to a two-input function:
liftM2 :: Monad m => (a -> b -> c) -> m a -> m b -> m c

So, for example, if a = Bool, b & c = [Char], and m is a list,


listM2 :: (Bool -> [Char] -> [Char]) -> [Bool] -> [[Char]] ->
[[Char]]

> liftM2 (\k -> if k then ('a':) else id) [True, False] ["bc", "d"]
["abc","ad","bc","d"]

liftM2 makes the function “nondeterministic” by applying the function with


every Bool in the first argument, i.e., both k = True (include ’a’) and k = False
(do not include ’a’), to every string in the second argument (["bc","d"])
filterM Computes a Powerset: Like a Haiku, but shorter
foldr f z [x1,x2,..,xn] = f x1 (f x2 ( ... (f xn z) ... ))

filterM p = foldr (\x -> liftM2 (\k -> if k then (x:)


else id) (p x)) (return [])
filterM p [x1,x2,..xn] =
liftM2 (\k -> if k then (x1:) else id) (p x1)
(liftM2 (\k -> if k then (x2:) else id) (p x2)
..
(liftM2 (\k -> if k then (xn:) else id) (p xn) (return [])) ..)

If we let p _ = [True, False], this chooses to prepend x1 or not to the result


of prepending x2 or not to ... to return [] = [[]]

Prelude> filterM (\_ -> [True, False]) "abc"


["abc","ab","ac","a","bc","b","c",""]
Functions as Monads
Much like functions are applicative functors, functions are Monads that apply
the same argument argument to all their constituent functions
instance Monad ((->) r) where
return x = \_ -> x −− Just produce x
h >>= f = \w -> f (h w) w −− Apply w to h and f

import Data.Char

isIDChar :: Char -> Bool −− ((->) Char) is the Monad


isIDChar = do
l <- isLetter −− The Char argument
n <- isDigit −− is applied to
underscore <- (=='_') −− all three of these functions
return $ l || n || underscore −− before their results are ORed

*Main> map isIDChar "12 aB_"


[True,True,False,True,True,True]
The State Monad: Modeling Computations with Side-Effects
The Writer Monad can only add to a state, not observe it. The State Monad
addresses this by passing a state to each operation. In Control.Monad.State,
newtype State s a = State { runState :: s -> (a, s) }

instance Monad (State s) where


return x = State $ \s -> (x, s)
State h >>= f = State $ \s -> let (a, s') = h s −− First step
State g = f a −− Pass result
in g s' −− Second step

get = State $ \s -> (s, s) −− Make the state the result


put s = State $ \_ -> ((), s) −− Set the state
modify f = State $ \s -> ((), f s) −− Apply a state update function

State is not a state; it more resembles a state machine’s next state function
a is the return value s is actually a state
Example: An Interpreter for a Simple Imperative Language
import qualified Data.Map as Map
type Store = Map.Map String Int −− Value of each variable

−− Representation of a program (an AST)


data Expr = Lit Int −− Numeric literal: 42
| Add Expr Expr −− Addition: 1 + 3
| Var String −− Variable reference: a
| Asn String Expr −− Variable assignment: a = 3 + 1
| Seq [Expr] −− Sequence of expressions: a = 3; b = 4;

p :: Expr −− Example program:


p = Seq [ Asn "a" (Lit 3) −− a = 3;
, Asn "b" (Add (Var "a") (Lit 1)) −− b = a + 1;
, Add (Add (Var "a") bpp) −− a + (b = b + 1) + b;
(Var "b") ]
where bpp = Asn "b" (Add (Var "b") (Lit 1))
Example: The Eval Function Taking a Store
eval :: Expr -> Store -> (Int, Store)
eval (Lit n) s = (n, s) −− Store unchanged
eval (Add e1 e2) s = let (n1, s') = eval e1 s
(n2, s'') = eval e2 s'−− Sees eval e1
in (n1 + n2, s'') −− Sees eval e2
eval (Var v) s =
case Map.lookup v s of −− Look up v
Just n -> (n, s)
Nothing -> error $ v ++ " undefined"
eval (Asn v e) s = let (n, s') = eval e s
in (n, Map.insert v n s') −− Sees eval e

eval (Seq es) s = foldl (\(_, ss) e -> eval e ss) (0, s) es

The fussy part here is “threading” the state through the computations
Example: The Eval Function in Uncurried Form
eval :: Expr -> (Store -> (Int, Store))
eval (Lit n) = \s -> (n, s) −− Store unchanged
eval (Add e1 e2) = \s -> let (n1, s') = eval e1 s
(n2, s'') = eval e2 s'−− Sees eval e1
in (n1 + n2, s'') −− Sees eval e2
eval (Var v) = \s -> −− Get the store
case Map.lookup v s of −− Look up v
Just n -> (n, s)
Nothing -> error $ v ++ " undefined"
eval (Asn v e) = \s -> let (n, s') = eval e s
in (n, Map.insert v n s') −− Sees eval e

eval (Seq es) = \s -> foldl (\(_, ss) e -> eval e ss) (0, s) es

The parentheses around Store -> (Int, Store) are unnecessary


Example: The Eval Function Using the State Monad
eval :: Expr -> State Store Int
eval (Lit n) = return n −− Store unchanged
eval (Add e1 e2) = do n1 <- eval e1
n2 <- eval e2 −− Sees eval e1
return $ n1 + n2 −− Sees eval e2
eval (Var v) = do s <- get −− Get the store
case Map.lookup v s of −− Look up v
Just n -> return n
Nothing -> error $ v ++ " undefined"
eval (Asn v e) = do n <- eval e
modify $ Map.insert v n −− Sees eval e
return n −− Assigned value
eval (Seq es) = foldM (\_ e -> eval e) 0 es −− Ignore value

The >>= operator threads the state through the computation


The Eval Function in Action: runState, evalState, and execState
a = 3;
b = a + 1;
a + (b = b + 1) + b

*Main> :t runState (eval p) Map.empty


runState (eval p) Map.empty :: (Int, Store) −− (Result, State)

*Main> :t evalState (eval p) Map.empty


evalState (eval p) Map.empty :: Int −− Result only
*Main> evalState (eval p) Map.empty
13

*Main> :t execState (eval p) Map.empty


execState (eval p) Map.empty :: Store −− State only
*Main> Map.toList $ execState (eval p) Map.empty
[("a",3),("b",5)]
Harnessing Monads
data Tree a = Leaf a | Branch (Tree a) (Tree a) deriving Show

A function that works in a Monad can harness any Monad:


mapTreeM :: Monad m => (a -> m b) -> Tree a -> m (Tree b)
mapTreeM f (Leaf x) = do x' <- f x
return $ Leaf x'
mapTreeM f (Branch l r) = do l' <- mapTreeM f l
r' <- mapTreeM f r
return $ Branch l' r'

toList :: Tree a -> [a]


toList t = execWriter $ mapTreeM (\x -> tell [x]) t −− Log each leaf

foldTree :: (a -> b -> b) -> b -> Tree a -> b


foldTree f s0 t = execState (mapTreeM (\x -> modify (f x)) t) s0

sumTree :: Num a => Tree a -> a


sumTree t = foldTree (+) 0 t −− Accumulate values using stateful fold
Harnessing Monads
*Main> simpleTree = Branch (Leaf (1 :: Int)) (Leaf 2)
*Main> toList simpleTree
[1,2]
*Main> sumTree simpleTree
3
*Main> mapTreeM (\x -> Just (x + 10)) simpleTree
Just (Branch (Leaf 11) (Leaf 12))
*Main> mapTreeM print simpleTree
1
2
*Main> mapTreeM (\x -> [x, x+10]) simpleTree
[Branch (Leaf 1) (Leaf 2),
Branch (Leaf 1) (Leaf 12),
Branch (Leaf 11) (Leaf 2),
Branch (Leaf 11) (Leaf 12)]

You might also like