liveBook · Manning

IORef

As explained in Functional Design and Architecture: Examples in Haskell

IORef is a Haskell mutable reference type that provides an IO-based interface for reading and updating a stored value.

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

How Functional Design and Architecture: Examples in Haskell covers it

How deep it goes
Explains reads, writes, strict updates, interpreter threading, and concurrency hazards; defers broader state and testing discussion.
Examples
Uses a mocked hardware call counter, sandwich ingredient stock, and a meteor-reporting channel.
What it assumes
Assumes familiarity with IO-based code while introducing IORef as a regular mutable variable.
What you can do after
Evaluate/understand when mutable IORef state fits testing or interpretation and when concurrent access requires STM TVars.
How it connects
Connects IORef with service handles, free monads, interpreters, bare threads, STM, and TVars.

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.