I am trying to get a grasp on Haskell using the online book Learn you a Haskell for great Good.
I have, to my knowledge, been able to understand Monads so far until I hi
Short answer:
State
is meant to exploit monads' features in order to simulate an imperative-like system state with local variables. The basic idea is to hide within the monad the activity of taking in the current state and returning the new state together with an intermediate result at each step (and here we have s -> (a,s)
.State
. The former may have whatever type you want (provided that they eventually produce some State a
if you want to use them in the state monad). The latter holds functions of type s -> (a,s)
: this is the state-passing layer managed by the monad.State
is actually produced by means of (>>=)
and return
as they're defined for the Monad (State s)
instance. Its role is to pass down the state through the calls of your code.Point 3 also is the reason why the state parameter disappears from the functions actually used in the state monad.
Long answer:
The State Monad has been studied in different papers, and exists also in the Haskell framework (I don't remember the good references right now, I'll add them asap).
This is the idea that it follows: consider a type data MyState = ...
whose values holds the current state of the system.
If you want to pass it down through a bunch of functions, you should write every function in such a way that it takes at least the current state as a parameter and returns you a pair with its result (depending on the state and the other input parameters) and the new (possibly modified) state. Well, this is exactly what the type of the state monad tells you: s -> (a, s)
. In our example, s
is MyState
and is meant to pass down the state of the system.
The function wrapped in the State
does not take parameters except from the current state, which is needed to produce as a result the new state and an intermediate result. The functions with more parameters that you saw in the examples are not a problem, because when you use them in the do
-notation within the monad, you'll apply them to all the "extra" needed parameters, meaning that each of them will result in a partially applied function whose unique remaining parameter is the state; the monad instance for State
will do the rest.
If you look at the type of the functions (actually, within monads they are usually called actions) that may be used in the monad, you'll see that they result type is boxed within the monad: this is the point that tells you that once you give them all the parameters, they will actually do not return you the result, but (in this case) a function s -> (a,s)
that will fit within the monad's composition laws.
The computation will be executed by passing to the whole block/composition the first/initial state of the system.
Finally, functions that do not take parameters will have a type like State a
where a
is their return type: if you have a look at the value constructor for State
, you'll see again that this is actually a function s -> (a,s)
.
The State
monad is essentially
type State s a = s -> (a,s)
a function from one state (s
) to a pair of the desired result (a
) and a new state. The implementation makes the threading of the state implicit and handles the state-passing and updating for you, so there's no risk of accidentally passing the wrong state to the next function.
Thus a function that takes k > 0
arguments, one of which is a state and returns a pair of something and a new state, in the State s
monad becomes a function taking k-1
arguments and returning a monadic action (which basically is a function taking one argument, the state, here).
In the non-State setting, pop
takes one argument, the stack, which is the state. So in the monadic setting, pop
becomes a State Stack Int
action taking no explicit argument.
Using the State
monad instead of explicit state-passing makes for cleaner code with fewer opportunities for error, that's what the State
monad accomplishes. Everything could be done without it, it would just be more cumbersome and error-prone.
I'm total newbie to Haskell and I couldn't understand well the State Monad code in that book, too. But let me add my answer here to help someone in the future.
Composing functions which handle stateful computation.
e.g. push 3 >>= \_ -> push 5 >>= \_ -> pop
pop
takes no parameters, while it is suggested function f
takes 1 parameter ?pop
takes no arguments because it is wrapped by State
.
unwapped function which type is s -> (a, s)
takes one argument. the
same goes for push
.
you can unwrapp with runState
.
runState pop :: Stack -> (Int, Stack) runState (push 3) :: Stack -> ((), Stack)
if you mean the right-hand side of the >>=
by the "function f
", the f
will be like \a -> pop
or \a -> push 3
, not just pop
.
These 3 things helped me to understand State Monad and the Stack example a little more.
>>=
)The definition of the bind operator in Monad typeclass is this
(>>=) :: (Monad m) => m a -> (a -> m b) -> m b
In the Stack example, m
is State Stack
.
If we mentaly replace m
with State Stack
, the definition can be like this.
(>>=) :: State Stack a -> (a -> State Stack b) -> State Stack b
Therefore, the type of left side argument for the bind operator will be State Stack a
.
And that of right side will be a -> State Stack b
.
Here is the example code using do notation in the book.
stackManip :: State Stack Int stackManip = do push 3 pop pop
it can be translated to the following code with bind operator.
stackManip :: State Stack Int stackManip = push 3 >>= \_ -> pop >>= \_ -> pop
Now we can see what will be the right-hand side for the bind operator.
Their types are a -> State Stack b
.
(\_ -> pop) :: a -> State Stack Int (\_ -> push 3) :: a -> State Stack ()
(State s)
and (State h)
in the instance declarationHere is the instance declaration for State in the book.
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
Considering the types with the Stack example, the type of (State s)
will be
(State s) :: State Stack s :: Stack
And the type of (State h)
will be
(State h) :: State Stack a h :: Stack -> (a, Stack)
(State h)
is the left-hand side argument of the bind operator and its type is State Stack a
as described above.
Then why h
becomes Stack -> (a, Stack)
?
It is the result of pattern matching against the State value constructor which is defined in the newtype wrapper. The same goes for the (State g)
.
newtype State s a = State { runState :: s -> (a,s) }
In general, type of h
is s ->(a, s)
, representation of the stateful computation. Any of followings could be the h
in the Stack example.
runState pop :: Stack -> (Int, Stack) runState (push 3) :: Stack -> ((), Stack) runState stackManip :: Stack -> (Int, Stack)
that's it.
The State
monad represents stateful computations i.e. computations that use values from, and perhaps modify, some external state. When you sequence stateful computations together, the later computations might give different results depending on how the previous computations modified the state.
Since functions in Haskell must be pure (i.e. have no side effects) we simulate the effect of external state by demanding that every function takes an additional parameter representing the current state of the world, and returns an additional value, representing the modified state. In effect, the external state is threaded through a sequence of computations, as in this abomination of a diagram that I just drew in MSPaint:
Notice how each box (representing a computation) has one input and two outputs.
If you look at the Monad
instance for State
you see that the definition of (>>=)
tells you how to do this threading. It says that to bind a stateful computation c0
to a function f
that takes results of a stateful computation and returns another stateful computation, we do the following:
c0
using the initial state s0
to get a result and a new state: (val, s1)
val
to the function f
to get a new stateful computation, c1
c1
with the modified state s1
How does this work with functions that already take n
arguments? Because every function in Haskell is curried by default, we simply tack an extra argument (for the state) onto the end, and instead of the normal return value, the function now returns a pair whose second element is the newly modified state. So instead of
f :: a -> b
we now have
f :: a -> s -> (b, s)
You might choose to think of as
f :: a -> ( s -> (b, s) )
which is the same thing in Haskell (since function composition is right associative) which reads "f
is a function which takes an argument of type a
and returns a stateful computation". And that's really all there is to the State
monad.