Sid-Ant
Sid-Ant

Reputation: 297

How does initial state gets picked up in State Monad?

I was reading LYAHFG and I can't get around understanding state monad.

In the book, state monad is defined as

newtype State s a = State { runState :: s -> (a,s) }  

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  
pop :: State Stack Int  
pop = State $ \(x:xs) -> (x,xs)  

push :: Int -> State Stack ()  
push a = State $ \xs -> ((),a:xs) 

stackManip :: State Stack Int  
stackManip = do  
    push 3  
    a <- pop  
    pop  

runState stackManip [5,8,2,1]  

The book explains this by using a stack, where stack is the state and an element is the result.

My question particularty is how does runState stackManip [5,8,2,1] work?

runState takes one argument, that's fine but stackManip doesn't take any argument. How does initial state [5,8,2,1] gets picked up?

Upvotes: 1

Views: 118

Answers (2)

chepner
chepner

Reputation: 532053

A value of type State is just a (wrapped) function. The Monad instance for State "composes" those functions, rather than actually working directly with state. runState simply "unwraps" the function, giving you something you can directly apply to an initial state.

Another way of looking at it is that runState is an application operator, like ($). It applies a State (i.e., a state transformer) to an initial state.

Let's start with evalState, though, which just produces the result, not the new state.

head      $      [1,2,3] -- 1
pop  `evalState` [1,2,3] -- 1

runState just gives you a tuple consisting of the result and the new state.

pop `runState` [1,2,3] -- (1,[2,3])

Upvotes: 2

Li-yao Xia
Li-yao Xia

Reputation: 33519

runState takes two arguments actually. When you declare a record

newtype State s a = State { runState :: s -> (a, s) }

This defines the function runState with the following signature:

runState :: State s a -> s -> (a, s)
runState (State run) = run

You get the s -> (a, s) type alone when manipulating records explicitly (pattern-matching, construction, update):

pop = State { runState = \(x : s) -> (x, s) }

Upvotes: 6

Related Questions