liveBook · Manning

Monad stack

As explained in Functional Design and Architecture: Examples in Haskell

A monad stack is a composition of nested monads that presents their combined effects through one monadic interface.

A monad stack combines nested monads so one computation can use several effects through a single monadic interface. Monad transformers make the composition: for example, StateT Board IO carries mutable board state while retaining IO actions. The result is itself a monad, so ordinary do-notation still organizes the workflow. section 2.1.2 section 7.2.3

The practical cost is that each layer owns its own operations. Code at the top layer acts directly on its context; reaching an underlying layer requires lift, and reaching IO beneath two state layers requires lift twice. This makes effect boundaries explicit, but long stacks can become awkward enough that a single combined state is often clearer. section 7.2.3

Minesweeper state and console output

Define BoardMonad a = StateT Board IO a. A reveal-cell action gets and updates the Board directly, while lift (putStrLn ...) logs attempted moves and outcomes. Adding StateT Score above BoardMonad separates score from board state: get reads score, lift get reads the board, and lift (lift ...) performs console IO. Tests run each StateT layer in turn, supplying initial score and board values. section 7.2.3

Stacks also mark application boundaries. A Servant handler can be ReaderT Runtime (ExceptT ServerError IO): ReaderT supplies runtime needed to run a business scenario, ExceptT handles server errors, and IO performs impure work. Multiple lifts cross these layers, while domain exceptions can be converted to HTTP errors at the handler boundary. section 14.2.2 section 14.3

Figure 14.8 Monad stack for Servant handlers

Inspect the handler stack to see Runtime access, server-error handling, and IO arranged as distinct layers. section 14.2.2

How Functional Design and Architecture: Examples in Haskell covers it

How deep it goes
Explains transformer nesting, lifting, double lifting, sequential execution, and testing, while noting that long stacks may reduce clarity.
Examples
Uses a Minesweeper board and score, spaceship control scripting, and Servant handlers that run business scenarios.
What it assumes
Assumes familiarity with monads and do-notation, then introduces transformers as a way to combine otherwise separate effects.
What you can do after
Build it from scratch by nesting StateT with IO, lifting lower-layer actions, and running layers sequentially in tests.
How it connects
Connects monad transformers with StateT, IO, ReaderT, ExceptT, do-notation, Servant, and Free monads.

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.