ExamplesBy LevelBy TopicLearning Paths
186 Expert

Free Monad Interpreters

Functional Programming

Tutorial Video

Text description (accessibility)

This video demonstrates the "Free Monad Interpreters" functional Rust example. Difficulty level: Expert. Key concepts covered: Functional Programming. The power of free monads is multiple interpreters for the same program. Key difference from OCaml: 1. **Effect handlers vs. free monads**: OCaml 5's effect handlers are a native runtime mechanism; free monads are a library pattern — effect handlers are faster and more composable.

Tutorial

The Problem

The power of free monads is multiple interpreters for the same program. A console DSL program can be interpreted by a "real" interpreter that reads from stdin and writes to stdout, a "test" interpreter that uses mock I/O with predetermined inputs, and a "logging" interpreter that records every operation for debugging. The program description is shared; only the interpreter changes. This is the essence of dependency injection at the computation level.

🎯 Learning Outcomes

  • • Implement multiple interpreters for the same free monad DSL
  • • Understand how different interpreters provide testability, logging, and simulation
  • • Learn the interpreter pattern: fold the free monad tree with different algebras
  • • See the analogy to dependency injection: swapping interpreters changes the computation's context
  • Code Example

    #![allow(clippy::all)]
    // Stub — awaiting conversion from OCaml source.

    Key Differences

  • Effect handlers vs. free monads: OCaml 5's effect handlers are a native runtime mechanism; free monads are a library pattern — effect handlers are faster and more composable.
  • Mutual recursion: OCaml's run_io and run_test can be let rec; Rust's interpreter functions are plain functions called recursively.
  • State threading: The test interpreter threads mutable state (inputs, outputs) through the traversal; in OCaml this is often done with a state monad or ref cells.
  • Stack depth: Both interpreters recurse on the continuation tree — deep programs risk stack overflow; trampolining (example 197) resolves this.
  • OCaml Approach

    OCaml's interpreter pattern is identical in structure. The run_io and run_test functions use let rec and pattern matching. OCaml's effect handlers (OCaml 5) provide a more efficient alternative to free monads for the same separation: effects are declared, then handled by different effect handlers — the operational semantics is similar to free monad interpreters but implemented at the runtime level.

    Full Source

    #![allow(clippy::all)]
    // Stub — awaiting conversion from OCaml source.

    Deep Comparison

    OCaml vs Rust: Free Monad Interp

    Overview

    See the example.rs and example.ml files for detailed implementations.

    Key Differences

    AspectOCamlRust
    Type systemHindley-MilnerOwnership + traits
    MemoryGCZero-cost abstractions
    MutabilityExplicit refmut keyword
    Error handlingOption/ResultResult<T, E>

    See README.md for detailed comparison.

    Exercises

  • Implement a "counting" interpreter that returns the number of I/O operations performed.
  • Write a "recording" interpreter that captures the full execution trace as a Vec<(Operation, Value)>.
  • Add a timeout interpreter: if the program performs more than N ReadLine operations, terminate with an error.
  • Open Source Repos