ExamplesBy LevelBy TopicLearning Paths
191 Expert

Effects as Exceptions

Functional Programming

Tutorial Video

Text description (accessibility)

This video demonstrates the "Effects as Exceptions" functional Rust example. Difficulty level: Expert. Key concepts covered: Functional Programming. Exceptions are a special case of algebraic effects — an effect that does not resume the computation. Key difference from OCaml: 1. **Typing**: Rust's `Result<T, E>` is statically typed — the error type is part of the function signature; OCaml's `exn` is a single open extensible type (all exceptions have the same type).

Tutorial

The Problem

Exceptions are a special case of algebraic effects — an effect that does not resume the computation. When an exception is thrown, the stack is unwound to the nearest handler, which may handle the exception and continue or re-throw it. Understanding exceptions as non-resumable effects unifies the conceptual model: both exceptions and effects interrupt the normal control flow and transfer control to a handler.

🎯 Learning Outcomes

  • • Understand exceptions as non-resumable algebraic effects
  • • Learn how Result<T, E> and the ? operator implement exception-like error handling in Rust
  • • See the connection between effect handler Abort results and exception propagation
  • • Compare Rust's typed exceptions (Result) with OCaml's dynamic exceptions (exn)
  • Code Example

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

    Key Differences

  • Typing: Rust's Result<T, E> is statically typed — the error type is part of the function signature; OCaml's exn is a single open extensible type (all exceptions have the same type).
  • Zero-cost: Rust's ? compiles to conditional branches — no runtime overhead on the happy path; OCaml's exceptions use stack unwinding — no overhead until thrown.
  • Propagation: Rust's ? requires the error types to match (or implement From); OCaml's exceptions propagate to any matching handler regardless of type.
  • Checked vs. unchecked: Rust forces callers to handle Result explicitly; OCaml's exceptions can propagate silently — closer to Java's unchecked exceptions.
  • OCaml Approach

    OCaml has two exception mechanisms:

  • Traditional exceptions: exception MyError of string; raise (MyError "msg") — caught with try ... with MyError msg -> ...
  • OCaml 5 effects: effect Fail : string — caught with a handler that does not call continue
  • The traditional OCaml approach uses dynamic exceptions (like Java's unchecked exceptions). OCaml's result type provides checked, typed exceptions similar to Rust's Result.

    Full Source

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

    Deep Comparison

    OCaml vs Rust: Effect Exceptions

    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 multi-error accumulator using Vec<E> — collect all errors rather than stopping at the first.
  • Write a try_all<T, E>(ops: Vec<impl FnOnce() -> Result<T, E>>) -> Result<Vec<T>, Vec<E>> that runs all operations and returns all successes or all errors.
  • Implement a retry combinator: retry<T, E>(n: usize, f: impl FnMut() -> Result<T, E>) -> Result<T, E>.
  • Open Source Repos