Prev: The IO monad TOC: Contents Next: The Reader monad

The State monad


Overview

Computation type: Computations which maintain state.
Binding strategy: Binding threads a state parameter through the sequence of bound functions so that the same state value is never used twice, giving the illusion of in-place update.
Useful for: Building computations from sequences of operations that require a shared state.
Zero and plus: None.
Example type: State st a

Motivation

A pure functional language cannot update values in place because it violates referential transparency. A common idiom to simulate such stateful computations is to "thread" a state parameter through a sequence of functions:

data MyType = MT Int Bool Char Int deriving Show

makeRandomValue :: StdGen -> (MyType, StdGen)
makeRandomValue g = let (n,g1) = randomR (1,100) g
                        (b,g2) = random g1
                        (c,g3) = randomR ('a','z') g2 
                        (m,g4) = randomR (-n,n) g3
                    in (MT n b c m, g4)

This approach works, but such code can be error-prone, messy and difficult to maintain. The State monad hides the threading of the state parameter inside the binding operation, simultaneously making the code easier to write, easier to read and easier to modify.

Definition

The definition shown here uses multi-parameter type classes and funDeps, which are not standard Haskell 98. It is not necessary to fully understand these details to make use 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 x) >>= f = State $ \s -> let (v,s') = x s in runState (f v) s' 

Values in the State monad are represented as transition functions from an initial state to a (value,newState) pair and a new type definition is provided to describe this construct: State s a is the type of a value of type a inside the State monad with state of type s.

The type constructor State s is an instance of the Monad class. The return function simply creates a state transition function which sets the value but leaves the state unchanged. The binding operator creates a state transition function that applies its right-hand argument to the value and new state from its left-hand argument.

class MonadState m s | m -> s where 
    get :: m s
    put :: s -> m ()

instance MonadState (State s) s where 
    get   = State $ \s -> (s,s) 
    put s = State $ \_ -> ((),s) 

The MonadState class provides a standard but very simple interface for State monads. The get function retrieves the state by copying it as the value. The put function sets the state of the monad and does not yield a value.

There are many additional functions provide which perform more complex computations built on top of get and put. The most useful one is gets which retrieves a function of the state. The others are listed in the documentation for the State monad library.

Example

A simple application of the State monad is to thread the random generator state through multiple calls to the generation function.

Code available in example15.hs
data MyType = MT Int Bool Char Int deriving Show

{- Using the State monad, we can define a function that returns
   a random value and updates the random generator state at
   the same time.
-}
getAny :: (Random a) => State StdGen a
getAny = do g      <- get
            (x,g') <- return $ random g
            put g'
            return x

-- similar to getAny, but it bounds the random value returned
getOne :: (Random a) => (a,a) -> State StdGen a
getOne bounds = do g      <- get
                   (x,g') <- return $ randomR bounds g
                   put g'
                   return x

{- Using the State monad with StdGen as the state, we can build
   random complex types without manually threading the
   random generator states through the code.
-}   
makeRandomValueST :: StdGen -> (MyType, StdGen)
makeRandomValueST = runState (do n <- getOne (1,100)
                                 b <- getAny
                                 c <- getOne ('a','z')
                                 m <- getOne (-n,n)
                                 return (MT n b c m))

Prev: The IO monad TOC: Contents Next: The Reader monad