Salsa-inspired incremental recomputation library with automatic dependency tracking, backdating, and durability-based verification skipping
{
"import": ["dowdiness/incr"]
}let rt = Runtime()
// Create inputs
let x = Input(rt, 10, label="x")
let y = Input(rt, 20, label="y")
// Create derived computations
let sum = Derived(rt, () => x.get() + y.get(), label="sum")
// `.get()` is only legal inside another derived computation.
// Outside the graph, use `read_or_abort()` or `read()`.
inspect(sum.read_or_abort(), content="30")
// Update an input — downstream derived values recompute on the next read
x.set(5)
inspect(sum.read_or_abort(), content="25")Note on the example above: It is nocheck because the snippet omits imports and a test wrapper. The same construction is checked in docs/target_api_examples.mbt.md and exercised end-to-end by tests/quickstart_test.mbt — if you edit the example, update those in lockstep.
| Need | Use |
|---|---|
| Default cached computation, especially if it may not be read after every input write | Derived |
| One memoized value per semantic key, created lazily | DerivedMap |
| Field-level invalidation inside a larger object | InputField |
| UI-facing value that should stay eagerly current after input writes | EagerDerived |
| Side effect that should run eagerly when dependencies change | Effect |
| Lazy derived value that must stay alive through downstream push subscribers or long-lived watches | ReachableDerived |
| Relational/fixpoint computation | Relation / MapRelation |
moon check # Type-check the workspace
moon build # Build the workspace
moon test # Run all workspace tests (~878 test blocks)
moon bench # Run benchmarks (always pass --release for representative numbers)match memo.get_result() {
Ok(value) => println("Got: " + value.to_string())
Err(err) => {
println("Cycle at cell " + err.cell().to_string())
println(err.format_path())
}
}let rt = Runtime()
let a = Signal(rt, 10)
let b = Signal(rt, 20)
let sum = Memo(rt, () => a.get() + b.get())
let observer = sum.observe()
inspect(observer.get(), content="30") // Computes and caches
a.set(15)
inspect(observer.get(), content="35") // Recomputes because `a` changed
observer.dispose()let rt = Runtime()
let source = Signal(rt, 10)
let by_id = MemoMap(rt, (id : Int) => source.get() + id)
inspect(by_id.get(1), content="11")
inspect(by_id.get(1), content="11") // cache hit for key=1
inspect(by_id.get(2), content="12") // separate memo for key=2let rt = Runtime()
let x = Signal(rt, 10)
let doubled = Memo(rt, () => x.get() * 2)let scope = Scope::new(rt)
let local = scope.signal(42)
let derived = scope.memo(fn() { local.get() * 2 })
// Component unmounts — one cleanup call
scope.dispose()let rt = Runtime()
let count = Signal(rt, 0)
count.set(5)
inspect(count.get(), content="5")let rt = Runtime()
let cell = TrackedCell(rt, 0, label="counter")
cell.set(5)
inspect(cell.get(), content="5")struct MyDb {
rt : @incr.Runtime
}
impl @incr.Database for MyDb with fn runtime(self) {
self.rt
}pub(open) trait Freshness {
fn is_fresh(Self) -> Bool
}impl Freshness for InputField[T]impl Freshness for ReachableDerived[T]pub(open) trait Readable {
fn is_up_to_date(Self) -> Bool
}fn[R : Readable] check(node : R) -> Bool {
node.is_up_to_date()
}impl Readable for HybridMemo[T]impl Readable for TrackedCell[T]let DURABILITY_COUNT : Intlet MAX_CYCLE_DISPLAY_STEPS : Intlet scope = Scope::new(rt)
let tracked = MyTracked::new(rt)
add_tracked(scope, tracked)
scope.dispose() // disposes all tracked cellsfn[Ctx : RuntimeContext, T : Eq] create_derived(ctx : Ctx, f : () -> T raise Failure, label? : String) -> Derived[T]fn[Ctx : RuntimeContext, K : Hash + Eq, V] create_derived_map(ctx : Ctx, f : (K) -> V raise Failure, label? : String) -> DerivedMap[K, V]fn[Ctx : RuntimeContext, T : Eq] create_eager_derived(ctx : Ctx, compute : () -> T) -> EagerDerived[T]fn[Db : Database, T : Eq] create_hybrid_memo(db : Db, f : () -> T raise Failure, label? : String) -> HybridMemo[T]fn[Ctx : RuntimeContext, T] create_input(ctx : Ctx, value : T, durability? : Durability, label? : String) -> Input[T]fn[Ctx : RuntimeContext, T] create_input_field(ctx : Ctx, value : T, durability? : Durability, label? : String) -> InputField[T]fn[Ctx : RuntimeContext, T : Eq] create_reachable_derived(ctx : Ctx, f : () -> T raise Failure, label? : String) -> ReachableDerived[T]fn[Db : Database, T] create_signal(db : Db, value : T, durability? : Durability, label? : String) -> Signal[T]fn[Db : Database, T] create_tracked_cell(db : Db, value : T, durability? : Durability, label? : String) -> TrackedCell[T]Install
Download zipSalsa-inspired incremental recomputation library with automatic dependency tracking, backdating, and durability-based verification skipping