7/26/2019 Haskell State Monad
1/21
11. State MonadThe use of the Eithermonad helped us simplify error processing in the last tutorial. I promised to
show you how another monad, the state monad, can eliminate explicit symbol-table threading. But
before I do that, let's have a short refresher on currying, since it's relevant to the construction of thestate monad (there is actually some beautiful math behind the relationship of currying and the state
monad.
There are two ways of encoding a two-argument function and, in !as"ell, they are e#uivalent. $ne
is to implement a function that ta"es two values%
fPair :: a -> b -> c
fPair x y = ...
The other is to implement a function that ta"es one argument and returns another function of one
argument (parentheses added for emphasis%
fCurry :: a -> (b -> c)
fCurry x = \y -> ...
This might seem li"e a trivial transformation, but I'll show you how it can help us in coding the
evaluator.
Curried Evaluator
&et me remind you what the signature of the function evaluatewas -- to ma"e things simpler, let'sconsider the version from before the introduction of the Eithermonad%
evaluate :: Tree -> SymTab -> (Double SymTab)
I'm going to parenthesie it the way that highlights the currying interpretation%
evaluate :: Tree -> (SymTab -> (Double SymTab))
&et's read this signature carefully% evaluateis a function that ta"es a Treeand returns a function,
which ta"es a SymTaband returns a pair (Double SymTab). hat if we ta"e this reading to heart
and rewrite evaluateso that it actually returns a function (a lambda.
&et's start with the !"ary#o$eevaluator, which used to loo" li"e this%
evaluate (!"ary#o$e o% tree) &ymTab =
let (x &ymTab') = evaluate tree &ymTab
i" ca&e o% of
Plu& -> ( x &ymTab')
i"u& -> (-x &ymTab')
and let's try something li"e this%
evaluate :: Tree -> (SymTab -> (Double SymTab))
evaluate (!"ary#o$e o% tree) =
https://www.fpcomplete.com/user/bartosz/basics-of-haskell/10_Error_Handlinghttps://www.schoolofhaskell.com/school/starting-with-haskell/basics-of-haskell/12-State-Monad#curried-evaluatorhttps://www.schoolofhaskell.com/school/starting-with-haskell/basics-of-haskell/12-State-Monad#curried-evaluatorhttps://www.fpcomplete.com/user/bartosz/basics-of-haskell/10_Error_Handling7/26/2019 Haskell State Monad
2/21
\&ymTab ->
let (x &ymTab') = -hi-*evaluate-+hi-* tree &ymTab --,,
i" ca&e o% of
Plu& -> ( x &ymTab')
i"u& -> (-x &ymTab')
)ou see what the problem is* In the new scheme, the inner call to evaluatewill no longer return a
pair (x &ymTab')but a function (SymTab -> (Double SymTab)). &et me call this
functionactfor action. !ow can we extract xand &ymTab'from that action* By running it+ e do
have an argument &ymTabto pass to it -- it's the argument of the lambda%
evaluate :: Tree -> (SymTab -> (Double SymTab))
evaluate (!"ary#o$e o% tree) =
\&ymTab ->
let act = evaluate tree
(x &ymTab') = -hi-*act &ymTab-+hi-*
i" ca&e o% of
Plu& -> ( x &ymTab')
i"u& -> (-x &ymTab')
hat have ust happened* e called the new evaluateonly to immediately execute the resulting
action* Then why even bother with the intermediate step*
irst of all, it's a neat idea that evaluation can be separated into two phases% one for creating a
networ" of functions li"e evaluatecalling each other but not actually evaluating the result and
another phase for excecuting this networ", starting with a particular state -- the symbol table in this
case. $bviously, if you provide a different starting symbol table, you will obtain a different final
result. But the networ" of functions depends only on the original parse tree.
The second reason is that this form brings us closer to our goal of abstracting away the tedium of
symbol-table passing. /ymbol table passing is what 0actions0 are supposed to do evaluateshould
only construct the trac"s for the symbol-table train.
Interestingly, this separation between creating an action and running it turned out to be #uite useful
in 122, as I showed in my old post 3onads in 122. There, the actions were constructed at compile
time using an 45/&, and then executed at runtime.
6oing bac" to our program, we'll try follow the same procedure we used to derive
the Eithermonad. The most important part of a monad is the bind function. 7emember, bind is the
glue that binds the output of one function to the input of another function -- the one we call a
continuation. The signature of bind is determined by the definition of the o"a$class. It has the
form%
bi"$ :: lob a
-> (a -> lob b)
-> lob b
http://bartoszmilewski.com/2011/07/11/monads-in-c/http://bartoszmilewski.com/2011/07/11/monads-in-c/7/26/2019 Haskell State Monad
3/21
where lobstands for the type constructor we are trying to monadie. In our case, this type
constructor is of the form (SymTab -> (a SymTab)), with the type parameter anested inside the
return type of an action. I'll call this function type the new Evaluator%
ty%e Evaluator a = SymTab -> (a SymTab)
e'll standardie it later using a "ety%edefinition, which is re#uired by i"&ta"tiate, but for
now let's ust wor" with a type synonym.
/o here's what monadic bind should loo" li"e for our type (yes, it's exactly the same as for
ourEithermonad, except that Evaluatornow hides a function%
bi"$S :: Evaluator a
-> (a -> Evaluator b)
-> Evaluator b
The client of bind is supposed to pass an evaluator as the first argument and a continuation as the
second. The continuation is a function that returns an evaluator. &et's loo" for this pattern in ourimplementation of evaluateof the !"ary#o$e%
evaluate :: Tree -> (SymTab -> (a SymTab))
evaluate (!"ary#o$e o% tree) =
(\&ymTab ->
let act = evaluate tree
(x &ymTab') = act &ymTab
i" ca&e o% of
Plu& -> ( x &ymTab')
i"u& -> (-x &ymTab'))
e are loo"ing for a piece of code that can be interpreted as 0the rest of code.0 $n first attempt we
might thin" of the following lambda as our continuation%
\x' -> ca&e o% of
Plu& -> ( x' &ymTab')
i"u& -> (-x' &ymTab'))
but it's the wrong type. $ur continuation is supposed to be returning an Evaluator, not a
pair(Double SymTab). !ow can we turn this value into an evaluator* That's what
monadic retur"is supposed to do. Its signature, again, is determined by the o"a$class (I'm
calling it retur"Sfor now to avoid name conflicts%
retur"S :: a -> Evaluator a
The implementation is a no-brainer, really. e turn xinto a function that returns this xwith a side
of &ymTab%
retur"S x = \&ymTab -> (x &ymTab)
/o here's the candidate that fulfills all our re#uirements for a continuation%
7/26/2019 Haskell State Monad
4/21
\x' -> ca&e o% of
Plu& -> retur" x'
i"u& -> retur" (-x')
This is indeed a fine monadic function (returning a value of the soon to be monadic Evaluator,
and it fits the type signature of the continuation re#uired by bind except that we don't see it in theoriginal code. e can't carve it out of the current implementation of evaluate. If we could only
find a way to insert this retur"Sand then immediately cancel it. But how can one undoretur"S*
ell, how about exectuting its result* 1hec" this out%
(retur"&S x) &ymTab' = (\&ymTab -> (x &ymTab)) &ymTab'
= (x &ymTab')
hen you execute a lambda, you simply replace it with its body and replace the formal parameter
with the actual argument. !ere, I replaced &ymTab(formal parameter, or boundvariable
with &ymTab'(the argument. In general, the argument may be a whole expression. )ou ust stic"
at every place the formal parameter appears in the body. ()ou have to be careful though not tointroduce name conflicts.
/o here's the final rewrite%
$ata /%erator = Plu& 0 i"u&
$ata Tree = !"ary#o$e /%erator Tree
ty%e SymTab = ()
-- &ho
ty%e Evaluator a = SymTab -> (a SymTab)
retur"S :: a -> Evaluator a
retur"S x = \&ymTab -> (x &ymTab)
evaluate :: Tree -> (SymTab -> (Double SymTab))
evaluate (!"ary#o$e o% tree) =
\&ymTab ->
let act = evaluate tree
(x &ymTab') = act &ymTab
1 = \x' -> ca&e o% of
Plu& -> retur"S x'
i"u& -> retur"S (-x')
act' = 1 x
i"
7/26/2019 Haskell State Monad
5/21
act' &ymTab'
mai" = %utStr2" 34t ty%e chec1&53
If it type chec"s, it must be correct, right* To convince yourself that this indeed wor"s, first
apply 1to x-- this will ust replace x'with x. Then apply the resulting action to &ymTab'to cancelout the retur"Ss.
&et's continue with our program to define a new monad. To this end, we need to identify the
pattern we've been loo"ing for. e want to pic" the implementation of bi"$Sfrom evaluate.
e can clearly see the two arguments to bind% one is act, the result of evaluate tree, and the
other is the continuation 1. The rest must be bind. !ere it is, together with retur"Sand the new
version of evaluate%
$ata /%erator = Plu& 0 i"u&
$ata Tree = !"ary#o$e /%erator Tree
ty%e SymTab = ()
-- &ho
ty%e Evaluator a = SymTab -> (a SymTab)
retur"S :: a -> Evaluator a
retur"S x = \&ymTab -> (x &ymTab)
bi"$S :: Evaluator a
-> (a -> Evaluator b)
-> Evaluator b
bi"$S act 1 =
\&ymTab ->
let (x &ymTab') = act &ymTab
act' = 1 x
i"
act' &ymTab'
evaluate :: Tree -> (SymTab -> (Double SymTab))
evaluate (!"ary#o$e o% tree) =
7/26/2019 Haskell State Monad
6/21
bi"$S (evaluate tree)
(\x -> ca&e o% of
Plu& -> retur"S x
i"u& -> retur"S (-x))
mai" = %utStr2" 34t ty%e chec1&53
Symbol Table Monad
&et's formalie what we've done so far using an actual instance of the o"a$typeclass. irst, we
need to encapsulate our evaluator type in a "ety%edeclaration. This muddles things a little, but is
necessary if we want to use it in an i"&ta"cedeclaration. !ere's a type that contains nothing but a
function%
"ety%e Evaluator a = Ev (SymTab -> (a SymTab))
8nd here are our return and bind functions in their cleaned up form%
i"&ta"ce o"a$ Evaluator here
retur" x = Ev (\&ymTab -> (x &ymTab))
(Ev act) >>= 1 = Ev 6 \&ymTab ->
let (x &ymTab') = act &ymTab
(Ev act') = 1 x
i"
act' &ymTab'
9ow that the paperwor" is done, we can start using the $onotation. !ere's our
monadic!"ary#o$eevaluator%
evaluate (!"ary#o$e o% tree) = $o
x 7- evaluate tree
ca&e o% of
Plu& -> retur" x
i"u& -> retur" (-x)
Sum#o$eis even more spectacular%
evaluate (Sum#o$e o% left ri8ht) = $o
lft 7- evaluate left
r8t 7- evaluate ri8ht
ca&e o% of
Plu& -> retur" (lft 9 r8t)
https://www.schoolofhaskell.com/school/starting-with-haskell/basics-of-haskell/12-State-Monad#symbol-table-monadhttps://www.schoolofhaskell.com/school/starting-with-haskell/basics-of-haskell/12-State-Monad#symbol-table-monad7/26/2019 Haskell State Monad
7/21
i"u& -> retur" (lft - r8t)
1ompare it with the original%
evaluate (Sum#o$e o% left ri8ht) &ymTab =
let (lft &ymTab') = evaluate left &ymTab
(r8t &ymTab'') = evaluate ri8ht &ymTab'
i"
ca&e o% of
Plu& -> (lft 9 r8t &ymTab'')
i"u& -> (lft - r8t &ymTab'')
8ll references to the symbol table are magically gone. The code is not only cleaner, but also less
error prone. In the original code there were way too many opportunities to use the wrong symbol
table for the wrong call. That's all ta"en care of now.
There are only three places where you'll see explicit use of the symbol table% loo1!%, a$$Symbol,
and the main loop -- as it should be+ I recommend studying the complete code for the calculculator
listed at the end of this tutorial, with special attention to those functions.
9ow you have seen with your own eyes that all this can be done with pure functions. e managed
to manipulate state -- the symbol table -- in a purely functional way.
There is a popular misconception that you mustuse impure code to deal with mutable state, and
that !as"ell monads are impure. There are ways to introduce impurities in !as"ell -- there's a
bunch of functions whose names start with unsafeand there is tracefor debugging, the STmonad
(not to be confused with the Statemonad, all of which (carefully let you inect impurity intoyour code. /ometimes it's done for debugging, sometimes for performance. In general, though,
you can and should stic" to the purely functional style.
State Monad
hat we have ust done is to create our own version of a generic state monad. It was, hopefully, a
good learning experience, but one that shouldn't be repeated when writing production code. /o
let's familiarie ourselves with the Co"trol.o"a$.Stateversion of the state monad (strictly
spea"ing the state monad is defined using a monad transformer, so the actual code in the library
may loo" a bit different from what I present. /tate monad is defined by a new typeState, which isparameteried by two type variables. The first one is used to represent the state (in our case that
would be SymTab, and the second is the generic type parameter of every monad type constructor.
"ety%e State & a = State & -> (a &)
Statehas one data constructor also called State. It ta"es a function as an argument. The
interesting thing is that this constructor is not exported from the library so you can't pattern match
on it. If you want to create a new monadic State, use the function &tate%
&tate :: (& -> (a &)) -> State & a
Instead of extracting an action from State, which you can't do, and acting with it on some state,you call the function ru"Statewhich does it for you%
https://www.schoolofhaskell.com/school/starting-with-haskell/basics-of-haskell/12-State-Monad#state-monadhttps://www.schoolofhaskell.com/school/starting-with-haskell/basics-of-haskell/12-State-Monad#state-monad7/26/2019 Haskell State Monad
8/21
ru"State :: State & a -> & -> (a &)
The o"a$instance declaration for Stateloo"s something li"e this%
i"&ta"ce o"a$ (State &) here
retur" x = &tate (\&t -> (x &t))
act >>= 1 = &tate 6 \&t ->
let (x &t') = ru"State act &t
i" ru"State (1 x) &t'
9otice that State &is not a type but a type constructor% it needs one more type variable to become
a type. 8s I mentioned before, o"a$class can only be instantiated with type constructors.
I've shown you how to extract the bind operator from state-threading code, but there is a more
general derivation that's based on types. In !as"ell you often see functions whose implementation
is determined by their signatures. /ometimes it's determined uni#uely, more often we pic" the
simplest non-trivial implementation that type chec"s. !ere's the signature of>>=that is re#uired by
the o"a$class as applied to State &%
(>>=) :: State & a -> (a -> State & b) -> State & b
act >>= 1 = ...
The first observation is that, in order to run the continuation 1, we need a value of type a. The only
source of such value could be the first argument, act, and the only way to retrieve it is to
call actwith some state. But we don't have any state yet.
But notice that bind itself doesn't produce a value -- it produces a Stateobect. !ow do you
construct a State* By calling &tatewith a function. Bind must therefore define a lambda of thesignature & -> (b &)and pass it to &tate. The outer shell of >>=must therefore have the form%
act >> 1 = &tate 6 \&t -> ...
9ow, inside the lambda, we do have access to a state variable &tand we can use it to run act.
act >> 1 = &tate 6 \&t ->
let (x &t') = ru"State act &t
...
9ow we have xof type aso we can call the continuation 1%
act >> 1 = &tate 6 \&t ->
let (x &t') = ru"State act &t
act' = 1 x
...
The continuation returns an action act'of the type State & b. $ur lambda, though, must return a
pair of the type (b &). The only way to generate a value of the type bis to run act'with some
state. !ere we have a choice% we can run it with the original &tor with the new&t'. The firstchoice would mean that the state never changes and, in fact, doesn't even have to be returned by
the action. There is a perfectly good monad built on this assumption% it's called the reader monad
7/26/2019 Haskell State Monad
9/21
(see the exercise at the end of this tutorial. But since here we are modeling mutable state, we
choose to use &t'to run act'%
act >> 1 = &tate 6 \&t ->
let (x &t') = ru"State act &t
act' = 1 x
i" ru"State act' &t'
There is one more ingredient necessary to ma"e the state monad usable% the ability to access and
modify the state. There are two generic functions 8etand %utthat provide this functionality%
8et :: State & &
8et = &tate 6 \&t -> (&t &t)
%ut :: & -> State & ()
%ut "eState = &tate 6 \ -> (() "eState)
8etreturns the value of the state. %utreturns unit, but has a 0side effect0 of inecting new state into
subse#uent computations.
What Is a Monad?
e've seen two seemingly disparate examples of a monad and I will show you some more in the
next tutorial. hat do they have in common, other than implementing thefunctions retur"and >>=* hy are these two functions so important* It's time for some deeper
insights.
The basic premise of all programming is that you can decompose a complex computation into a set
of simpler ones.
The difference between various programming paradigms is in the mechanics of composing smaller
computations into larger ones. or instance, in 1 you use a combination of functions and side
effects. )ou call a function (procedure whose effects can be%
1. Returning a value
2. Modifying an argument (when it's a reference)
3. Modifying global variables
4. nteracting with the e!ternal world
/ome of the effects are visible in the signature of the function (types of input and output
parameters, others are implicit. The compiler may help with flagging explicit mismatches, but it
can't chec" the implicit ones. /o when you're composing functions in 1, you have to "eep in mind
all the hidden interactions between them.
In $$ programming, side effects are somewhat tamed with data hiding. 8lthough arguments are
mostly passed by reference, including the implicit thi&pointer, the things you can do to them arerestricted by their interfaces. /till, hidden dependencies ma"e composition fragile. This is
especially painful when dealing with concurrency.
https://www.schoolofhaskell.com/school/starting-with-haskell/basics-of-haskell/12-State-Monad#what-is-a-monad-https://www.schoolofhaskell.com/school/starting-with-haskell/basics-of-haskell/12-State-Monad#what-is-a-monad-7/26/2019 Haskell State Monad
10/21
The starting point of functional programming is that functions have no side effects whatsoever, so
function composition is a straightforward matter of passing the results of one function as the input
to the next. This is a great starting point from the point of composability. !owever, many of the
traditional notions of computation don't have straightforward translations into pure functions. This
has been a huge problem in the adoption of functional languages.
Two things happened (not necessarily in that order to change this situation%
1. "e learned how to translate most com#utations into functions.
2. "e use monads to abstract the tedium of this translation.
I tried to emphasie the same two steps when introducing monads.
irst, I showed you how to translatepartial computationsinto total functions. These functions
encapsulate their results into aybeor Eithertypes. I also showed you how to deal with mutable
state by passing it as an additional parameter into and out of a function.
This is a very general pattern% Ta"e a computation that ta"es input and produces output but does it
in a non-functional way, and modify input and output types in such a way that the computationbecomes functional.
9ext, I showed you a way to do the same thing by modifying only the return types of the
computation. If the translation of a computation re#uired adding input parametersto the original
signature (passing the symbol table in, for instance, I used currying and turned the output type into
a function type. (In 4xercise : you'll use the same tric" used to implement the reader monad.
/o this is lesson one% 8 computation can be turned into a function that encapsulates the originally
non-functional bits into its modified (decorated, fancified, or whatever you call it output data type.
The great thing about it is that now all this additional information is visible to the compiler and the
type chec"er. There is even a name for this system in type theory% the effect system. 8 function
signature may expose the effectsof a function in addition to ust turning input into output types.
These effects are propagated when composing functions (as was the effect of modifying the symbol
table, or being undefined for some values of arguments and can be chec"ed at compile time.
8 potential shortcoming of this approach is that the composition of such fancified functions
re#uires writing some boilerplated code. In the case of aybe- or Either-returning functions, we
have to pattern match the results and for" the execution. In case of action-returning functions, we
need to run these actions, provide the additional parameters they need, and pass results to the next
action.
To our great relief, this highly repetitive (and error-prone glue code can be abstracted into ust two
functions% >>=and retur"(optionally >>and fail. 9ow we can test our implementation of theglue code in one place, or still better, use the library code. 8nd to ma"e our lives even better, we
have this wonderful syntactic sugar in the shape of the $onotation.
But now, when you loo" at a do bloc", it loo"s very much li"e imperative code with hidden side
effects. The Eithermonadic code loo"s li"e using functions that can throw
exceptions. Statemonad code loo"s as if the state were a global mutable variable. )ou access it
using 8etwith no arguments, and you modify it by calling %utthat returns no value. /o what have
we gained in comparison to 1*
Wemight not see the hidden effects, but the compiler does. It desugars every do bloc" and type-
chec"s it. The state might loo" li"e a global variable but it's not. 3onadic bind ma"es sure that the
state is threaded from function to function. It's never shared. If you ma"e your !as"ell code
concurrent, there will be no data races.
7/26/2019 Haskell State Monad
11/21
Exercises
Ex 1.5efine the reader monad. It's supposed to model computations that have access to some read-
only environment. In imperative code such environment is often implemented as a global obect. In
functional languages we need to pass it as an argument to every function that might potentially need
access to it. The reader monad hides this process."ety%e ;ea$er e a = ;ea$er (e -> a)
rea$er :: (e -> a) -> ;ea$er e a
rea$er f = u"$efi"e$
ru";ea$er :: ;ea$er e a -> e -> a
ru";ea$er = u"$efi"e$
a&1 :: ;ea$er e e
a&1 = rea$er (\e -> e)
i"&ta"ce o"a$ (;ea$er e) here
...
ty%e E"v = ;ea$er Stri"8
-- currie$ ver&io" of
-- ty%e E"v a = ;ea$er Stri"8 a
te&t :: E"v 4"t
te&t = $o
& 7- a&1
retur" 6 rea$ & 9 e -> a
ru";ea$er (;ea$er act) e"v = act e"v
a&1 :: ;ea$er e e
a&1 = rea$er (\e -> e)
i"&ta"ce o"a$ (;ea$er e) here
retur" x = rea$er (\ -> x)
r$ >>= 1 = rea$er 6 \e"v ->
let x = ru";ea$er r$ e"v
act' = 1 x
i" ru";ea$er act' e"v
ty%e E"v = ;ea$er Stri"8
-- currie$ ver&io" of
-- ty%e E"v a = ;ea$er Stri"8 a
te&t :: E"v 4"t
te&t = $o
& 7- a&1
retur" 6 rea$ & 9 Evaluator Double
loo1!% &tr = $o ...
a$$Symbol :: Stri"8 -> Double -> Evaluator ()
a$$Symbol &tr val = $o ...
evaluate :: Tree -> Evaluator Double
evaluate (Sum#o$e o% left ri8ht) = ...
evaluate (Pro$#o$e o% left ri8ht) = ...
evaluate (!"ary#o$e o% tree) = ...
evaluate (#um#o$e x) = ...
evaluate (@ar#o$e &tr) = ...
evaluate (?&&i8"#o$e &tr tree) = ...
7/26/2019 Haskell State Monad
14/21
ex%r = ?&&i8"#o$e 3x3 (Pro$#o$e Time& (@ar#o$e 3%i3)
(Pro$#o$e Time& (#um#o$e A) (#um#o$e B)))
mai" = %ri"t 6 ru"State (evaluate ex%r) (.from2i&t (3%i3 %i))
im%ort Data.Char
im%ort ualifie$ Data.a% a&
im%ort Co"trol.o"a$.State
$ata /%erator = Plu& 0 i"u& 0 Time& 0 Div
$erivi"8 (Sho E)
$ata Tree = Sum#o$e /%erator Tree Tree
0 Pro$#o$e /%erator Tree Tree
0 ?&&i8"#o$e Stri"8 Tree
0 !"ary#o$e /%erator Tree
0 #um#o$e Double
0 @ar#o$e Stri"8
$erivi"8 Sho
ty%e SymTab = .a% Stri"8 Double
ty%e Evaluator a = State SymTab a
loo1!% :: Stri"8 -> Evaluator Double
loo1!% &tr = $o
&ymTab 7- 8et
ca&e .loo1u% &tr &ymTab of
u&t v -> retur" v
#othi"8 -> error 6 3!"$efi"e$ variable 3 99 &tr
a$$Symbol :: Stri"8 -> Double -> Evaluator ()
a$$Symbol &tr val = $o
7/26/2019 Haskell State Monad
15/21
&ymTab 7- 8et
%ut 6 .i"&ert &tr val &ymTab
retur" ()
evaluate :: Tree -> Evaluator Double
evaluate (Sum#o$e o% left ri8ht) = $o
lft 7- evaluate left
r8t 7- evaluate ri8ht
ca&e o% of
Plu& -> retur" 6 lft 9 r8t
i"u& -> retur" 6 lft - r8t
evaluate (Pro$#o$e o% left ri8ht) = $o
lft 7- evaluate left
r8t 7- evaluate ri8ht
ca&e o% of
Time& -> retur" 6 lft F r8t
Div -> retur" 6 lft + r8t
evaluate (!"ary#o$e o% tree) = $o
x 7- evaluate tree
ca&e o% of
Plu& -> retur" x
i"u& -> retur" (-x)
evaluate (#um#o$e x) = retur" x
evaluate (@ar#o$e &tr) = loo1!% &tr
evaluate (?&&i8"#o$e &tr tree) = $o
v 7- evaluate tree
7/26/2019 Haskell State Monad
16/21
a$$Symbol &tr v
retur" v
ex%r = ?&&i8"#o$e 3x3 (Pro$#o$e Time& (@ar#o$e 3%i3)
(Pro$#o$e Time& (#um#o$e A) (#um#o$e B)))
mai" = %ri"t 6 ru"State (evaluate ex%r) (.from2i&t (3%i3 %i))
Calculator with the Symbol Table Monad
!ere's the complete runnable version of the calculator that uses our /ymbol Table 3onad.
im%ort Data.Char
im%ort ualifie$ Data.a% a&
$ata /%erator = Plu& 0 i"u& 0 Time& 0 Div
$erivi"8 (Sho E)
$ata To1e" = To1/% /%erator
0 To1?&&i8"
0 To12Pare"
0 To1;Pare"
0 To14$e"t Stri"8
0 To1#um Double
0 To1E"$
$erivi"8 (Sho E)
o%erator :: Char -> /%erator
o%erator c 0 c == '9' = Plu&
0 c == '-' = i"u&
0 c == 'F' = Time&
0 c == '+' = Div
to1e"iGe :: Stri"8 -> To1e"
https://www.schoolofhaskell.com/school/starting-with-haskell/basics-of-haskell/12-State-Monad#calculator-with-the-symbol-table-monadhttps://www.schoolofhaskell.com/school/starting-with-haskell/basics-of-haskell/12-State-Monad#calculator-with-the-symbol-table-monad7/26/2019 Haskell State Monad
17/21
to1e"iGe =
to1e"iGe (c : c&)
0 elem c 39-F+3 = To1/% (o%erator c) : to1e"iGe c&
0 c == '=' = To1?&&i8" : to1e"iGe c&
0 c == '(' = To12Pare" : to1e"iGe c&
0 c == ')' = To1;Pare" : to1e"iGe c&
0 i&Di8it c = "umber c c&
0 i&?l%ha c = i$e"tifier c c&
0 i&S%ace c = to1e"iGe c&
0 otheri&e = error 6 3Ca""ot to1e"iGe 3 99 c
i$e"tifier :: Char -> Stri"8 -> To1e"
i$e"tifier c c& = let ("ame c&') = &%a" i&?l%ha#um c& i"
To14$e"t (c:"ame) : to1e"iGe c&'
"umber :: Char -> Stri"8 -> To1e"
"umber c c& =
let ($i8& c&') = &%a" i&Di8it c& i"
To1#um (rea$ (c : $i8&)) : to1e"iGe c&'
---- %ar&er ----
$ata Tree = Sum#o$e /%erator Tree Tree
0 Pro$#o$e /%erator Tree Tree
0 ?&&i8"#o$e Stri"8 Tree
0 !"ary#o$e /%erator Tree
0 #um#o$e Double
0 @ar#o$e Stri"8
$erivi"8 Sho
loo1?hea$ :: To1e" -> To1e"
loo1?hea$ = To1E"$
7/26/2019 Haskell State Monad
18/21
loo1?hea$ (t:t&) = t
acce%t :: To1e" -> To1e"
acce%t = error 3#othi"8 to acce%t3
acce%t (t:t&) = t&
ex%re&&io" :: To1e" -> (Tree To1e")
ex%re&&io" to1& =
let (termTree to1&') = term to1&
i"
ca&e loo1?hea$ to1&' of
(To1/% o%) 0 elem o% Plu& i"u& ->
let (exTree to1&'') = ex%re&&io" (acce%t to1&')
i" (Sum#o$e o% termTree exTree to1&'')
To1?&&i8" ->
ca&e termTree of
@ar#o$e &tr ->
let (exTree to1&'') = ex%re&&io" (acce%t to1&')
i" (?&&i8"#o$e &tr exTree to1&'')
-> error 3/"ly variable& ca" be a&&i8"e$ to3
-> (termTree to1&')
term :: To1e" -> (Tree To1e")
term to1& =
let (facTree to1&') = factor to1&
i"
ca&e loo1?hea$ to1&' of
(To1/% o%) 0 elem o% Time& Div ->
let (termTree to1&'') = term (acce%t to1&')
i" (Pro$#o$e o% facTree termTree to1&'')
-> (facTree to1&')
7/26/2019 Haskell State Monad
19/21
factor :: To1e" -> (Tree To1e")
factor to1& =
ca&e loo1?hea$ to1& of
(To1#um x) -> (#um#o$e x acce%t to1&)
(To14$e"t &tr) -> (@ar#o$e &tr acce%t to1&)
(To1/% o%) 0 elem o% Plu& i"u& ->
let (facTree to1&') = factor (acce%t to1&)
i" (!"ary#o$e o% facTree to1&')
To12Pare" ->
let (ex%Tree to1&') = ex%re&&io" (acce%t to1&)
i"
if loo1?hea$ to1&' += To1;Pare"
the" error 3i&&i"8 ri8ht %are"the&i&3
el&e (ex%Tree acce%t to1&')
-> error 6 3Par&e error o" to1e": 3 99 &ho to1&
%ar&e :: To1e" -> Tree
%ar&e to1& = let (tree to1&') = ex%re&&io" to1&
i"
if "ull to1&'
the" tree
el&e error 6 32eftover to1e"&: 3 99 &ho to1&'
---- evaluator ----
-- &ho
ty%e SymTab = .a% Stri"8 Double
"ety%e Evaluator a = Ev (SymTab -> (a SymTab))
i"&ta"ce o"a$ Evaluator here
(Ev act) >>= 1 = Ev 6
7/26/2019 Haskell State Monad
20/21
\&ymTab ->
let (x &ymTab') = act &ymTab
(Ev act') = 1 x
i" act' &ymTab'
retur" x = Ev (\&ymTab -> (x &ymTab))
loo1!% :: Stri"8 -> Evaluator Double
loo1!% &tr = Ev 6 \&ymTab ->
ca&e .loo1u% &tr &ymTab of
u&t v -> (v &ymTab)
#othi"8 -> error 6 3!"$efi"e$ variable 3 99 &tr
a$$Symbol :: Stri"8 -> Double -> Evaluator Double
a$$Symbol &tr val = Ev 6 \&ymTab ->
let &ymTab' = .i"&ert &tr val &ymTab
i" (val &ymTab')
evaluate :: Tree -> Evaluator Double
evaluate (Sum#o$e o% left ri8ht) = $o
lft 7- evaluate left
r8t 7- evaluate ri8ht
ca&e o% of
Plu& -> retur" 6 lft 9 r8t
i"u& -> retur" 6 lft - r8t
evaluate (Pro$#o$e o% left ri8ht) = $o
lft 7- evaluate left
r8t 7- evaluate ri8ht
ca&e o% of
Time& -> retur" 6 lft F r8t
Div -> retur" 6 lft + r8t
7/26/2019 Haskell State Monad
21/21
evaluate (!"ary#o$e o% tree) = $o
x 7- evaluate tree
ca&e o% of
Plu& -> retur" x
i"u& -> retur" (-x)
evaluate (#um#o$e x) = retur" x
evaluate (@ar#o$e &tr) = loo1!% &tr
evaluate (?&&i8"#o$e &tr tree) = $o
v 7- evaluate tree
a$$Symbol &tr v
mai" = $o
loo% (.from2i&t (3%i3 %i))
loo% &ymTab = $o
&tr 7- 8et2i"e
if "ull &tr
the"
retur" ()
el&e
let to1& = to1e"iGe &tr
tree = %ar&e to1&
Ev act = evaluate tree
(val &ymTab') = act &ymTab
i" $o
%ri"t val
loo% &ymTab'
Top Related