Histomorphism
Tutorial Video
Text description (accessibility)
This video demonstrates the "Histomorphism" functional Rust example. Difficulty level: Expert. Key concepts covered: Functional Programming. A histomorphism (histo) is a catamorphism where the fold function has access to the entire history of previous fold results, not just the immediate result. Key difference from OCaml: 1. **HKT requirement**: These morphisms ideally require higher
Tutorial
The Problem
A histomorphism (histo) is a catamorphism where the fold function has access to the entire history of previous fold results, not just the immediate result. The cofree comonad of the base functor carries the history. This enables computing Fibonacci in O(n) with a single pass, longest increasing subsequence, and dynamic programming patterns that need previous computed values.
🎯 Learning Outcomes
Code Example
#![allow(clippy::all)]
//! # Histomorphism
//! Fold with access to all previous results.
pub fn histo<A, B: Clone>(xs: &[A], nil: B, cons: impl Fn(&A, &[B]) -> B) -> B {
let mut history = vec![nil];
for x in xs.iter().rev() {
let next = cons(x, &history);
history.push(next);
}
history.pop().unwrap()
}
pub fn fib_histo(n: usize) -> u64 {
if n == 0 {
return 0;
}
if n == 1 {
return 1;
}
let xs: Vec<()> = vec![(); n];
histo(&xs, 0u64, |_, hist| {
if hist.len() < 2 {
1
} else {
hist[hist.len() - 1] + hist[hist.len() - 2]
}
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_fib() {
assert_eq!(fib_histo(10), 55);
}
}Key Differences
recursion-schemes are widely used; in Rust and OCaml, direct recursion is more common in practice.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)]
//! # Histomorphism
//! Fold with access to all previous results.
pub fn histo<A, B: Clone>(xs: &[A], nil: B, cons: impl Fn(&A, &[B]) -> B) -> B {
let mut history = vec![nil];
for x in xs.iter().rev() {
let next = cons(x, &history);
history.push(next);
}
history.pop().unwrap()
}
pub fn fib_histo(n: usize) -> u64 {
if n == 0 {
return 0;
}
if n == 1 {
return 1;
}
let xs: Vec<()> = vec![(); n];
histo(&xs, 0u64, |_, hist| {
if hist.len() < 2 {
1
} else {
hist[hist.len() - 1] + hist[hist.len() - 2]
}
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_fib() {
assert_eq!(fib_histo(10), 55);
}
}#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_fib() {
assert_eq!(fib_histo(10), 55);
}
}
Deep Comparison
Histomorphism
Fold with access to all prior results