Learn You a Haskell for Great Good! - Miran Lipovaca [144]
push a xs = ((), a:xs)
We used () as the result when pushing to the stack because pushing an item onto the stack doesn’t have any important result value—its main job is to change the stack. If we apply only the first parameter of push, we get a stateful computation. pop is already a stateful computation because of its type.
Let’s write a small piece of code to simulate a stack using these functions. We’ll take a stack, push 3 to it, and then pop two items, just for kicks. Here it is:
stackManip :: Stack -> (Int, Stack)
stackManip stack = let
((), newStack1) = push 3 stack
(a , newStack2) = pop newStack1
in pop newStack2
We take a stack, and then we do push 1 stack, which results in a tuple. The first part of the tuple is a (), and the second is a new stack, which we call newStack1. Then we pop a number from newStack1, which results in a number a (which is the 3) that we pushed and a new stack, which we call newStack2. Then we pop a number off newStack2, and we get a number that’s b and a newStack3. We return a tuple with that number and that stack. Let’s try it out:
ghci> stackManip [5,8,2,1]
(5,[8,2,1])
The result is 5, and the new stack is [8,2,1]. Notice how stackManip is itself a stateful computation. We’ve taken a bunch of stateful computations and sort of glued them together. Hmm, sounds familiar.
The preceding code for stackManip is kind of tedious, since we’re manually giving the state to every stateful computation and storing it and then giving it to the next one. Wouldn’t it be cooler if, instead of giving the stack manually to each function, we could write something like the following?
stackManip = do
push 3
a <- pop
pop
Well, using the State monad will allow us to do exactly that. With it, we will be able to take stateful computations like these and use them without needing to manage the state manually.
The State Monad
The Control.Monad.State module provides a newtype that wraps stateful computations. Here’s its definition:
newtype State s a = State { runState :: s -> (a, s) }
A State s a is a stateful computation that manipulates a state of type s and has a result of type a.
Much like Control.Monad.Writer, Control.Monad.State doesn’t export its value constructor. If you want to take a stateful computation and wrap it in the State newtype, use the state function, which does the same thing that the State constructor would do.
Now that you’ve seen what stateful computations are about and how they can even be thought of as values with contexts, let’s check out their Monad instance:
instance Monad (State s) where
return x = State $ \s -> (x, s)
(State h) >>= f = State $ \s -> let (a, newState) = h s
(State g) = f a
in g newState
Our aim with return is to take a value and make a stateful computation that always has that value as its result. That’s why we just make a lambda \s -> (x, s). We always present x as the result of the stateful computation, and the state is kept unchanged, because return must put a value in a minimal context. So return will make a stateful computation that presents a certain value as the result and keeps the state unchanged.
What about >>=? Well, the result of feeding a stateful computation to a function with >>= must be a stateful computation, right? So, we start of with the State newtype wrapper, and then we type out a lambda. This lambda will be our new stateful computation. But what goes on in it? Well, we need to somehow extract the result value from the first stateful computation. Because we’re in a stateful computation right now, we can give the stateful computation h our current state s, which results in a pair of the result and a new state: (a, newState).
So far, every time we implemented >>=, once we had extracted just the result from the monadic value, we applied the function f to it to get the new monadic value. In Writer, after doing that and getting the new monadic value, we still need to make sure that the context is taken care of by mappending the old monoid value with the new one. Here, we do f a, and we get