very experimental incremental-computing framework.
Original paper is from Umut A. Acar, you can find it here.
Heavily inspired by Jane Street's Incremental Computing Library, Incremental.
- Only some of the core features are implemented.
- Var
- Map
- Map2 (technically with the three above, you can already construct any arbitrary statically-structured graphs).
- Bind (allows you to add dynamism to graphs).
- No peripheral utilities implemented (hooks, sentinels, etc)
- Incremental computation (duh)
- Easy to use interface
- Strongly typed all the way, and Rust safe.
- Blazingly fast!
Here's a quick example.
use incrementars::prelude::{Incrementars, Observable};
pub fn main() {
let mut dag = Incrementars::new();
let length = dag.var(2.0);
let area = dag.map(length.as_input(), |x| {
println!("calculating area");
x * x
});
// on initial stabalization, area is calculated to be 4.
assert_eq!(area.observe(), 4.0);
length.set(3.0);
// right after setting, dag isn't stablized yet.
assert_eq!(area.observe(), 4.0);
dag.stablize();
assert_eq!(area.observe(), 9.0);
println!("introducing height...");
let height = dag.var(5.0);
let volume = dag.map2(area.as_input(), height.as_input(), |x, y| {
println!("calculating volume");
x * y
});
assert_eq!(volume.observe(), 45.0);
println!("setting height (this shouldn't trigger area calculation!)");
height.set(10.0);
dag.stablize();
assert_eq!(volume.observe(), 90.0);
println!("setting length (this should trigger area calculation)");
length.set(2.0);
dag.stablize();
assert_eq!(volume.observe(), 40.0);
}
I refactored the original implementation. The original implementation involves passing around two node handles (one for reads and one for writes), which at times can feel unergonomic / confusing. The new implementation is much more elegant in that it uses a single node handle for both reads and writes.
Internally, it uses Rc<RefCell>>
heavily. This is a challenge intrinsic to Rust given how ownerships & borrow checking
work.