liveBook · Manning

Hierarchical free monad

As explained in Functional Design and Architecture: Examples in Haskell

A hierarchical free monad is an architectural design pattern that organizes composable free-monad-based program structure hierarchically.

A hierarchical free monad (HFM) organizes an application as a hierarchy of small free-monad eDSLs rather than one language that owns every operation. A higher-level language can evaluate scripts from lower-level languages, so subsystems remain separated while business scripts can compose their capabilities. The pattern is intended for statically typed functional application architecture, alongside domain modeling, frameworks, concurrency, logging, and testing. section 1.0 section 2.2.3

The motivating problem is composition without flattening unrelated responsibilities into one free language or a plain effect list. When a script must use two languages directly, it must know both. HFM instead lets one language expose selected operations of another: languages are nested where that dependency makes architectural sense. The result is a subsystem hierarchy, not a claim that every concern is the same kind of effect. section 2.2.3 section 9.1.3

Figure 2.15 Hierarchical free monads

The hierarchy diagram provides the core structural idea: free-monadic languages are arranged by dependency and delegation rather than merged into a single interface. section 2.2.3

A lower-level eDSL keeps a narrow responsibility. In a spaceship model, Hdl defines devices and components but does not control the ship. A higher-level LogicControl language can embed an Hdl script through an evaluation operation, receive its result, and continue with reporting, storage, or device-control work. Existential result types in the evaluation constructors allow embedded scripts with different return types to be used through one outer language. section 6.4.2 section 6.4.2

Device definition followed by control

A LogicControl script first evaluates createBoosters, which returns two controllers from Hdl. It then reads each controller status through DeviceControl operations and converts hardware failures into a LogicFailure. Device construction, hardware access, and control-level failure handling therefore remain distinct while participating in one typed workflow. section 6.4.1 section 6.4.2

This structure addresses an earlier, awkward alternative: list-based nested languages require external-language type parameters, circular dependencies for unbounded scripts, and deeply nested lambdas and lists. Free-monad scripts instead use ordinary monadic sequencing and normal functions, making the business-logic layer easier to organize into isolated sublayers with clearer responsibilities. section 6.4.1 section 6.4.2

Hierarchies can also separate application-level orchestration from reusable subsystem logic:

  • Place random values, delays, mutable references, and meteor reporting in LangL; let AppL evaluate LangL scenarios and fork LangL processes for concurrent work. section 9.1.3
  • Nest LoggerL inside LangL so business logic calls logDebug without owning console formatting or the logger interpreter. section 10.1.1
  • Keep several layers only when they clarify responsibilities; a hierarchy is presented as useful, not suitable for every case. section 9.1.3

The practical boundary is interpretation. Languages describe operations; interpreters supply operational details such as sensor responses or console output. Smart constructors adapt a lower-level language into the higher-level one, hiding helper scripts where direct use would not fit the intended subsystem interface. This preserves a readable business vocabulary while keeping implementation choices outside scripts. section 6.4.2 section 10.1.1

How Functional Design and Architecture: Examples in Haskell covers it

How deep it goes
Explains typed language nesting, GADT evaluation constructors, smart constructors, and interpreters, while deferring broader domain implementation.
Examples
Uses spaceship device creation and control, meteor-tracking processes, and a nested console logger.
What it assumes
Assumes familiarity with statically typed functional programming while developing free-monad design step by step from simpler concepts.
What you can do after
Build it from scratch by defining focused free languages and embedding lower-level scripts through typed evaluation operations.
How it connects
Connects free monads, eDSLs, smart constructors, interpreters, GADTs, subsystem boundaries, concurrency, logging, SRP, and ISP.

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.