// Example 208: Traversal โ Focus on Zero or More Targets
// A traversal focuses on 0-to-many values inside a structure
struct Traversal<S, A> {
over: Box<dyn Fn(&dyn Fn(&A) -> A, &S) -> S>,
to_list: Box<dyn Fn(&S) -> Vec<A>>,
}
impl<S: 'static, A: 'static> Traversal<S, A> {
fn new(
over: impl Fn(&dyn Fn(&A) -> A, &S) -> S + 'static,
to_list: impl Fn(&S) -> Vec<A> + 'static,
) -> Self {
Traversal { over: Box::new(over), to_list: Box::new(to_list) }
}
fn modify(&self, f: &dyn Fn(&A) -> A, s: &S) -> S { (self.over)(f, s) }
fn collect(&self, s: &S) -> Vec<A> { (self.to_list)(s) }
fn length_of(&self, s: &S) -> usize { self.collect(s).len() }
}
// Approach 1: Traversal over Vec elements
fn each_traversal<T: Clone + 'static>() -> Traversal<Vec<T>, T> {
Traversal::new(
|f, xs| xs.iter().map(|x| f(x)).collect(),
|xs| xs.clone(),
)
}
// Approach 2: Traversal into a tree
#[derive(Debug, Clone, PartialEq)]
enum Tree<T> {
Leaf(T),
Branch(Box<Tree<T>>, Box<Tree<T>>),
}
fn each_leaf<T: Clone + 'static>() -> Traversal<Tree<T>, T> {
fn over_tree<T: Clone>(f: &dyn Fn(&T) -> T, t: &Tree<T>) -> Tree<T> {
match t {
Tree::Leaf(x) => Tree::Leaf(f(x)),
Tree::Branch(l, r) => Tree::Branch(
Box::new(over_tree(f, l)),
Box::new(over_tree(f, r)),
),
}
}
fn to_list_tree<T: Clone>(t: &Tree<T>) -> Vec<T> {
match t {
Tree::Leaf(x) => vec![x.clone()],
Tree::Branch(l, r) => {
let mut v = to_list_tree(l);
v.extend(to_list_tree(r));
v
}
}
}
Traversal::new(over_tree, to_list_tree)
}
// Approach 3: Combinators
fn sum_of(t: &Traversal<Vec<i32>, i32>, s: &Vec<i32>) -> i32 {
t.collect(s).iter().sum()
}
fn all_of<S, A>(t: &Traversal<S, A>, pred: impl Fn(&A) -> bool, s: &S) -> bool {
t.collect(s).iter().all(|a| pred(a))
}
fn any_of<S, A>(t: &Traversal<S, A>, pred: impl Fn(&A) -> bool, s: &S) -> bool {
t.collect(s).iter().any(|a| pred(a))
}
// Filtered traversal
fn filtered<T: Clone + 'static>(
pred: impl Fn(&T) -> bool + Clone + 'static,
) -> Traversal<Vec<T>, T> {
let p = pred.clone();
let p2 = pred;
Traversal::new(
move |f, xs: &Vec<T>| xs.iter().map(|x| if p(x) { f(x) } else { x.clone() }).collect(),
move |xs: &Vec<T>| xs.iter().filter(|x| p2(x)).cloned().collect(),
)
}
fn main() {
// List traversal
let xs = vec![1, 2, 3, 4, 5];
let t = each_traversal::<i32>();
assert_eq!(t.collect(&xs), vec![1, 2, 3, 4, 5]);
assert_eq!(t.modify(&|x| x * 2, &xs), vec![2, 4, 6, 8, 10]);
assert_eq!(t.length_of(&xs), 5);
assert_eq!(sum_of(&t, &xs), 15);
// Tree traversal
let tree = Tree::Branch(
Box::new(Tree::Branch(Box::new(Tree::Leaf(1)), Box::new(Tree::Leaf(2)))),
Box::new(Tree::Leaf(3)),
);
let tl = each_leaf::<i32>();
assert_eq!(tl.collect(&tree), vec![1, 2, 3]);
let tree2 = tl.modify(&|x| x + 10, &tree);
assert_eq!(each_leaf::<i32>().collect(&tree2), vec![11, 12, 13]);
// Filtered
let evens = filtered(|x: &i32| x % 2 == 0);
assert_eq!(evens.collect(&xs), vec![2, 4]);
assert_eq!(evens.modify(&|x| x * 10, &xs), vec![1, 20, 3, 40, 5]);
// Combinators
assert!(all_of(&t, |x| *x > 0, &xs));
assert!(any_of(&t, |x| *x == 3, &xs));
println!("โ All tests passed");
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_vec_traversal() {
let t = each_traversal::<i32>();
let xs = vec![10, 20, 30];
assert_eq!(t.modify(&|x| x + 1, &xs), vec![11, 21, 31]);
}
#[test]
fn test_tree_traversal() {
let t = each_leaf::<String>();
let tree = Tree::Leaf("hello".to_string());
assert_eq!(t.collect(&tree), vec!["hello".to_string()]);
}
#[test]
fn test_filtered() {
let f = filtered(|x: &i32| *x > 3);
let xs = vec![1, 2, 3, 4, 5];
assert_eq!(f.collect(&xs), vec![4, 5]);
}
#[test]
fn test_length_of() {
let t = each_traversal::<i32>();
assert_eq!(t.length_of(&vec![1, 2, 3]), 3);
assert_eq!(t.length_of(&vec![]), 0);
}
}
(* Example 208: Traversal โ Focus on Zero or More Targets *)
(* A traversal focuses on 0-to-many values inside a structure *)
type ('s, 'a) traversal = {
over : ('a -> 'a) -> 's -> 's; (* modify all focused values *)
to_list : 's -> 'a list; (* collect all focused values *)
}
(* Approach 1: Traversal over list elements *)
let each_traversal : ('a list, 'a) traversal = {
over = List.map;
to_list = Fun.id;
}
(* Approach 2: Traversal into a tree structure *)
type 'a tree = Leaf of 'a | Branch of 'a tree * 'a tree
let rec tree_over f = function
| Leaf x -> Leaf (f x)
| Branch (l, r) -> Branch (tree_over f l, tree_over f r)
let rec tree_to_list = function
| Leaf x -> [x]
| Branch (l, r) -> tree_to_list l @ tree_to_list r
let each_leaf : ('a tree, 'a) traversal = {
over = tree_over;
to_list = tree_to_list;
}
(* Approach 3: Traversal combinators *)
let length_of t s = List.length (t.to_list s)
let sum_of t s = List.fold_left ( + ) 0 (t.to_list s)
let all_of t pred s = List.for_all pred (t.to_list s)
let any_of t pred s = List.exists pred (t.to_list s)
let find_of t pred s = List.find_opt pred (t.to_list s)
(* Filtered traversal *)
let filtered pred (t : ('s, 'a) traversal) : ('s, 'a) traversal = {
over = (fun f s -> t.over (fun a -> if pred a then f a else a) s);
to_list = (fun s -> List.filter pred (t.to_list s));
}
(* === Tests === *)
let () =
(* List traversal *)
let xs = [1; 2; 3; 4; 5] in
assert (each_traversal.to_list xs = [1; 2; 3; 4; 5]);
assert (each_traversal.over (fun x -> x * 2) xs = [2; 4; 6; 8; 10]);
assert (length_of each_traversal xs = 5);
assert (sum_of each_traversal xs = 15);
(* Tree traversal *)
let tree = Branch (Branch (Leaf 1, Leaf 2), Leaf 3) in
assert (each_leaf.to_list tree = [1; 2; 3]);
let tree2 = each_leaf.over (fun x -> x + 10) tree in
assert (each_leaf.to_list tree2 = [11; 12; 13]);
assert (sum_of each_leaf tree = 6);
(* Filtered traversal *)
let evens = filtered (fun x -> x mod 2 = 0) each_traversal in
assert (evens.to_list xs = [2; 4]);
let xs2 = evens.over (fun x -> x * 10) xs in
assert (xs2 = [1; 20; 3; 40; 5]);
(* Combinators *)
assert (all_of each_traversal (fun x -> x > 0) xs);
assert (not (all_of each_traversal (fun x -> x > 3) xs));
assert (any_of each_traversal (fun x -> x = 3) xs);
assert (find_of each_traversal (fun x -> x > 3) xs = Some 4);
print_endline "โ All tests passed"