I have a function:
test :: String -> State String String
test x =
get >>= \test ->
let test' = x ++ test in
put test' >>
get >>= \test2 -> put (test2 ++ x) >>
return "test"
I can pretty much understand what goes on throughout this function, and I'm starting to get the hang of monads. What I don't understand is how, when I run this:
runState (test "testy") "testtest"
the 'get' function in 'test' somehow gets the initial state "testtest". Can someone break this down and explain it to me?
I appreciate any responses!
I was originally going to post this as a comment, but decided to expound a bit more.
Strictly speaking, get
doesn't "take" an argument. I think a lot of what is going on is masked by what you aren't seeing--the instance definitions of the State monad.
get
is actually a method of the MonadState class. The State monad is an instance of MonadState, providing the following definition of get
:
get = State $ \s -> (s,s)
In other words, get
just returns a very basic State monad (remembering that a monad can be thought of as a "wrapper" for a computation), where any input s
into the computation will return a pair of s
as the result.
The next thing we need to look at is >>=
, which State defines thusly:
m >>= k = State $ \s -> let
(a, s') = runState m s
in runState (k a) s'
So, >>=
is going to yield a new computation, which won't be computed until it gets an initial state (this is true of all State computations when they're in their "wrapped" form). The result of this new computation is achieved by applying whatever is on the right side of the >>=
to the result of running the computation that was on the left side. (That's a pretty confusing sentence that may require an additional reading or two.)
I've found it quite useful to "desugar" everything that's going on. Doing so takes a lot more typing, but should make the answer to your question (where get
is getting from) very clear. Note that the following should be considered psuedocode...
test x =
State $ \s -> let
(a,s') = runState (State (\s -> (s,s))) s --substituting above defn. of 'get'
in runState (rightSide a) s'
where
rightSide test =
let test' = x ++ test in
State $ \s2 -> let
(a2, s2') = runState (State $ \_ -> ((), test')) s2 -- defn. of 'put'
in runState (rightSide2 a2) s2'
rightSide2 _ =
-- etc...
That should make it obvious that the end result of our function is a new State computation that will need an initial value (s
) to make the rest of the stuff happen. You supplied s
as "testtest"
with your runState
call. If you substitute "testtest" for s
in the above pseudocode, you'll see that the first thing that happens is we run get
with "testtest" as the 'initial state'. This yields ("testtest", "testtest")
and so on.
So that's where get
gets your initial state "testtest". Hope this helps!
It might help you to take a deeper look at what the State
type constructor really is, and how runState uses it. In GHCi:
Prelude Control.Monad.State> :i State
newtype State s a = State {runState :: s -> (a, s)}
Prelude Control.Monad.State> :t runState
runState :: State s a -> s -> (a, s)
State
takes two arguments: the type of the state, and the returned type. It is implemented as a function taking the initial state and yielding a return value and the new state.
runState
takes such a function, the initial input, and (most probably) just applies one to the other to retrieve the (result,state) pair.
Your test
function is a big composition of State
-type functions, each taking a state input and yielding a (result,state) output, plugged in one another in a way that makes sense to your program. All runState
does is provide them with a state starting point.
In this context, get
is simply a function that takes state as an input, and returns a (result,state) output such that the result is the input state, and the state is unchanged (the output state is the input state). In other words, get s = (s, s)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With