use std::fmt::Debug;
#[derive(Debug, Clone, PartialEq)]
pub struct Focused<C, A> {
value: A,
context: C,
}
impl<C: Clone + Debug, A: Clone + Debug> Focused<C, A> {
pub fn new(value: A, context: C) -> Self {
Self { value, context }
}
pub fn extract(&self) -> &A {
&self.value
}
pub fn context(&self) -> &C {
&self.context
}
pub fn extend<B: Clone + Debug>(&self, f: impl Fn(&Focused<C, A>) -> B) -> Focused<C, B> {
Focused {
value: f(self),
context: self.context.clone(),
}
}
pub fn duplicate(&self) -> Focused<C, Focused<C, A>> {
Focused {
value: self.clone(),
context: self.context.clone(),
}
}
pub fn map<B: Clone + Debug>(&self, f: impl Fn(&A) -> B) -> Focused<C, B> {
Focused {
value: f(&self.value),
context: self.context.clone(),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Cofree<A> {
pub head: A,
pub tail: Vec<Cofree<A>>,
}
impl<A: Clone + Debug> Cofree<A> {
pub fn leaf(value: A) -> Self {
Self {
head: value,
tail: Vec::new(),
}
}
pub fn node(value: A, children: Vec<Cofree<A>>) -> Self {
Self {
head: value,
tail: children,
}
}
pub fn extract(&self) -> &A {
&self.head
}
pub fn extend<B: Clone + Debug>(&self, f: &dyn Fn(&Cofree<A>) -> B) -> Cofree<B> {
Cofree {
head: f(self),
tail: self.tail.iter().map(|child| child.extend(f)).collect(),
}
}
pub fn duplicate(&self) -> Cofree<Cofree<A>> {
Cofree {
head: self.clone(),
tail: self.tail.iter().map(|child| child.duplicate()).collect(),
}
}
pub fn fold<B>(&self, f: &dyn Fn(&A, &[B]) -> B) -> B {
let children: Vec<B> = self.tail.iter().map(|c| c.fold(f)).collect();
f(&self.head, &children)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn focused_extract_returns_value() {
let f = Focused::new(42, "context");
assert_eq!(f.extract(), &42);
}
#[test]
fn focused_left_identity() {
let w = Focused::new(42, vec![1, 2, 3]);
let result = w.extend(|fw| fw.extract().clone());
assert_eq!(result.value, w.value);
assert_eq!(result.context, w.context);
}
#[test]
fn focused_right_identity() {
let w = Focused::new(10, "ctx");
let f = |fw: &Focused<&str, i32>| fw.extract() * 2;
let extended = w.extend(f);
assert_eq!(*extended.extract(), f(&w));
}
#[test]
fn focused_duplicate_extract() {
let w = Focused::new(42, "ctx");
let dup = w.duplicate();
assert_eq!(dup.extract(), &w);
}
#[test]
fn cofree_leaf_extract() {
let leaf = Cofree::leaf(42);
assert_eq!(cofree_leaf_extract_val(&leaf), &42);
}
fn cofree_leaf_extract_val(c: &Cofree<i32>) -> &i32 {
c.extract()
}
#[test]
fn cofree_tree_structure() {
let tree = Cofree::node("root", vec![Cofree::leaf("left"), Cofree::leaf("right")]);
assert_eq!(tree.extract(), &"root");
assert_eq!(tree.tail.len(), 2);
assert_eq!(tree.tail[0].extract(), &"left");
}
#[test]
fn cofree_fold_counts_nodes() {
let tree = Cofree::node(
1,
vec![Cofree::node(2, vec![Cofree::leaf(3)]), Cofree::leaf(4)],
);
let count = tree.fold(&|_, children: &[usize]| 1 + children.iter().sum::<usize>());
assert_eq!(count, 4);
}
#[test]
fn cofree_extend_sums_subtrees() {
let tree = Cofree::node(1, vec![Cofree::leaf(2), Cofree::leaf(3)]);
let summed = tree
.extend(&|node| node.fold(&|val, children: &[i32]| val + children.iter().sum::<i32>()));
assert_eq!(*summed.extract(), 6); assert_eq!(*summed.tail[0].extract(), 2);
assert_eq!(*summed.tail[1].extract(), 3);
}
#[test]
fn trace_schema_as_cofree() {
let trace = Cofree::node(
("tokenize", "5 tokens"),
vec![
Cofree::node(
("parse", "S[dcl]"),
vec![Cofree::leaf(("interpret", "prop(dog, big)"))],
),
Cofree::leaf(("metacognition", "KnownKnown")),
],
);
assert_eq!(trace.extract().0, "tokenize");
let steps = trace.fold(&|val, children: &[Vec<&str>]| {
let mut all = vec![val.0];
for c in children {
all.extend(c.iter());
}
all
});
assert_eq!(
steps,
vec!["tokenize", "parse", "interpret", "metacognition"]
);
}
}