IORef is a mutable variable with an IO-based interface. It is useful when an interpreter, service mock, or simple application needs to retain and update a value across impure operations. section 4.2.4 section 6.3.5
A reference can hold practical implementation state without placing that state in domain logic. A sandwich interpreter keeps kitchen inventory as IORef Ingredients, then reads, decrements, and writes the stock while interpreting operations. The ingredient map remains an interpretation-layer detail rather than part of the recipe language. section 6.3.5 section 6.3.5
Count calls in a service mock
Pass an IORef Int into a mocked service handle and update it with modifyIORef' each time getDevicePart runs. The counter lets a test observe how often an impure service method was invoked while keeping the mock configurable. section 4.2.4
Listing 6.5 Free interpreter for SandwichRecipe
interpretRecipe :: IORef Ingredients -> SandwichRecipe Sandwich -> IO Sandwich interpretRecipe ingredsRef (Free step) = do #1 next <- interpretStep ingredsRef step #2 interpretRecipe ingredsRef next #3 interpretRecipe ingredsRef (Pure sandwich) = pure sandwich #4
The interpreter receives a single IORef Ingredients and threads it through each recursive interpretation step, separating mutable stock from the free script. section 6.3.5
Do not treat a raw IORef as a safe concurrent channel. A reporting channel built from IORef [Meteor] performs separate read and write actions; concurrent actors can lose reports, duplicate them, or outpace processing. For shared concurrent state, STM TVars provide thread-safe, transactional access. section 9.1.2 section 9.2.3
When preserving architectural boundaries, expose state operations through an application language rather than arbitrary IO:
- Model creation, reads, and writes as language operations such as NewVar, ReadVar, and WriteVar, while interpreters perform the underlying IORef effects. section 9.2.3
- Avoid a general evalIO escape hatch when it would allow unrelated IO actions to enter otherwise abstracted stateful code. section 9.2.3
- Move concurrent reporting from IORef operations to a TVar transaction run atomically when updates must be safe. section 9.2.3
