Search code examples
haskellstate-monad

Haskell State Monad


Does the put function of the State Monad update the actual state or does it just return a new state with the new value? My question is, can the State Monad be used like a "global variable" in an imperative setting? And does put modify the "global variable"?

My understanding was NO it does NOT modify the initial state, but using the monadic interface we can just pass around the new states b/w computations, leaving the initial state "intact". Is this correct? if not, please correct me.


Solution

  • The answer is in the types.

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

    Thus, a state is essentially a function that takes one parameter, 's' (which we call the state), and returns a tuple (value, state). The monad is implemented like below

    instance Monad (State s) where
      return a = State $ \s -> (a,s)
      (State f) >>= h = State $ \s -> let (a,s') =  f s
                                      in (runState h a) s'
    

    Thus, you have a function that operates on the initial state and spits out a value-state tuple to be processed by the next function in the composition.

    Now, put is the following function.

    put newState = State $ \s -> ((),newState)
    

    This essentially sets the state that will be passed to the next function in the composition and the downstream function will see the modified state.

    In fact, the State monad is completely pure (that is, nothing is being set); only what is passed downstream changes. In other words, the State monad saves you the trouble of carrying around a state explicitly in a pure language like Haskell. In other words, State monad just provides an interface that hides the details of state threading (that's what is called in the WikiBooks and or Learn you a Haskell, I think).

    The following shows this in action. You have get, which sets the value field to be the same as the state field (Note that, when I mean setting, I mean the output, not a variable). put gets the state via the value passed to it, increments it and sets the state with this new value.

    -- execState :: State s a -> s -> s
    let x =  get >>= \x -> put (x+10)
    execState x 10
    

    The above outputs 20.

    Now, let's do the following.

    execState (x >> x) 10
    

    This will give an output of 30. The first x sets the state to 20 via the put. This is now used by the second x. The get at this point sets the state passed it to the value field, which is now 20. Now, our put will get this value, increment it by 10 and set this as the new state.

    Thus, you have states in a pure context. Hope this helps.