Question

I'm trying to understand what happens in the following code, the code behaves properly, but I'm trying to understand why.

import Control.Monad.State
import System.IO
import System.Environment

echoArgs :: [String] -> State Int [String]
echoArgs x = loopArgs x >> return x
      where loopArgs [] = return () 
            loopArgs s@(x':xs') = modify (+1) >> loopArgs xs'

main :: IO () 
main = do 
    argv <- getArgs
    let s = echoArgs argv
    mapM_ putStr' (evalState s 0) 
    putStrLn $ "\nNum Args = " ++ show (execState s 0) 
    where putStr' x = putStr $ x ++ " "

What I'm not understanding is why the state of the State monad does not get 'reset' with each successive call to loopArgs. Does the state get passed as a variable, with each >> and if so could someone show me how?

Was it helpful?

Solution

Does the state get passed as a variable, with each >> and if so could someone show me how?

It does indeed. It's helpful to look at a toy implementation of the State monad.

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

instance Monad (State s) where
  return a = State $ \s -> (a, s)

  State act >>= k = State $ \s ->
    let (a, s') = act s
    in runState (k a) s'

get :: State s s
get = State $ \s -> (s, s)

put :: s -> State s ()
put s = State $ \_ -> ((), s)

modify :: (s -> s) -> State s ()
modify f = get >>= \x -> put (f x)

When you bind using >>= or >> the accumulated state is threaded through as an argument to the function on the right hand side.

When you run execState or evalState it then just extracts either the resulting value or the state from the resulting tuple.

execState :: State s a -> s -> s
execState act = snd . runState act

evalState :: State s a -> s -> a
evalState act = fst . runState act
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top