Online Book Reader

Home Category

Learn You a Haskell for Great Good! - Miran Lipovaca [145]

By Root 466 0
a new stateful computation g. Now that we have a new stateful computation and a new state (which goes by the name of newState), we just apply that stateful computation g to the newState. The result is a tuple of the final result and final state!

So, with >>=, we kind of glue two stateful computations together. The second computation is hidden inside a function that takes the previous computation’s result. Because pop and push are already stateful computations, it’s easy to wrap them into a State wrapper:

import Control.Monad.State

pop :: State Stack Int

pop = state $ \(x:xs) -> (x, xs)

push :: Int -> State Stack ()

push a = state $ \xs -> ((), a:xs)

Notice how we used the state function to wrap a function into the State newtype instead of using the State value constructor directly.

pop is already a stateful computation, and push takes an Int and returns a stateful computation. Now we can rewrite our previous example of pushing 3 onto the stack and then popping two numbers off, like this:

import Control.Monad.State

stackManip :: State Stack Int

stackManip = do

push 3

a <- pop

pop

See how we’ve glued a push and two pops into one stateful computation? When we unwrap it from its newtype wrapper, we get a function to which we can provide some initial state:

ghci> runState stackManip [5,8,2,1]

(5,[8,2,1])

We didn’t need to bind the second pop to a, because we didn’t use that a at all. So, we could have written it like this:

stackManip :: State Stack Int

stackManip = do

push 3

pop

pop

Pretty cool. But what if we want to do something a little more complicated? Let’s say we want to pop one number off the stack, and if that number is 5, we’ll just push it back on the stack and stop. But if the number isn’t 5, we’ll push 3 and 8 back on instead. Here’s the code:

stackStuff :: State Stack ()

stackStuff = do

a <- pop

if a == 5

then push 5

else do

push 3

push 8

This is quite straightforward. Let’s run it with an initial stack:

ghci> runState stackStuff [9,0,2,1,0]

((),[8,3,0,2,1,0])

Remember that do expressions result in monadic values, and with the State monad, a single do expression is also a stateful function. Because stackManip and stackStuff are ordinary stateful computations, we can glue them together to produce further stateful computations:

moreStack :: State Stack ()

moreStack = do

a <- stackManip

if a == 100

then stackStuff

else return ()

If the result of stackManip on the current stack is 100, we run stackStuff; otherwise, we do nothing. return () just keeps the state as it is and does nothing.

Getting and Setting State


The Control.Monad.State module provides a type class called MonadState, which features two pretty useful functions: get and put. For State, the get function is implemented like this:

get = state $ \s -> (s, s)

It just takes the current state and presents it as the result.

The put function takes some state and makes a stateful function that replaces the current state with it:

put newState = state $ \s -> ((), newState)

So, with these, we can see what the current stack is or we can replace it with a whole other stack, like so:

stackyStack :: State Stack ()

stackyStack = do

stackNow <- get

if stackNow == [1,2,3]

then put [8,3,1]

else put [9,2,1]

We can also use get and put to implement pop and push. Here’s pop:

pop :: State Stack Int

pop = do

(x:xs) <- get

put xs

return x

We use get to get the whole stack, and then we use put to make everything but the top element the new state. Then we use return to present x as the result.

Here’s push implemented with get and put:

push :: Int -> State Stack ()

push x = do

xs <- get

put (x:xs)

We just use get to get the current stack and use put to make the set the new state as our stack, with the element x on top.

It’s worth examining what the type of >>= would be if it worked only for State values:

(>>=) :: State s a -> (a -> State s b) -> State s b

See how the type of the state s stays the same, but the type of the result can change from a to b? This means that we can glue together several stateful

Return Main Page Previous Page Next Page

®Online Book Reader