A port of Jane Street's Incremental library.
sh
cargo add incremental
Basic usage:
```rust use incremental::IncrState;
let state = IncrState::new(); let variable = state.var(5); let times10 = variable.map(|num| num * 10); let observer = times10.observe();
// stabilise will propagate any changes state.stabilise(); let value = observer.value(); assert_eq!(value, 50);
// now mutate variable.set(10); state.stabilise();
// watch as var was propagated through the tree, and reached our observer assert_eq!(observer.value(), 100); ```
Subscriptions, and an illustration of how propagation stops when nodes produce the same value as last time:
```rust use incremental::{IncrState, Update};
// A little system to compute the absolute value of an input // Note that the input could change (e.g. 5 to -5), but the // output may stay the same (5 both times). let state = IncrState::new(); let variable = state.var(5i32); let absolute = variable.map(|num| num.abs()); let observer = absolute.observe();
// set up a subscription. use std::{cell::RefCell, rc::Rc}; let latest = Rc::new(RefCell::new(None)); let latestclone = latest.clone(); let subscriptiontoken = observer.subscribe(move |update| { *latestclone.borrowmut() = Some(update.cloned()); });
// initial stabilisation state.stabilise(); asserteq!(observer.value(), 5); asserteq!(latest.borrow().clone(), Some(Update::Initialised(5)));
// now mutate, but such that the output of abs() won't change
variable.set(-5);
state.stabilise();
// The subscription function was not called, because the absolute
node
// produced the same value as last time we stabilised.
asserteq!(latest.borrow().clone(), Some(Update::Initialised(5)));
asserteq!(observer.value(), 5);
// now mutate such that the output changes too variable.set(-10); state.stabilise(); // The observer did get a new value, and did call the subscription function asserteq!(latest.borrow().clone(), Some(Update::Changed(10))); asserteq!(observer.value(), 10);
// now unsubscribe. this also implicitly happens if you drop the observer, // but you can individually unsubscribe particular subscriptions if you wish. observer.unsubscribe(subscription_token); // dropping the observer also unloads any part of the computation graph // that was only running for the purposes of this particular observer drop(observer);
// now that the observer is dead, we can mutate the variable and nothing will // happen, like, at all. The absolute value will not be computed. variable.set(100000000); let recomputed = state.stats().recomputed; state.stabilise(); assert_eq!(recomputed, state.stats().recomputed); ```