ExamplesBy LevelBy TopicLearning Paths
607 Fundamental

Fixed-Point Types

Functional Programming

Tutorial Video

Text description (accessibility)

This video demonstrates the "Fixed-Point Types" functional Rust example. Difficulty level: Fundamental. Key concepts covered: Functional Programming. A fixed point of a type function F is a type T where T = F(T). Key difference from OCaml: 1. **HKT requirement**: These morphisms ideally require higher

Tutorial

The Problem

A fixed point of a type function F is a type T where T = F(T). In Haskell, Fix F = F (Fix F) captures the recursive structure common to all recursive types. This abstraction enables writing functions over any recursive type generically. In Rust, fixed points require Box or Rc for heap indirection. This pattern is the theoretical foundation for catamorphisms, anamorphisms, and the morphism zoo.

🎯 Learning Outcomes

  • • The categorical definition and the mathematical laws that must hold
  • • How to implement this pattern in Rust despite the lack of higher-kinded types
  • • The relationship to more familiar functional idioms (fold, unfold, map)
  • • Key concepts: Fix, unfix, recursive types as fixed points, recursive schemes
  • • Where this pattern appears in production systems and when simpler alternatives suffice
  • Code Example

    #![allow(clippy::all)]
    //! # Fixed-Point Types
    //! Recursive types using fixed-point combinators.
    
    pub enum Fix<F> {
        In(Box<F>),
    }
    
    pub enum ListF<A, R> {
        Nil,
        Cons(A, R),
    }
    
    pub type FixList<A> = Fix<ListF<A, Fix<ListF<A, ()>>>>;
    
    pub fn fold_list<A: Clone, B>(list: &[A], init: B, f: impl Fn(B, &A) -> B) -> B {
        list.iter().fold(init, f)
    }
    
    #[cfg(test)]
    mod tests {
        use super::*;
        #[test]
        fn test_fold() {
            let sum = fold_list(&[1, 2, 3], 0, |a, b| a + b);
            assert_eq!(sum, 6);
        }
    }

    Key Differences

  • HKT requirement: These morphisms ideally require higher-kinded types for full generality; Rust uses GATs or associated types as approximations.
  • Performance: Rust's implementations are more verbose but compile to efficient machine code; OCaml's implementations are more concise with similar runtime performance.
  • Practical adoption: In Haskell, recursive schemes from recursion-schemes are widely used; in Rust and OCaml, direct recursion is more common in practice.
  • Theoretical value: Understanding these patterns deepens intuition for all recursive programming, even when direct recursion is used in production code.
  • OCaml Approach

    OCaml's pattern matching and recursive types make morphism implementations natural. The Fix type and F-algebra/coalgebra patterns translate directly, though without Haskell's typeclass machinery:

    (* OCaml recursive schemes use:
       - Recursive variant types for F-algebras
       - Higher-order functions for the morphism
       - GADTs for type-safe fixed points in advanced cases *)
    

    Full Source

    #![allow(clippy::all)]
    //! # Fixed-Point Types
    //! Recursive types using fixed-point combinators.
    
    pub enum Fix<F> {
        In(Box<F>),
    }
    
    pub enum ListF<A, R> {
        Nil,
        Cons(A, R),
    }
    
    pub type FixList<A> = Fix<ListF<A, Fix<ListF<A, ()>>>>;
    
    pub fn fold_list<A: Clone, B>(list: &[A], init: B, f: impl Fn(B, &A) -> B) -> B {
        list.iter().fold(init, f)
    }
    
    #[cfg(test)]
    mod tests {
        use super::*;
        #[test]
        fn test_fold() {
            let sum = fold_list(&[1, 2, 3], 0, |a, b| a + b);
            assert_eq!(sum, 6);
        }
    }
    ✓ Tests Rust test suite
    #[cfg(test)]
    mod tests {
        use super::*;
        #[test]
        fn test_fold() {
            let sum = fold_list(&[1, 2, 3], 0, |a, b| a + b);
            assert_eq!(sum, 6);
        }
    }

    Deep Comparison

    Fixed-Point Types

    Fix F = F (Fix F) - recursive structure

    Exercises

  • Laws verification: Write tests that verify the categorical laws for this morphism on a specific data type.
  • New data type: Apply the morphism to a different recursive data type (e.g., apply catamorphism to a rose tree instead of a binary tree).
  • Comparison: Implement the same computation using direct recursion and the morphism — measure whether the morphism version composes more cleanly.
  • Open Source Repos