incremental-rs

A port of Jane Street's Incremental library.

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); ```