Paramorphism
Tutorial Video
Text description (accessibility)
This video demonstrates the "Paramorphism" functional Rust example. Difficulty level: Expert. Key concepts covered: Functional Programming. A paramorphism (para) is a generalization of a catamorphism where the fold function also receives the original substructure in addition to the folded result. Key difference from OCaml: 1. **HKT requirement**: These morphisms ideally require higher
Tutorial
The Problem
A paramorphism (para) is a generalization of a catamorphism where the fold function also receives the original substructure in addition to the folded result. This enables patterns like Fibonacci (needs both current and previous values), factorial (needs both n and (n-1)!), and pretty-printing with parentheses around specific forms. It is strictly more powerful than cata for certain patterns.
🎯 Learning Outcomes
Code Example
#![allow(clippy::all)]
//! # Paramorphism
//! Fold with access to original substructure.
pub fn para<A: Clone, B>(xs: &[A], nil: B, cons: impl Fn(&A, &[A], B) -> B) -> B {
let mut acc = nil;
for i in (0..xs.len()).rev() {
acc = cons(&xs[i], &xs[i + 1..], acc);
}
acc
}
pub fn tails<A: Clone>(xs: &[A]) -> Vec<Vec<A>> {
para(xs, vec![vec![]], |_, rest, mut acc| {
acc.insert(0, rest.to_vec());
acc
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_tails() {
let result = tails(&[1, 2, 3]);
assert_eq!(result.len(), 4); // [1,2,3], [2,3], [3], []
}
}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)]
//! # Paramorphism
//! Fold with access to original substructure.
pub fn para<A: Clone, B>(xs: &[A], nil: B, cons: impl Fn(&A, &[A], B) -> B) -> B {
let mut acc = nil;
for i in (0..xs.len()).rev() {
acc = cons(&xs[i], &xs[i + 1..], acc);
}
acc
}
pub fn tails<A: Clone>(xs: &[A]) -> Vec<Vec<A>> {
para(xs, vec![vec![]], |_, rest, mut acc| {
acc.insert(0, rest.to_vec());
acc
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_tails() {
let result = tails(&[1, 2, 3]);
assert_eq!(result.len(), 4); // [1,2,3], [2,3], [3], []
}
}#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_tails() {
let result = tails(&[1, 2, 3]);
assert_eq!(result.len(), 4); // [1,2,3], [2,3], [3], []
}
}
Deep Comparison
Paramorphism
Fold that also sees remaining structure