Reading — step 1 of 4
Learn
~2 min readBeyond Functor
The State monad threads a state value through a sequence of operations — pure code that LOOKS like it has mutable state.
import Control.Monad.State
-- State Int Int = a function: Int -> (Int, Int)
-- (the new state, the result)
counter :: State Int Int
counter = do
n <- get -- read state
put (n + 1) -- write state
return n -- yield current value
-- Use:
runState counter 0 -- (0, 1) — first value 0, new state 1
runState (do
a <- counter
b <- counter
c <- counter
return [a, b, c]
) 0
-- ([0, 1, 2], 3) — three counts, ending state 3
Key operations:
get :: State s s— read current stateput :: s -> State s ()— replace statemodify :: (s -> s) -> State s ()— apply functiongets :: (s -> a) -> State s a— read transformedrunState :: State s a -> s -> (a, s)— extractevalState :: State s a -> s -> a— just the resultexecState :: State s a -> s -> s— just the final state
Real example — labeling a tree:
data Tree a = Leaf | Node (Tree a) a (Tree a)
label :: Tree a -> Tree (Int, a)
label t = evalState (label' t) 0
where
label' Leaf = return Leaf
label' (Node l x r) = do
l' <- label' l
n <- get
put (n + 1)
r' <- label' r
return (Node l' (n, x) r')
Cleaner than threading the counter manually through every recursive call.
State is StateT Identity — the State monad transformer over Identity. In real code you usually use StateT s IO (state + IO) or StateT s Maybe.
Why useful in pure code:
- Tracking unique IDs across a recursive computation
- Building up a result while consuming an iterator
- Algorithms that need a running register (parser state, evaluator)
Performance note: pure State copies state on every modification. For high-throughput use STRef (in the ST monad) or IORef (in IO).
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…