A free monad represents domain actions as a pure, declarative program value rather than running them immediately. Build scenarios from a small domain-specific language, then hand the scenario to an interpreter that performs each action. This separates business logic from effects and implementation details. section 6.2.1 section 6.2.2
The pattern has three parts: a domain eDSL that defines available actions, one or more interpreters that implement those actions, and monadic scenarios that express business logic. Wrapping an algebra in the recursive Free type makes actions sequential and composable while retaining a uniform script type. section 6.2.2 section 6.3.4
Use a free monad when domain logic needs to remain independent of execution:
- Describe actions without evaluating them, then choose real, test, sequential, concurrent, or environment-specific interpreters later. section 6.2.3
- Compose dependent actions in familiar monadic notation instead of exposing continuation-passing plumbing in business logic. section 6.2.2 section 6.4.1
- Treat the language as a functional interface: expose domain meaning while hiding subsystem implementation details. section 2.2.3 section 6.1
- Keep subsystems loosely coupled by arranging separate free eDSLs hierarchically and lifting lower-level actions into higher-level languages. section 6.4.2
A recipe as a program
A sandwich recipe can start a sandwich, add cheese and salt, and finish it. Each step returns a value needed by the next, yet the whole recipe remains a SandwichRecipe value. An interpreter can later supply ingredients, maintain state, and produce the sandwich. section 6.3.1 section 7.1.1
Listing 2.6 A possible free monadic scenario
script :: Language () #1 script = do #2 readThermometer "Garage" readThermometer "Near the road" readThermometer "House" readBarometer "Garage" readBarometer "House" reportTemperature reportAtmospherePressure clearData
This scenario records thermometer and barometer reads, reporting, and cleanup as an ordered Language value; execution is deferred to an interpreter. section 2.2.3
Internally, Free has two recursive cases: Pure ends a chain with a result, while Free holds one language action whose continuation leads to the next Free layer. Monad binding replaces the temporary Pure continuation with later work, avoiding uncontrolled growth of nested script types. section 6.3.2 section 6.3.4

