Haskell State Monad

download Haskell State Monad

of 21

Transcript of Haskell State Monad

  • 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_Handling
  • 7/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-monad
  • 7/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-monad
  • 7/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-monad
  • 7/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'