Stage 1 — TreeishPipeline
#![allow(unused)]
fn main() {
/// Stage-1 typestate pipeline with two base slots: `treeish`
/// (graph) and `fold`. Used when children are directly enumerable
/// from nodes of the same type (`N → N*`).
#[must_use]
pub struct TreeishPipeline<D, N, H, R>
where D: Domain<N>,
N: 'static, H: 'static, R: 'static,
{
pub(crate) treeish: <D as Domain<N>>::Graph<N>,
pub(crate) fold: <D as Domain<N>>::Fold<H, R>,
}
}
Two slots:
treeish: <D as Domain<N>>::Graph<N>— direct child enumeration,N → N*.fold: <D as Domain<N>>::Fold<H, R>— the algebra overN.
No grow step, no entry seeds. Execution starts from a &N
root supplied to the executor.
Constructors
#![allow(unused)]
fn main() {
// Shared domain.
TreeishPipeline::<Shared, _, _, _>::new(
treeish_arc, // hylic::graph::Treeish<N>
&fold, // &shared::Fold<N, H, R>
);
// Local domain — note the `_local` suffix; Rust's inherent-method
// resolution can't disambiguate two `new`s on the same struct that
// differ only in the domain marker.
TreeishPipeline::<Local, _, _, _>::new_local(
treeish_local, // local::Edgy<N, N>
fold_local, // local::Fold<N, H, R>
);
// Domain-generic.
TreeishPipeline::<D, _, _, _>::from_slots(treeish, fold);
}
Stage-1 reshape
One sugar — there’s no grow axis to reshape and no seeds to
filter:
| method | output |
|---|---|
map_node_bi(co, contra) | TreeishPipeline<D, N2, H, R> |
Provided by TreeishSugarsShared (Local mirror:
TreeishSugarsLocal); see Sugars.
Stage 2
Two ways to enter:
- Explicit:
tree_pipeline.lift()returnsStage2Pipeline<TreeishPipeline<D, N, H, R>, IdentityLift>. - Auto-lift: every Stage-2 sugar is also callable directly on
TreeishPipeline.tree_pipeline.wrap_init(w)is shorthand fortree_pipeline.lift().wrap_init(w).
#![allow(unused)]
fn main() {
#[test]
fn treeish_pipeline_chain() {
use hylic_pipeline::prelude::*;
#[derive(Clone)]
struct Node { value: u64, children: Vec<Node> }
let root = Node {
value: 1,
children: vec![
Node { value: 2, children: vec![] },
Node { value: 3, children: vec![] },
],
};
let tp: TreeishPipeline<Shared, Node, u64, u64> = TreeishPipeline::new(
treeish(|n: &Node| n.children.clone()),
&fold(|n: &Node| n.value, |h: &mut u64, c: &u64| *h += c, |h: &u64| *h),
);
let r: (u64, bool) = tp
.wrap_init(|n: &Node, orig: &dyn Fn(&Node) -> u64| orig(n) + 1)
.zipmap(|r: &u64| *r > 5)
.run_from_node(&FUSED, &root);
assert_eq!(r, (9, true));
}
}
The chain’s input N stays at the user’s N (no wrap layer);
the Wrap impl is Identity.
Running
#![allow(unused)]
fn main() {
let r = pipeline.run_from_node(&FUSED, &root);
}
PipelineExec::run_from_node(&exec, &root) is a blanket method
on every TreeishSource. The first init runs on the supplied
root. Returns the chain-tip R — the base fold’s R when no
Stage-2 sugars are composed, otherwise whatever the rightmost
lift produces.
Stage2Pipeline<TreeishPipeline<…>, L> inherits the same
method through its TreeishSource impl; the call shape is
identical.
Worked example
#![allow(unused)]
fn main() {
#[test]
fn treeish_pipeline_ctor() {
use hylic_pipeline::prelude::*;
#[derive(Clone)]
struct Node { value: u64, children: Vec<Node> }
let root = Node { value: 7, children: vec![] };
let tp: TreeishPipeline<Shared, Node, u64, u64> = TreeishPipeline::new(
treeish(|n: &Node| n.children.clone()),
&fold(
|n: &Node| n.value,
|h: &mut u64, c: &u64| *h += c,
|h: &u64| *h,
),
);
assert_eq!(tp.run_from_node(&FUSED, &root), 7);
}
}