Reversible Pipelines
Practical patterns combining reflection, inversion, and provenance. These examples assume the API from Reflection & Inversion.
Reflecting a pipeline for logging
describe() turns a pipeline into a serializable stage list — useful for
logging, debugging, and storing pipeline definitions as config.
#![allow(unused)] fn main() { use orlando_transducers::pipeline; use orlando_transducers::Describable; let p = pipeline!(map(|x: i32| x + 1) >> filter(|x: &i32| *x % 2 == 0) >> take(3)); // Log what the pipeline does, without running it. let summary: Vec<&'static str> = p.describe().iter().map(|s| s.name()).collect(); println!("pipeline = {}", summary.join(" >> ")); // pipeline = map >> filter >> take }
Reversible unit conversion
A pipeline built entirely from isoMap stages is invertible. Round-trip data
through the forward transform and its inverse to verify correctness.
#![allow(unused)] fn main() { use orlando_transducers::invert::IsoMap; use orlando_transducers::transducer::Transducer; use orlando_transducers::collectors::to_vec; // Two-step temperature transform: C -> F, then offset by 10. let forward = IsoMap::new(|c: f64| c * 9.0 / 5.0 + 32.0, |f: f64| (f - 32.0) * 5.0 / 9.0) .compose(IsoMap::new(|x: f64| x + 10.0, |y: f64| y - 10.0)); let celsius = vec![0.0, 37.0, 100.0]; let stored = to_vec(&forward, celsius.clone()); // ...later, recover the originals by inverting: let recovered = to_vec(&forward.invert(), stored); assert_eq!(recovered, celsius); }
Provenance: reconstructing what Filter dropped
For a lossy pipeline, trace records which inputs survived — the kept-mask is
the practical "inverse" when a true inverse is impossible.
#![allow(unused)] fn main() { use orlando_transducers::provenance::trace; use orlando_transducers::transforms::Filter; use orlando_transducers::transducer::Transducer; let records = vec![ (1, "alice", true), (2, "bob", false), (3, "carol", true), (4, "dave", false), ]; // Keep only active records. let active = Filter::new(|r: &(i32, &str, bool)| r.2); let (kept, t) = trace(&active, records.clone()); assert_eq!(kept.len(), 2); // The mask tells us exactly which inputs survived. assert_eq!(t.kept_mask(records.len()), vec![true, false, true, false]); }
Combining inversion and reflection
Because invert() produces another Describable pipeline, you can reflect
both directions from a single declaration.
#![allow(unused)] fn main() { use orlando_transducers::pipeline; use orlando_transducers::Describable; let p = pipeline!( iso_map(|x: i32| x * 2, |y: i32| y / 2) >> iso_map(|x: i32| x + 10, |y: i32| y - 10) ); let forward = p.describe(); let reverse = p.invert().describe(); // Each direction is two isoMap stages. assert_eq!(forward.len(), 2); assert_eq!(reverse.len(), 2); }