// Example 216: Fix Point — Unrolling Recursion from a Functor
// Approach 1: Fix point for lists
#[derive(Debug, Clone)]
enum ListF<A> {
NilF,
ConsF(i64, A),
}
impl<A> ListF<A> {
fn map<B>(self, f: impl Fn(A) -> B) -> ListF<B> {
match self {
ListF::NilF => ListF::NilF,
ListF::ConsF(x, rest) => ListF::ConsF(x, f(rest)),
}
}
fn map_ref<B>(&self, f: impl Fn(&A) -> B) -> ListF<B> {
match self {
ListF::NilF => ListF::NilF,
ListF::ConsF(x, rest) => ListF::ConsF(*x, f(rest)),
}
}
}
#[derive(Debug, Clone)]
struct FixList(Box<ListF<FixList>>);
fn nil() -> FixList { FixList(Box::new(ListF::NilF)) }
fn cons(x: i64, xs: FixList) -> FixList { FixList(Box::new(ListF::ConsF(x, xs))) }
fn cata_list<A>(alg: &dyn Fn(ListF<A>) -> A, fix: &FixList) -> A {
alg(fix.0.map_ref(|child| cata_list(alg, child)))
}
// Approach 2: Fix point for binary trees
#[derive(Debug, Clone)]
enum TreeF<A> {
LeafF(i64),
BranchF(A, A),
}
impl<A> TreeF<A> {
fn map_ref<B>(&self, f: impl Fn(&A) -> B) -> TreeF<B> {
match self {
TreeF::LeafF(n) => TreeF::LeafF(*n),
TreeF::BranchF(l, r) => TreeF::BranchF(f(l), f(r)),
}
}
}
#[derive(Debug, Clone)]
struct FixTree(Box<TreeF<FixTree>>);
fn leaf(n: i64) -> FixTree { FixTree(Box::new(TreeF::LeafF(n))) }
fn branch(l: FixTree, r: FixTree) -> FixTree { FixTree(Box::new(TreeF::BranchF(l, r))) }
fn cata_tree<A>(alg: &dyn Fn(TreeF<A>) -> A, fix: &FixTree) -> A {
alg(fix.0.map_ref(|child| cata_tree(alg, child)))
}
// Approach 3: Generic Fix via trait
trait Functor: Sized {
type Inner;
fn fmap<B>(&self, f: impl Fn(&Self::Inner) -> B) -> Self::Mapped<B>;
type Mapped<B>;
}
fn main() {
// List fix point
let xs = cons(1, cons(2, cons(3, nil())));
let sum_alg = |l: ListF<i64>| match l {
ListF::NilF => 0,
ListF::ConsF(x, acc) => x + acc,
};
assert_eq!(cata_list(&sum_alg, &xs), 6);
let len_alg = |l: ListF<usize>| match l {
ListF::NilF => 0,
ListF::ConsF(_, acc) => 1 + acc,
};
assert_eq!(cata_list(&len_alg, &xs), 3);
let to_vec = |l: ListF<Vec<i64>>| match l {
ListF::NilF => vec![],
ListF::ConsF(x, mut acc) => { acc.insert(0, x); acc },
};
assert_eq!(cata_list(&to_vec, &xs), vec![1, 2, 3]);
// Tree fix point
let tree = branch(branch(leaf(1), leaf(2)), leaf(3));
let sum_tree = |t: TreeF<i64>| match t {
TreeF::LeafF(n) => n,
TreeF::BranchF(l, r) => l + r,
};
assert_eq!(cata_tree(&sum_tree, &tree), 6);
let depth_tree = |t: TreeF<usize>| match t {
TreeF::LeafF(_) => 0,
TreeF::BranchF(l, r) => 1 + l.max(r),
};
assert_eq!(cata_tree(&depth_tree, &tree), 2);
println!("✓ All tests passed");
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_list_cata() {
let xs = cons(10, cons(20, nil()));
let sum = |l: ListF<i64>| match l { ListF::NilF => 0, ListF::ConsF(x, a) => x + a };
assert_eq!(cata_list(&sum, &xs), 30);
}
#[test]
fn test_empty_list() {
let sum = |l: ListF<i64>| match l { ListF::NilF => 0, ListF::ConsF(x, a) => x + a };
assert_eq!(cata_list(&sum, &nil()), 0);
}
#[test]
fn test_tree_count() {
let t = branch(leaf(1), branch(leaf(2), leaf(3)));
let count = |t: TreeF<usize>| match t { TreeF::LeafF(_) => 1, TreeF::BranchF(l, r) => l + r };
assert_eq!(cata_tree(&count, &t), 3);
}
}
(* Example 216: Fix Point — Unrolling Recursion from a Functor *)
(* The key insight: separate the SHAPE of data from RECURSION itself.
type 'a list_f = NilF | ConsF of int * 'a (* one layer, non-recursive *)
type fix_list = fix_list list_f (* recursion via fix point *)
*)
(* Approach 1: Fix point for lists *)
type 'a list_f = NilF | ConsF of int * 'a
let map_list_f f = function
| NilF -> NilF
| ConsF (x, rest) -> ConsF (x, f rest)
type fix_list = FixL of fix_list list_f
let unfix_l (FixL f) = f
(* Build lists *)
let nil = FixL NilF
let cons x xs = FixL (ConsF (x, xs))
(* cata for lists *)
let rec cata_list alg (FixL f) =
alg (map_list_f (cata_list alg) f)
(* Approach 2: Fix point for binary trees *)
type 'a tree_f = LeafF of int | BranchF of 'a * 'a
let map_tree_f f = function
| LeafF n -> LeafF n
| BranchF (l, r) -> BranchF (f l, f r)
type fix_tree = FixT of fix_tree tree_f
let unfix_t (FixT f) = f
let leaf n = FixT (LeafF n)
let branch l r = FixT (BranchF (l, r))
let rec cata_tree alg (FixT f) =
alg (map_tree_f (cata_tree alg) f)
(* Approach 3: Generic fix point (using polymorphic variant or functor) *)
(* In OCaml, we can use a functor module *)
module type FUNCTOR = sig
type 'a t
val map : ('a -> 'b) -> 'a t -> 'b t
end
module Fix (F : FUNCTOR) = struct
type t = In of t F.t
let out (In f) = f
let rec cata alg (In f) =
alg (F.map (cata alg) f)
end
(* === Tests === *)
let () =
(* List fix point *)
let xs = cons 1 (cons 2 (cons 3 nil)) in
let sum_alg = function NilF -> 0 | ConsF (x, acc) -> x + acc in
assert (cata_list sum_alg xs = 6);
let length_alg = function NilF -> 0 | ConsF (_, acc) -> 1 + acc in
assert (cata_list length_alg xs = 3);
let to_list_alg = function NilF -> [] | ConsF (x, acc) -> x :: acc in
assert (cata_list to_list_alg xs = [1; 2; 3]);
(* Tree fix point *)
let tree = branch (branch (leaf 1) (leaf 2)) (leaf 3) in
let sum_tree = function LeafF n -> n | BranchF (l, r) -> l + r in
assert (cata_tree sum_tree tree = 6);
let depth_tree = function LeafF _ -> 0 | BranchF (l, r) -> 1 + max l r in
assert (cata_tree depth_tree tree = 2);
let count_tree = function LeafF _ -> 1 | BranchF (l, r) -> l + r in
assert (cata_tree count_tree tree = 3);
print_endline "✓ All tests passed"