liveBook · Manning

State monad

As explained in Functional Design and Architecture: Examples in Haskell

A state monad is a monadic abstraction that threads changeable state through computations while presenting state transitions as composable values.

A state monad organizes pure stateful work without passing an updated model through every function call. It is useful when a deep application model—such as a spaceship’s mass, coordinates, orientation, and controllers—would otherwise produce repetitive record updates, especially alongside error-handling effects. section 7.2.1 section 7.3

Within a State computation, get reads the current state and put replaces it with a new value. Functions share an apparent mutable context in do-notation, but no shared memory is mutated: the monad internally wraps argument-passing state, and bind carries each resulting state into the next computation. section 7.2.2 section 7.3

An age script

An Age state can begin at 0, run gettingOlder twice, read the final age, and return messages. Each invocation sees the result of the preceding put, so the script reads imperatively while remaining a pure computation that can be run with an initial state and checked in a test. section 7.2.2

import Control.Monad.State (State, get, put)             #1
  
type Age = Int
  
gettingOlder :: State Age String                         #2
gettingOlder = do
  age <- get                                             #3
  let newAge = age + 1
  put newAge                                             #4
  pure ("You're now older for 1 year: " <> show newAge)

This compact script shows the core pattern: read Age with get, calculate a replacement value, store it with put, and return a normal result. section 7.2.2

StateT extends the pattern when stateful logic must coexist with another monad. A Minesweeper board can live in StateT Board IO: revealCell reads and replaces the board with get and put, while lift adapts console IO into the surrounding stateful computation. section 7.2.3

When using StateT stacks, keep these operational distinctions clear:

  • Lift an action when it belongs to an inner monad rather than the current StateT layer; deeper stacks require repeated lifting. section 7.2.3
  • Treat each StateT layer as a distinct state context, such as score above board; a single combined state may be clearer and simpler when separate layers add no value. section 7.2.3
  • Run a state layer with runState, execState, or evalState according to whether both result and state, only state, or only result are needed; use corresponding StateT runners for transformer layers. section 7.2.3

Stateful domain scripts and tests

A ship-control script can move SpaceshipModel from an explicit function argument into StateT SpaceshipModel LogicControl, leaving operations such as rotation to read and update contextual model state while lifting existing control actions. A board script can likewise be run from an initial board and assert the resulting board in a test. section 7.2.3 section 7.2.3

The pattern is for pure state that feels imperative but remains immutable. It complements rather than eliminates impure state: IORefs, MVars, vectors, and STM may be preferable when performance or convenience matters, provided mutable effects remain easy to control safely. section 7.3

How Functional Design and Architecture: Examples in Haskell covers it

How deep it goes
Explains get, put, bind-based state threading, runners, and StateT stacks, while deliberately deferring category theory.
Examples
Uses Age updates, a Minesweeper board, state-stack tests, and spaceship rotation control scripts.
What it assumes
Assumes prior familiarity with IO and free monads, but does not require category theory or formal monad knowledge.
What you can do after
Apply it via State and StateT to structure, run, and test pure stateful computations with layered effects.
How it connects
Connects state monads with argument-passing state, monad transformers, IO, free monads, STM, and immutable state.

Also explained in

Compare how 2 books explain state monad →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Functional Design and Architecture: Examples in Haskell. The section links point to the parts of the book it draws on.