Sign in

    dowdiness/incr/cells does not have a README file

    AcceptStatus

    pub(all) enum AcceptStatus {
    NoAccept
    AcceptedChanged
    AcceptedUnchanged
    RetainedDueToError
    } derive(Eq)

    Status of a single committed-revision transition through the accept gate. Mirrors the spec's state-machine Status column.

    AcceptedDerived

    pub struct AcceptedDerived[V, E] {
    // private fields
    }
    fn AcceptedDerived::AcceptedDerived(rt : Runtime, compute : () -> Result[V, E], label? : String) -> AcceptedDerived[V, E]

    A success-gated derived value. Construct with AcceptedDerived::AcceptedDerived (owns its candidate compute), AcceptedDerived::from_candidate (wraps an existing candidate Derived), or Scope::accepted_derived — all V : Eq. For candidate values that are not Eq but carry a Revision, use the BackdateEq tier (AcceptedDerived::accepted_memo / Scope::accepted_memo), which gates acceptance by revision identity.

    AcceptedDerived::accepted

    fn[V, E] AcceptedDerived::accepted(self : AcceptedDerived[V, E]) -> Result[V?,
    ReadError
    ]

    The last accepted value, or a read error. The retained accepted value is left untouched by a read error; the read returns the error in its place.

    This is an OUTSIDE-graph read: it surfaces the candidate read channel and, when called inside a tracked compute, records a dependency on the current candidate. An in-graph accepted-only consumer must use accepted_get / accepted_get_or_abort instead, which depend on the accepted projection and so re-run only when the accepted value changes — not on current-error churn.

    AcceptedDerived::accepted_changed_at

    fn[V, E] AcceptedDerived::accepted_changed_at(self : AcceptedDerived[V, E]) ->
    Revision

    Revision at which the accepted value last actually changed. Gated solely by V-equality on the accepted value: current-result churn (changing diagnostics, repeated errors, equal successful recomputations) never advances it. This is an incr graph Revision, not a domain document revision.

    AcceptedDerived::accepted_get

    fn[V, E] AcceptedDerived::accepted_get(self : AcceptedDerived[V, E]) -> Result[V?,
    ReadError
    ]

    INSIDE-graph read of the accepted value. Call this from a compute closure (Derived / EagerDerived / Effect): it reads the accepted projection, so it records a dependency on the accepted value, not the candidate. The consumer therefore re-runs only when the accepted value actually changes — never on current-error churn (Err(e1) -> Err(e2) with the accepted value retained backdates the projection). Returns Err only for a read failure of the accepted projection itself; the candidate's read channel is the concern of the outside-graph accepted / accepted_or_abort.

    AcceptedDerived::accepted_get_or_abort

    fn[V, E] AcceptedDerived::accepted_get_or_abort(self : AcceptedDerived[V, E]) -> V?

    Strict inside-graph companion to accepted_get; aborts on a read error (cycle / disposed), as in-graph strict reads do. Records a dependency on the accepted projection.

    AcceptedDerived::accepted_memo

    fn[V :
    BackdateEq
    +
    HasChangedAt
    , E : Eq] AcceptedDerived::accepted_memo(rt : Runtime, compute : () -> Result[V, E], label? : String) -> AcceptedDerived[V, E]

    BackdateEq companion of AcceptedDerived::AcceptedDerived: owns its candidate compute, accepts by revision identity. Requires V : BackdateEq,E : Eq.

    AcceptedDerived::accepted_or_abort

    fn[V, E] AcceptedDerived::accepted_or_abort(self : AcceptedDerived[V, E]) -> V?

    Strict accepted read; aborts on a read error.

    AcceptedDerived::current

    fn[V, E] AcceptedDerived::current(self : AcceptedDerived[V, E]) -> Result[Result[V, E],
    ReadError
    ]

    The current candidate result for the latest committed revision. The outer Result is the read channel (Cycle / Disposed); the inner Result[V, E] is the domain candidate.

    AcceptedDerived::current_or_abort

    fn[V, E] AcceptedDerived::current_or_abort(self : AcceptedDerived[V, E]) -> Result[V, E]

    Strict current read; aborts on a read error.

    AcceptedDerived::dispose

    fn[V, E] AcceptedDerived::dispose(self : AcceptedDerived[V, E]) -> Unit

    Disposes this AcceptedDerived. Idempotent. For from_candidate, the external candidate is NOT disposed (the caller owns it).

    AcceptedDerived::from_candidate

    fn[V : Eq, E] AcceptedDerived::from_candidate(candidate : Derived[Result[V, E]], label? : String) -> AcceptedDerived[V, E]

    Builds an AcceptedDerived over an existing candidate Derived. The candidate's lifecycle is owned by the CALLER — AcceptedDerived::dispose disposes only the fold, accepted projection, and gc anchor, not the candidate.

    AcceptedDerived::is_disposed

    fn[V, E] AcceptedDerived::is_disposed(self : AcceptedDerived[V, E]) -> Bool

    Returns true if this AcceptedDerived has been disposed.

    AcceptedDerived::snapshot

    A coherent snapshot (current + accepted + status), or a read error.

    AcceptedDerived::snapshot_or_abort

    fn[V, E] AcceptedDerived::snapshot_or_abort(self : AcceptedDerived[V, E]) -> AcceptedSnapshot[V, E]

    Strict snapshot read; aborts on a read error.

    AcceptedDerived::watch_accepted

    fn[V, E] AcceptedDerived::watch_accepted(self : AcceptedDerived[V, E]) -> Watch[V?]

    A persistent outside-graph anchor on the accepted projection. The caller owns the returned Watch and must dispose() it when done. It backdates with the accepted value, so it is woken only when the accepted value actually changes — not on current-error churn.

    AcceptedSnapshot

    pub struct AcceptedSnapshot[V, E] {
    current : Result[V, E]
    accepted : V?
    status : AcceptStatus
    } derive(Eq)

    A coherent view of one committed revision: the current candidate result, the retained accepted value, and the transition status. current carries the domain Result[V, E] (the read-error channel lives on the accessor return type, not here).

    Accumulator

    pub struct Accumulator[T] {
    // private fields
    }

    A side-channel collector for values pushed by memo compute closures.

    Typed buffers (per_memo, prev_push_sets) live on the handle; runtime holds only type-erased closures in SlotMeta. See the design doc docs/superpowers/specs/2026-04-19-accumulator-api-design.md.

    Accumulator::debug

    fn[T] Accumulator::debug(self : Accumulator[T]) -> String

    Debug representation.

    Accumulator::dispose

    fn[T] Accumulator::dispose(self : Accumulator[T]) -> Unit

    Disposes the accumulator. Idempotent. Clears typed buffers and removes reverse-index entries from rt.accumulator_contributions. The slot_id stays allocated — monotonic, never reused.

    Accumulator::id

    Returns the slot's unique ID.

    Accumulator::is_disposed

    fn[T] Accumulator::is_disposed(self : Accumulator[T]) -> Bool

    Returns true iff this accumulator has been disposed.

    Accumulator::label

    fn[T] Accumulator::label(self : Accumulator[T]) -> String?

    Returns the optional label provided at construction.

    Accumulator::new

    fn[T : Eq] Accumulator::new(rt~ : Runtime, label? : String) -> Accumulator[T]

    Creates a new accumulator bound to the given runtime.

    Constructs the typed buffer state and the SlotMeta closures that the runtime uses to drive snapshot/finalize/dispose phases.

    Accumulator::push

    fn[T] Accumulator::push(self : Accumulator[T], value : T) -> Unit raise Failure

    Appends a value to this accumulator, keyed by the currently-computing memo's CellId.

    Errors

    • fail if called outside a tracked compute context
    • fail if the top tracking frame is not a Memo (MVP restriction)
    • fail if this accumulator has been disposed
    • abort (via check_cross_runtime) if slot belongs to a different runtime

    CellInfo

    Structured metadata about a cell in the dependency graph.

    This structure provides a uniform view of both Signal and Memo cells. For signals, the dependencies array will be empty.

    Fields

    • label: Optional human-readable name for debugging and introspection
    • id: The unique identifier for this cell
    • changed_at: When this cell's value last actually changed
    • verified_at: When this cell was last confirmed up-to-date
    • durability: How often this cell is expected to change
    • dependencies: Cell IDs this cell depends on (empty for signals)

    Derived

    pub(all) struct Derived[T] {
    // private fields
    }
    fn Derived::Derived(rt : Runtime, compute : () -> T raise Failure, label? : String) -> Derived[T]

    Target-name lazy derived cell facade over Memo.

    Derived::accumulated

    fn[T, A] Derived::accumulated(self : Derived[T], acc : Accumulator[A]) -> Result[Array[A],
    ReadError
    ] raise Failure

    Tracked accumulator read intended for use inside a compute closure. Returns a defensive copy of the values self pushed during its last successful compute, forces verification of self, and stages a synthetic dep on the current frame. See Memo::accumulated.

    Derived::accumulated_or_abort

    fn[T, A] Derived::accumulated_or_abort(self : Derived[T], acc : Accumulator[A]) -> Array[A] raise Failure

    Tracked accumulator read that aborts on a graph mechanism failure.

    Derived::accumulated_peek

    fn[T, A] Derived::accumulated_peek(self : Derived[T], acc : Accumulator[A]) -> Array[A]

    Untracked accumulator read. Returns [] when the accumulator or target derived value is disposed; otherwise a defensive copy. See Memo::accumulated_peek.

    Derived::changed_at

    Revision at which this derived value's content last actually changed. Backdated by structural equality: when a recomputation produces a value equal to the previous one, changed_at is preserved rather than advanced. See Memo::changed_at.

    Derived::fallible

    fn[V : Eq, E : Eq] Derived::fallible(rt : Runtime, compute : () -> Result[V, E], label? : String) -> Derived[Result[V, E]]

    Creates a fallible lazy derived value: a recoverable, domain-specific failure is expressed in the value as Result[V, E], never raised. The compute is noraise, so the domain error is forced into the value, where it is cached, change-detected (Eq), and replayed like any other value; reads surface only graph failures (cycles), never an uncatchable abort. A raise Failure from a plain Derived compute is a defect, not a domain error. See docs/design/specs/2026-05-28-honest-read-error-ownership.md.

    Derived::get

    fn[T] Derived::get(self : Derived[T]) -> Result[T,
    ReadError
    ]

    Strict graph read. Requires an active tracked context.

    Derived::get_or_abort

    fn[T] Derived::get_or_abort(self : Derived[T]) -> T

    Strict graph read that aborts on invalid context or any read error.

    Derived::id

    Returns the unique cell identifier for this derived value. Stable across reads — useful for graph-shape probes (gc anchoring, edge inspection) where a cell needs an identity independent of its value.

    Derived::is_disposed

    fn[T] Derived::is_disposed(self : Derived[T]) -> Bool

    Returns true if this derived value has been disposed.

    Derived::is_fresh

    fn[T] Derived::is_fresh(self : Derived[T]) -> Bool

    Returns whether this derived value is verified at the current revision.

    Derived::observe

    fn[T] Derived::observe(self : Derived[T]) -> Observer[T]

    Acquires an Observer that roots this derived cell against gc. The caller must dispose() the returned observer when done; otherwise the cell (and its dependency cone) stays pinned indefinitely. See Memo::observe for the underlying semantics.

    Derived::read

    fn[T] Derived::read(self : Derived[T]) -> Result[T,
    ReadError
    ]

    Permissive read. Works outside the graph and records a dependency if tracked.

    Derived::read_or_abort

    fn[T] Derived::read_or_abort(self : Derived[T]) -> T

    Permissive read that aborts on any read error.

    Derived::watch

    fn[T] Derived::watch(self : Derived[T]) -> Watch[T]

    Creates a long-lived outside-graph reader that returns read errors.

    DerivedAbortedEvent

    pub(all) struct DerivedAbortedEvent {
    cell_id :
    CellId

    elapsed_ns : Int64
    started_revision :
    Revision

    error : Error
    }

    Event emitted when a derived recompute raises a catchable error.

    DerivedCompletedEvent

    pub(all) struct DerivedCompletedEvent {
    cell_id :
    CellId

    elapsed_ns : Int64
    started_revision :
    Revision

    verified_at :
    Revision

    changed_at :
    Revision

    backdated : Bool
    }

    Event emitted when a derived recompute completes successfully.

    DerivedEnteringEvent

    pub(all) struct DerivedEnteringEvent {
    cell_id :
    CellId

    started_revision :
    Revision

    }

    Event emitted when a derived recompute starts.

    DerivedEvent

    pub(all) enum DerivedEvent {
    EnteringCompute(DerivedEnteringEvent)
    Completed(DerivedCompletedEvent)
    Aborted(DerivedAbortedEvent)
    }

    Derived recompute lifecycle event delivered by Runtime::on_derived_event.

    DerivedMap

    pub(all) struct DerivedMap[K, V] {
    // private fields
    }
    fn DerivedMap::DerivedMap(rt : Runtime, compute : (K) -> V raise Failure, label? : String) -> DerivedMap[K, V]

    Target-name keyed derived facade over MemoMap.

    DerivedMap::cache_len

    fn[K, V] DerivedMap::cache_len(self : DerivedMap[K, V]) -> Int

    Returns the number of cached entries.

    DerivedMap::clear_cache

    fn[K, V] DerivedMap::clear_cache(self : DerivedMap[K, V]) -> Unit

    Clears all cached entries.

    DerivedMap::fallible

    fn[K : Hash + Eq, V, E] DerivedMap::fallible(rt : Runtime, compute : (K) -> Result[V, E], label? : String) -> DerivedMap[K, Result[V, E]]

    Creates a fallible keyed derived map: a per-key recoverable domain failure is expressed in the value as Result[V, E], never raised. The compute is noraise. See Derived::fallible and docs/design/specs/2026-05-28-honest-read-error-ownership.md.

    DerivedMap::get

    fn[K : Hash + Eq, V : Eq] DerivedMap::get(self : DerivedMap[K, V], key : K) -> Result[V,
    ReadError
    ]

    Strict graph read for key. Requires an active tracked context.

    DerivedMap::get_or_abort

    fn[K : Hash + Eq, V : Eq] DerivedMap::get_or_abort(self : DerivedMap[K, V], key : K) -> V

    Strict graph read for key that aborts on invalid context or any read error.

    DerivedMap::has_cached

    fn[K : Hash + Eq, V] DerivedMap::has_cached(self : DerivedMap[K, V], key : K) -> Bool

    Returns whether a cached entry exists for key.

    DerivedMap::read

    fn[K : Hash + Eq, V : Eq] DerivedMap::read(self : DerivedMap[K, V], key : K) -> Result[V,
    ReadError
    ]

    Permissive read for key. Records a dependency if tracked.

    DerivedMap::read_or

    fn[K : Hash + Eq, V : Eq] DerivedMap::read_or(self : DerivedMap[K, V], key : K, fallback : V) -> V

    Returns the value for key, or fallback if a read error is detected.

    DerivedMap::read_or_abort

    fn[K : Hash + Eq, V : Eq] DerivedMap::read_or_abort(self : DerivedMap[K, V], key : K) -> V

    Permissive read for key that aborts on any read error.

    DerivedMap::read_or_else

    fn[K : Hash + Eq, V : Eq] DerivedMap::read_or_else(self : DerivedMap[K, V], key : K, fallback : (
    ReadError
    ) -> V) -> V

    Returns the value for key, or computes a fallback from the read error.

    DerivedMap::sweep_cache

    fn[K : Hash + Eq, V] DerivedMap::sweep_cache(self : DerivedMap[K, V]) -> Int

    Removes cached entries whose underlying cells have been disposed.

    EagerDerived

    pub(all) struct EagerDerived[T] {
    // private fields
    }
    fn EagerDerived::EagerDerived(rt : Runtime, compute : () -> T) -> EagerDerived[T]

    Target-name eager derived cell facade over Reactive.

    EagerDerived::get

    fn[T] EagerDerived::get(self : EagerDerived[T]) -> T

    Strict graph read. Requires an active tracked context.

    EagerDerived::id

    Returns the unique cell identifier for this eager derived value. Stable across reads — useful for graph-shape probes (gc anchoring, edge inspection) where a cell needs an identity independent of its value.

    EagerDerived::read

    fn[T] EagerDerived::read(self : EagerDerived[T]) -> T

    Permissive read. Works outside the graph and records a dependency if tracked.

    EagerDerived::watch

    fn[T] EagerDerived::watch(self : EagerDerived[T]) -> Watch[T]

    Creates a long-lived outside-graph reader for this eager derived value.

    Effect

    pub struct Effect {
    // private fields
    }

    User-facing push-mode effect.

    Runs the provided function immediately on creation to establish dependencies, then re-runs whenever any dependency changes.

    Effect::dispose

    fn Effect::dispose(self : Effect) -> Unit

    Removes this effect from the dependency graph.

    Effect::id

    Returns the unique identifier for this effect cell.

    Effect::is_disposed

    fn Effect::is_disposed(self : Effect) -> Bool

    Returns true if this effect has been disposed.

    Effect::new

    fn Effect::new(rt : Runtime, f : () -> Unit) -> Effect

    Creates a new push-mode effect.

    f is invoked immediately to establish dependencies and run the initial side effect. It is re-run by push propagation whenever any dependency changes.

    FunctionalRelation

    pub(all) struct FunctionalRelation[K, V] {
    // private fields
    }
    fn FunctionalRelation::FunctionalRelation(rt : Runtime, merge? : (V, V) -> V, label? : String) -> FunctionalRelation[K, V]

    A Datalog functional relation: a key-value map with delta tracking for fixpoint evaluation.

    Unlike Relation[T] (a HashSet), FunctionalRelation[K, V] uses @hashmap.HashMap[K, V] and supports replacing values for existing keys.

    Three layers:
    • current — materialized post-drain (readable via get()/iter())
    • delta — frontier for current fixpoint iteration
    • staged_delta — staging buffer during fixpoint

    • insert() adds to frontier delta outside fixpoint, staged delta during fixpoint
    • get() reads current (the materialized post-drain map)
    • iter() iterates current; records a dependency for pull verification
    • delta_iter() iterates delta; used by rule bodies

    FunctionalRelation::delta_iter

    fn[K, V] FunctionalRelation::delta_iter(self : FunctionalRelation[K, V]) -> Iter[(K, V)]

    Iterates over the delta set (new/updated entries not yet drained to current).

    Used by rule bodies to read only the new entries produced in the previous fixpoint iteration.

    FunctionalRelation::dispose

    fn[K, V] FunctionalRelation::dispose(self : FunctionalRelation[K, V]) -> Unit

    Disposes this functional relation, clearing its maps and marking it as Disposed.

    FunctionalRelation::get

    fn[K : Hash + Eq, V] FunctionalRelation::get(self : FunctionalRelation[K, V], key : K) -> V?

    Looks up a key in the current (materialized) map.

    Values in delta are NOT visible via get until after fixpoint() drains them. Like iter(), this records a dependency for pull verification.

    FunctionalRelation::id

    Returns the CellId for this functional relation.

    FunctionalRelation::insert

    fn[K : Hash + Eq, V : Eq] FunctionalRelation::insert(self : FunctionalRelation[K, V], key : K, value : V) -> Bool

    Inserts a key-value pair into the delta set.

    Outside fixpoint(), inserts go to the current frontier delta. During fixpoint(), inserts go to the staged delta for the next iteration.

    If a merge function is provided, it is applied when the key already has an effective value: merge(old, new). If the merged result equals the old effective value, the insert is a no-op.

    Returns true if the effective value changed, false if it was a no-op.

    FunctionalRelation::is_disposed

    fn[K, V] FunctionalRelation::is_disposed(self : FunctionalRelation[K, V]) -> Bool

    Returns true if this functional relation has been disposed.

    FunctionalRelation::iter

    fn[K, V] FunctionalRelation::iter(self : FunctionalRelation[K, V]) -> Iter[(K, V)]

    Iterates over the current (materialized) map as (key, value) pairs.

    Records a dependency so pull memos that call iter() automatically re-verify when the functional relation changes after a fixpoint().

    FunctionalRelation::new

    fn[K : Hash + Eq, V] FunctionalRelation::new(rt : Runtime, merge? : (V, V) -> V, label? : String) -> FunctionalRelation[K, V]

    HybridMemo

    pub(all) struct HybridMemo[T] {
    // private fields
    } derive(
    Debug
    )
    fn HybridMemo::HybridMemo(rt : Runtime, compute : () -> T raise Failure, label? : String) -> HybridMemo[T]

    User-facing hybrid memo cell.

    Verifies lazily on get(). When verified_at >= current_revision, get() returns the cached value immediately without any dep walk. Staleness is detected purely via the revision counter — no dirty flag.

    HybridMemo::dispose

    fn[T] HybridMemo::dispose(self : HybridMemo[T]) -> Unit

    Disposes this hybrid memo, removing it from all upstream subscriber sets and marking its slot as Disposed. After disposal, calling get() on this cell or any cell that depends on it will abort.

    HybridMemo::get

    fn[T : Eq] HybridMemo::get(self : HybridMemo[T]) -> T

    Returns the memoized value, recomputing if necessary.

    Must be called inside a tracked context (another memo's compute function). Outside a compute function, use the target facade ReachableDerived::read(), ReachableDerived::read_or_abort(), or ReachableDerived::watch().

    HybridMemo::id

    Returns the unique identifier for this hybrid memo.

    HybridMemo::is_disposed

    fn[T] HybridMemo::is_disposed(self : HybridMemo[T]) -> Bool

    Returns true if this hybrid memo has been disposed.

    HybridMemo::is_up_to_date

    fn[T] HybridMemo::is_up_to_date(self : HybridMemo[T]) -> Bool

    Returns true if the hybrid memo's value is verified at the current revision.

    HybridMemo::new

    fn[T : Eq] HybridMemo::new(rt : Runtime, compute : () -> T raise Failure, label? : String) -> HybridMemo[T]

    Creates a new hybrid memo with the given compute function.

    HybridMemo::observe

    fn[T : Eq] HybridMemo::observe(self : HybridMemo[T]) -> Observer[T]

    Creates an observer for a HybridMemo cell.

    Input

    pub(all) struct Input[T] {
    // private fields
    }
    fn Input::Input(rt : Runtime, initial : T, durability? :
    Durability
    , label? : String) -> Input[T]

    Target-name input cell facade over Signal.

    Input::force_set

    fn[T] Input::force_set(self : Input[T], new_value : T) -> Unit

    Sets the input to a value and always bumps the relevant revision.

    Input::get

    fn[T] Input::get(self : Input[T]) -> T

    Returns the current value and records a dependency when tracked.

    Input::id

    Returns the unique cell identifier for this input. Stable across reads — useful for graph-shape probes and cross-runtime identity checks (input.id().runtime_id).

    Input::is_fresh

    fn[T] Input::is_fresh(self : Input[T]) -> Bool

    Returns true. Inputs are directly-set cells and are always fresh.

    Input::peek

    fn[T] Input::peek(self : Input[T]) -> T

    Returns the current value without recording a dependency.

    Input::set

    fn[T : Eq] Input::set(self : Input[T], new_value : T) -> Unit

    Sets the input to a new value, skipping same-value updates.

    InputField

    pub(all) struct InputField[T] {
    // private fields
    }
    fn InputField::InputField(rt : Runtime, initial : T, durability? :
    Durability
    , label? : String) -> InputField[T]

    Target-name field-level input facade over TrackedCell.

    InputField::as_tracked_cell

    fn[T] InputField::as_tracked_cell(self : InputField[T]) -> TrackedCell[T]

    Returns the compatibility tracked-cell handle for interop.

    InputField::clear_on_change

    fn[T] InputField::clear_on_change(self : InputField[T]) -> Unit

    Removes this field's on_change callback.

    InputField::dispose

    fn[T] InputField::dispose(self : InputField[T]) -> Unit

    Disposes this field by disposing its underlying tracked cell.

    InputField::durability

    Returns the durability level configured at construction.

    InputField::force_set

    fn[T] InputField::force_set(self : InputField[T], new_value : T) -> Unit

    Sets the field to a value and always bumps the relevant revision.

    InputField::get

    fn[T] InputField::get(self : InputField[T]) -> T

    Returns the current value and records a dependency when tracked.

    InputField::id

    Returns the unique identifier for this field.

    InputField::is_disposed

    fn[T] InputField::is_disposed(self : InputField[T]) -> Bool

    Returns true if this field has been disposed.

    InputField::is_fresh

    fn[T] InputField::is_fresh(self : InputField[T]) -> Bool

    Returns true. Input fields are directly-set cells and are always fresh.

    InputField::on_change

    fn[T] InputField::on_change(self : InputField[T], f : (T) -> Unit) -> Unit

    Registers a callback that fires whenever this field's value changes.

    InputField::peek

    fn[T] InputField::peek(self : InputField[T]) -> T

    Returns the current value without recording a dependency.

    InputField::set

    fn[T : Eq] InputField::set(self : InputField[T], new_value : T) -> Unit

    Sets the field to a new value, skipping same-value updates.

    MapRelation

    pub(all) struct MapRelation[K, V] {
    // private fields
    }
    fn MapRelation::MapRelation(rt : Runtime, merge? : (V, V) -> V, label? : String) -> MapRelation[K, V]

    Target-name facade for a Datalog functional relation.

    MapRelation::delta_iter

    fn[K, V] MapRelation::delta_iter(self : MapRelation[K, V]) -> Iter[(K, V)]

    Iterates over the current delta layer.

    MapRelation::dispose

    fn[K, V] MapRelation::dispose(self : MapRelation[K, V]) -> Unit

    Disposes this map relation.

    MapRelation::get

    fn[K : Hash + Eq, V] MapRelation::get(self : MapRelation[K, V], key : K) -> V?

    Looks up a key in the current materialized map.

    MapRelation::id

    Returns the CellId for this map relation.

    MapRelation::insert

    fn[K : Hash + Eq, V : Eq] MapRelation::insert(self : MapRelation[K, V], key : K, value : V) -> Bool

    Inserts or updates a key-value pair in the map relation's delta layer.

    MapRelation::is_disposed

    fn[K, V] MapRelation::is_disposed(self : MapRelation[K, V]) -> Bool

    Returns true if this map relation has been disposed.

    MapRelation::iter

    fn[K, V] MapRelation::iter(self : MapRelation[K, V]) -> Iter[(K, V)]

    Iterates over the current materialized map.

    Memo

    pub(all) struct Memo[T] {
    // private fields
    } derive(
    Debug
    )
    fn Memo::Memo(rt : Runtime, compute : () -> T raise Failure, label? : String) -> Memo[T]

    A derived computation with automatic dependency tracking and memoization.

    Memos are the interior nodes of the dependency graph. They compute a value from other signals and memos, cache the result, and only recompute when their dependencies actually change.

    Key Features

    • Lazy evaluation: The compute function is not called until get().
    • Automatic tracking: Dependencies are discovered by intercepting get() calls.
    • Memoization: Results are cached; repeated get() calls return the cached value.
    • Backdating: If a recomputation produces the same value, downstream memos skip work.

    Example

    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()

    Memo::accumulated

    fn[T, A] Memo::accumulated(self : Memo[T], acc : Accumulator[A]) -> Result[Array[A],
    ReadError
    ] raise Failure

    Tracked read: returns the accumulated values pushed by self during its last successful compute, records a synthetic dep on the current compute frame on success, and forces verification of self via ensure_computed_untracked.

    The read channel reports graph mechanism failures as ReadError: cycles return Err(Cycle(_)), and a directly disposed target memo returns Err(Disposed(_)). A target compute that raises Failure is a defect and aborts, matching Derived::read.

    Disposed accumulators and static-Derived recompute use are programmer defects that raise Failure; cross-runtime misuse aborts via the runtime guard rather than surfacing as ReadError.

    Memo::accumulated_or_abort

    fn[T, A] Memo::accumulated_or_abort(self : Memo[T], acc : Accumulator[A]) -> Array[A] raise Failure

    Tracked accumulator read that aborts on a graph mechanism failure.

    Memo::accumulated_peek

    fn[T, A] Memo::accumulated_peek(self : Memo[T], acc : Accumulator[A]) -> Array[A]

    Untracked read: returns the accumulated values pushed by self during its last successful compute. Does NOT force verification and does NOT record a dependency. Returns a defensive copy.

    Permissive on disposal: returns [] if the accumulator or target memo is disposed. Matches Signal::peek semantics.

    Memo::accumulated_result

    fn[T, A] Memo::accumulated_result(self : Memo[T], acc : Accumulator[A]) -> Result[Array[A],
    ReadError
    ] raise Failure

    Compatibility alias for the Result-style accumulator read.

    Memo::changed_at

    fn[T] Memo::changed_at(self : Memo[T]) ->
    Revision

    Returns the revision when this memo's value last changed.

    This reflects backdating: if a recomputation produces the same value, changed_at is preserved from the previous computation.

    Returns

    The revision timestamp of the last actual value change

    Memo::clear_on_change

    fn[T] Memo::clear_on_change(self : Memo[T]) -> Unit

    Removes the on_change callback for this memo.

    Memo::dependencies

    fn[T] Memo::dependencies(self : Memo[T]) -> Array[
    CellId
    ]

    Returns the list of cell IDs this memo currently depends on.

    The dependency list is empty before the first computation and updates each time the memo recomputes (for dynamic dependencies).

    Returns

    A copy of the current dependency list. Returns empty array if the memo has never been computed.

    Example

    let x = Signal(rt, 1)
    let m = Memo(rt, () => x.get() * 2)
    let observer = m.observe()
    observer.get() |> ignore
    observer.dispose()
    inspect(m.dependencies().contains(x.id()), content="true")

    Memo::dispose

    fn[T] Memo::dispose(self : Memo[T]) -> Unit

    Disposes this memo, releasing its resources and marking it as Disposed.

    After disposal, calling get() will abort. Disposal is idempotent.

    Memo::get

    fn[T] Memo::get(self : Memo[T]) -> T

    Returns the memoized value, recomputing if necessary.

    Must be called inside a tracked context (another memo's compute function). Outside a compute function, use the target facade Derived::read(), Derived::read_or_abort(), or Derived::watch().

    If called inside another memo's compute function, this automatically records a dependency.

    Returns

    The current (possibly recomputed) value

    Panics

    Aborts if called outside tracked context, or if a cycle is detected. Use get_result() for error-returning cycle handling, or the target facade read/watch methods for outside-the-graph reads.

    Memo::get_or

    fn[T] Memo::get_or(self : Memo[T], fallback : T) -> T

    Returns the memoized value, or fallback if a cycle is detected.

    This is a convenience wrapper around get_result() for call sites that prefer a non-panicking, non-Result API.

    Memo::get_or_else

    fn[T] Memo::get_or_else(self : Memo[T], fallback : (
    CycleError
    ) -> T) -> T

    Returns the memoized value, or computes a fallback value from the cycle error.

    This is a convenience wrapper around get_result() for graceful recovery.

    Memo::get_result

    fn[T] Memo::get_result(self : Memo[T]) -> Result[T,
    CycleError
    ]

    Returns the memoized value as a Result, recomputing if necessary.

    This is the error-returning version of get(). Use this method when you want to handle cycles gracefully instead of aborting.

    Returns

    Ok(value) with the current (possibly recomputed) value, or Err(CycleError) if a cycle is detected

    Example

    match memo.get_result() {
    Ok(value) => println("Got: " + value.to_string())
    Err(err) => {
    println("Cycle at " + err.cell().to_string())
    println("Path: " + err.format_path(rt))
    }
    }

    Memo::id

    Returns the unique identifier for this memo.

    The CellId can be used with Runtime::cell_info() to retrieve metadata, or to compare cell identities.

    Returns

    The cell identifier for this memo

    Memo::is_disposed

    fn[T] Memo::is_disposed(self : Memo[T]) -> Bool

    Returns true if this memo has been disposed.

    Memo::is_up_to_date

    fn[T] Memo::is_up_to_date(self : Memo[T]) -> Bool

    Returns true if the memo has a cached value and its verified_at matches the runtime's current revision. Returns false if the memo has never been computed (value is None), even at the initial revision.

    Memo::new

    fn[T : Eq] Memo::new(rt : Runtime, compute : () -> T raise Failure, label? : String) -> Memo[T]

    Creates a new memo with the given compute function.

    The compute function is called lazily on the first get() call, not at creation time. Every Signal::get() or Memo::get() call inside the compute function is automatically recorded as a dependency.

    Parameters

    • rt: The runtime that will manage this memo
    • compute: A function that computes the derived value

    Returns

    A new memo. The compute function has not yet been called.

    Type Constraints

    T must implement Eq for backdating: when the compute function returns the same value as before, the memo's changed_at timestamp is preserved, preventing unnecessary downstream recomputation.

    For other backdate strategies use:
    • Memo::new_memo — T : BackdateEq; compares changed_at revisions by default (O(1))
    • Memo::new_no_backdate — no constraint on T; never backdates

    Memo::new_memo

    fn[T :
    BackdateEq
    +
    HasChangedAt
    ] Memo::new_memo(rt : Runtime, compute : () -> T raise Failure, label? : String) -> Memo[T]

    Creates a memo using BackdateEq for change detection. T must implement BackdateEq (and its supertrait HasChangedAt). The backdate check calls BackdateEq::backdate_equal, not structural Eq.

    Memo::new_no_backdate

    fn[T] Memo::new_no_backdate(rt : Runtime, compute : () -> T raise Failure, label? : String) -> Memo[T]

    Creates a memo that never backdates: always marks as changed on recomputation. Use when the cost of comparison exceeds the benefit, or when downstream memos will always need to recompute anyway. No trait constraint on T.

    Memo::observe

    fn[T] Memo::observe(self : Memo[T]) -> Observer[T]

    Creates an observer for a Memo cell.

    Memo::on_change

    fn[T] Memo::on_change(self : Memo[T], f : (T) -> Unit) -> Unit

    Registers a callback that fires whenever this memo's value changes.

    The callback fires lazily — only during get() when recomputation produces a new value that differs from the previous value. It does not fire on the first computation (which establishes the initial value), and does not fire if the memo backdates (recomputes to the same value). Only one callback can be registered at a time; calling this again replaces the previous callback.

    Parameters

    • f: Called with the new value whenever this memo's value changes

    Memo::verified_at

    fn[T] Memo::verified_at(self : Memo[T]) ->
    Revision

    Returns the revision when this memo was last verified up-to-date.

    A memo is stale when verified_at < current_revision. Use this to understand when a memo was last checked.

    Returns

    The revision timestamp of the last verification

    MemoMap

    pub(all) struct MemoMap[K, V] {
    // private fields
    }
    fn MemoMap::MemoMap(rt : Runtime, compute : (K) -> V raise Failure, label? : String) -> MemoMap[K, V]

    A keyed memoization map backed by one Memo per key.

    MemoMap[K, V] provides a minimal parameterized-query pattern: each key gets its own lazily-created Memo[V], and subsequent reads of that key reuse the cached memoized value.

    Reading a key requires K : Hash + Eq and V : Eq, matching the underlying HashMap lookup and Memo value semantics.

    Example

    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=2

    MemoMap::clear

    fn[K, V] MemoMap::clear(self : MemoMap[K, V]) -> Unit

    Disposes every wrapper memo and empties the entry map.

    Unlike sweep(), which only removes entries whose memos are already disposed, clear() actively disposes each wrapper memo. Use this after a structural event (e.g. pipeline rebuild) that invalidates all cached keys — sweep() is insufficient because MemoMap's wrappers are Runtime-owned and are not reached by chain-scope disposal.

    MemoMap::contains

    fn[K : Hash + Eq, V] MemoMap::contains(self : MemoMap[K, V], key : K) -> Bool

    Returns true if a memo entry for key has already been created.

    MemoMap::get

    fn[K : Hash + Eq, V : Eq] MemoMap::get(self : MemoMap[K, V], key : K) -> V

    Returns the value for key, computing lazily on first access.

    Permissive read for test code and other top-level call sites. If called while a tracking frame is active, records the per-key dependency.

    MemoMap::get_or

    fn[K : Hash + Eq, V : Eq] MemoMap::get_or(self : MemoMap[K, V], key : K, fallback : V) -> V

    Returns the value for key, or fallback if a cycle is detected.

    MemoMap::get_or_else

    fn[K : Hash + Eq, V : Eq] MemoMap::get_or_else(self : MemoMap[K, V], key : K, fallback : (
    CycleError
    ) -> V) -> V

    Returns the value for key, or computes a fallback from the cycle error.

    MemoMap::get_result

    fn[K : Hash + Eq, V : Eq] MemoMap::get_result(self : MemoMap[K, V], key : K) -> Result[V,
    CycleError
    ]

    Returns the value for key as a Result, matching Memo::get_result.

    Permissive read for callers that want cycle errors as values.

    MemoMap::get_tracked

    fn[K : Hash + Eq, V : Eq] MemoMap::get_tracked(self : MemoMap[K, V], key : K) -> V

    Returns the value for key, aborting if called outside a tracked context.

    Stricter-contract peer of get(). Both methods record a dependency on the per-key memo when invoked from inside another memo's compute function; the difference is what happens at top level — get() reads silently without recording a dep, whereas get_tracked() aborts via Memo::get's tracking guard. Prefer this when the call site is expected to be tracking (catches "meant to call from inside a memo but I'm not" bugs).

    Caveat: MemoMap::clear() unconditionally disposes every per-key wrapper, so a tracked dependent recorded via this method will hit the disposed-dep guard on its next verify if clear() runs between reads. gc() is safe — it keeps wrapper memos alive as long as a tracked dependent is reachable from a root. The same caveat applies to get().

    MemoMap::length

    fn[K, V] MemoMap::length(self : MemoMap[K, V]) -> Int

    Returns the number of memo entries that have been created so far.

    MemoMap::new

    fn[K : Hash + Eq, V] MemoMap::new(rt : Runtime, compute : (K) -> V raise Failure, label? : String) -> MemoMap[K, V]

    Creates a new empty MemoMap.

    Entries are created lazily on first get/get_result per key.

    MemoMap::sweep

    fn[K : Hash + Eq, V] MemoMap::sweep(self : MemoMap[K, V]) -> Int

    Removes entries whose memos have been disposed (e.g. by gc()).

    Two-pass: collects keys to remove, then removes them (cannot mutate HashMap during iteration). Returns the number of entries swept.

    Call after Runtime::gc() to clean up stale entries.

    Observer

    pub struct Observer[T] {
    // private fields
    } derive(
    Debug
    )

    A typed keep-alive handle for reading computed values from outside the dependency graph.

    Observer is NOT a cell -- it has no SoA slot, no CellRef variant, and no subscriber set. It is a lightweight ref-counted handle that:
    1. Increments gc_root_counts for the target cell (keeps it alive during gc)
    2. Provides a typed getter for reading the cell's value
    3. Decrements gc_root_counts on dispose (allows gc to collect)

    Observer::dispose

    fn[T] Observer::dispose(self : Observer[T]) -> Unit

    Releases this observer's keep-alive hold on the target cell.

    Decrements gc_root_counts. When the last observer for a cell is disposed (count reaches 0), calls on_unobserve via CellLifecycle (no-op in Layer 4a; Layer 4b adds push suspension).

    Idempotent: disposing an already-disposed observer is a no-op.

    Observer::get

    fn[T] Observer::get(self : Observer[T]) -> T

    Returns the current value of the observed cell.

    For Memo and HybridMemo targets, this triggers pull verification if the cell is stale. For Reactive targets, returns the eagerly computed cached value.

    Observer::is_disposed

    fn[T] Observer::is_disposed(self : Observer[T]) -> Bool

    Returns true if this observer has been disposed.

    ReachableDerived

    pub(all) struct ReachableDerived[T] {
    // private fields
    }
    fn ReachableDerived::ReachableDerived(rt : Runtime, compute : () -> T raise Failure, label? : String) -> ReachableDerived[T]

    Target-name reachable lazy derived cell facade over HybridMemo.

    ReachableDerived::get

    Strict graph read. Requires an active tracked context.

    ReachableDerived::get_or_abort

    fn[T : Eq] ReachableDerived::get_or_abort(self : ReachableDerived[T]) -> T

    Strict graph read that aborts on invalid context or any read error.

    ReachableDerived::id

    Returns the unique cell identifier for this reachable derived value. Stable across reads — useful for graph-shape probes (gc anchoring, edge inspection) where a cell needs an identity independent of its value.

    ReachableDerived::is_fresh

    fn[T] ReachableDerived::is_fresh(self : ReachableDerived[T]) -> Bool

    Returns whether this reachable derived value is verified at the current revision.

    ReachableDerived::read

    Permissive read. Works outside the graph and records a dependency if tracked.

    ReachableDerived::read_or_abort

    fn[T : Eq] ReachableDerived::read_or_abort(self : ReachableDerived[T]) -> T

    Permissive read that aborts on any read error.

    ReachableDerived::watch

    fn[T : Eq] ReachableDerived::watch(self : ReachableDerived[T]) -> Watch[T]

    Creates a long-lived outside-graph reader that returns read errors.

    Reactive

    pub struct Reactive[T] {
    // private fields
    }

    User-facing push-mode reactive cell.

    Recomputed eagerly when any upstream cell changes. Value is cached; consecutive reads without upstream changes do not trigger recomputation.

    Reactive::dispose

    fn[T] Reactive::dispose(self : Reactive[T]) -> Unit

    Removes this reactive from the dependency graph.

    After disposal:
    • The reactive is removed from all source subscriber sets.
    • cell_index[id] is set to Disposed.
    • The SoA slot is added to free_push_reactives for future reuse.

    Reactive::get

    fn[T] Reactive::get(self : Reactive[T]) -> T

    Returns the current cached value of the reactive cell.

    Must be called inside a tracked context (a memo or reactive compute function). Outside a compute function, use the target facade EagerDerived::read() or EagerDerived::watch().

    Reactive::id

    Returns the unique cell identifier for this reactive.

    Reactive::is_disposed

    fn[T] Reactive::is_disposed(self : Reactive[T]) -> Bool

    Returns true if this reactive has been disposed.

    Reactive::new

    fn[T : Eq] Reactive::new(rt : Runtime, compute_fn : () -> T) -> Reactive[T]

    Creates a new push-mode reactive cell.

    compute_fn is invoked immediately to set the initial value and establish dependencies. Whenever any dependency changes, the runtime calls compute_fn again via push propagation (triggered by Signal::set or Runtime::batch).

    Type Erasure

    compute_fn is captured in a () -> Bool closure stored on PushReactiveData. The Bool indicates whether the value changed (enabling early cutoff for downstream push cells).

    Reactive::observe

    fn[T] Reactive::observe(self : Reactive[T]) -> Observer[T]

    Creates an observer for a Reactive cell.

    Relation

    pub(all) struct Relation[T] {
    // private fields
    }
    fn Relation::Relation(rt : Runtime, label? : String) -> Relation[T]

    A Datalog relation: a set of tuples with delta tracking for fixpoint evaluation.

    Relation[T] holds typed current and delta sets via Ref. The Runtime sees only type-erased closures on RelationData.

    • insert() adds to frontier delta outside fixpoint, and to staged delta during fixpoint
    • contains() checks current (the materialized post-drain set)
    • iter() iterates current; records a dependency for pull verification
    • delta_iter() iterates delta; used by rule bodies

    Relation::contains

    fn[T : Hash + Eq] Relation::contains(self : Relation[T], value : T) -> Bool

    Checks whether a fact exists in the current (materialized) set.

    Facts in delta are NOT visible via contains until after fixpoint() drains them. Like iter(), this records a dependency for pull verification.

    Relation::delta_iter

    fn[T] Relation::delta_iter(self : Relation[T]) -> Iter[T]

    Iterates over the delta set (new facts not yet drained to current).

    Used by rule bodies to read only the new facts produced in the previous fixpoint iteration.

    Relation::dispose

    fn[T] Relation::dispose(self : Relation[T]) -> Unit

    Disposes this relation, clearing its fact sets and marking it as Disposed.

    Relation::id

    Returns the CellId for this relation.

    Relation::insert

    fn[T : Hash + Eq] Relation::insert(self : Relation[T], value : T) -> Bool

    Inserts a fact into the delta set.

    Outside fixpoint(), inserts go to the current frontier delta. During fixpoint(), inserts go to the staged delta for the next iteration.

    Returns true if the fact was newly added, false if it already exists.

    Relation::is_disposed

    fn[T] Relation::is_disposed(self : Relation[T]) -> Bool

    Returns true if this relation has been disposed.

    Relation::iter

    fn[T] Relation::iter(self : Relation[T]) -> Iter[T]

    Iterates over the current (materialized) set.

    Records a dependency so pull memos that call iter() automatically re-verify when the relation changes after a fixpoint().

    Relation::new

    fn[T : Hash + Eq] Relation::new(rt : Runtime, label? : String) -> Relation[T]

    Runtime

    pub(all) struct Runtime {
    // private fields
    }
    fn Runtime::Runtime(on_change? : () -> Unit) -> Runtime

    Central coordinator for the incremental computation framework.

    The Runtime manages all bookkeeping for dependency tracking, revision counting, and batch operations. Every Signal and Memo is associated with exactly one Runtime.

    Pointer chase tradeoff

    All sub-struct field accesses (e.g. self.core.revision.current_revision) require one extra pointer dereference compared to the old flat layout. MoonBit structs are heap-allocated, so RuntimeCore is a separate heap object. In hot loops (verify dep-walk, push propagation), the sub-struct pointers are cache-hot since they are dereferenced on every iteration.

    Example

    let rt = Runtime()

    let x = Signal(rt, 10)

    let doubled = Memo(rt, () => x.get() * 2)

    Runtime::batch

    fn Runtime::batch(self : Runtime, f : () -> Unit raise?) -> Unit raise?

    Executes a closure with batched signal updates.

    All Signal::set calls inside the closure are deferred. At batch end, a single revision bump occurs for all changes. This provides:

    • Atomicity: Memos see either all changes or none
    • Efficiency: One verification pass instead of many
    • Revert detection: Setting a signal back to its original value is a no-op

    Batches can be nested. Only the outermost batch commits changes.

    Parameters

    • f: The closure to execute If f raises an error, pending writes in this batch are rolled back and the error is re-raised to the caller.

    Example

    rt.batch(() => {
    x.set(10)
    y.set(20)
    z.set(30)
    })
    // Single revision bump for all three changes

    Revert Detection

    rt.batch(() => {
    x.set(5) // Change from 0 to 5
    x.set(0) // Change back to 0
    })
    // No revision bump — net change is zero

    Abort Behavior

    MoonBit abort() is not catchable. If f aborts (rather than raises), cleanup cannot run and runtime state may be left inconsistent.

    Runtime::batch_result

    fn Runtime::batch_result(self : Runtime, f : () -> Unit raise?) -> Result[Unit, Error]

    Executes a batch and returns raised errors as Result instead of re-raising.

    This is a convenience wrapper around Runtime::batch for callers that prefer explicit result handling. Like Runtime::batch, this captures raised errors only; abort() is not recoverable here and still escapes Runtime::batch_result.

    Runtime::cell_info

    Returns structured metadata for any cell (signal or memo).

    This method provides uniform introspection access to cell metadata regardless of whether the cell is a Signal or Memo. It returns CellInfo containing the cell's ID, type, revision information, durability, and dependency list.

    Parameters

    • id: The CellId to query (obtained via Signal::id() or Memo::id())

    Returns

    • Some(CellInfo): If the cell ID is valid and points to an active cell
    • None: If the cell ID is out of bounds or points to an unused slot

    Usage

    let rt = Runtime()
    let sig = Signal(rt, 42)
    let memo = Memo(rt, () => sig.get() * 2)
    let _ = memo.get() // Force computation

    // Query signal metadata
    match rt.cell_info(sig.id()) {
    Some(info) => {
    println("Signal changed at: \{info.changed_at}")
    println("Dependencies: \{info.dependencies.length()}")
    }
    None => println("Cell not found")
    }

    // Query memo metadata
    match rt.cell_info(memo.id()) {
    Some(info) => {
    println("Memo durability: \{info.durability}")
    println("Depends on \{info.dependencies.length()} cells")
    }
    None => println("Cell not found")
    }

    Notes

    • The dependency array is a copy; modifying it does not affect the runtime
    • For signals, the dependencies array is always empty
    • For memos, dependencies are populated after the first computation
    • The method performs bounds checking and returns None for invalid IDs

    Runtime::clear_derived_event_listener

    fn Runtime::clear_derived_event_listener(self : Runtime) -> Unit raise Failure

    Clears the derived recompute lifecycle event listener.

    Runtime::clear_memo_event_listener

    #deprecated("Use Runtime::clear_derived_event_listener")
    fn Runtime::clear_memo_event_listener(self : Runtime) -> Unit raise Failure

    Deprecated alias of Runtime::clear_derived_event_listener.

    Runtime::clear_on_change

    fn Runtime::clear_on_change(self : Runtime) -> Unit

    Removes the on_change callback.

    Runtime::dependents

    Returns the cell IDs that depend on the given cell (reverse edges).

    This enables introspection of the dependency graph in both directions. The returned array is a snapshot; modifying it does not affect the runtime.

    Returns an empty array if the cell ID is invalid, disposed, out of bounds, or belongs to a different runtime — matching cell_info semantics.

    Parameters

    • id: The cell to query

    Returns

    Array of CellIds that have id in their dependency list, or empty array if the cell ID is not valid for this runtime or has been disposed

    Runtime::dispose_cell

    fn Runtime::dispose_cell(self : Runtime, cell_id :
    CellId
    ) -> Unit

    Disposes a cell by CellId, dispatching via CellLifecycle trait. Pure-state guards (cross-runtime, already-disposed, phase/tracking, and gc_root_counts cleanup) live in @kernel; the trait dispatch on self.cell_lifecycle stays on Runtime because the CellLifecycle trait is defined over Runtime (out of R1 scope to retype).

    Runtime::dispose_rule

    fn Runtime::dispose_rule(self : Runtime, rule_id :
    RuleId
    ) -> Unit

    Disposes a rule cell. Public because RuleId has no runtime reference, so the user must call rt.dispose_rule(rule_id) directly.

    Runtime::fixpoint

    fn Runtime::fixpoint(self : Runtime) -> Unit

    Runs fixpoint evaluation and publishes any resulting cell changes.

    Runtime::gc

    fn Runtime::gc(self : Runtime) -> Unit

    Runs mark-and-sweep garbage collection on the dependency graph.

    Dispose dispatch is injected as a closure closing over self.dispose_cell, so per-kind CellLifecycle dispatch remains reachable from the kernel sweep loop.

    Runtime::gc_root_count

    fn Runtime::gc_root_count(self : Runtime, id :
    CellId
    ) -> Int

    Number of GC roots currently anchoring id — i.e. how many Watch or Observer handles or gc_root = Root cells reference it. Returns 0 for unknown or disposed cells (matches the Runtime::dependents soft-fail idiom).

    Phase 1 coordinator use: registration-time mode-1 check that each protected cell is observer-rooted before the editor is published.

    Runtime::id

    Returns this runtime's identity.

    Use it to ask "are these two runtimes the same?" (a.id() == b.id()) without allocating a probe cell to read a CellId's runtime_id. A RuntimeId is a debug / introspection identity, not a stable application key — see RuntimeId.

    Runtime::new

    fn Runtime::new(on_change? : () -> Unit) -> Runtime

    Creates a new runtime with an empty dependency graph.

    Parameters

    • on_change: Optional callback invoked whenever any signal changes (or at the end of a batch if values actually changed).

    Runtime::new_rule

    fn Runtime::new_rule(self : Runtime, input_relations : Array[
    CellId
    ], output_relations : Array[
    CellId
    ], apply_delta : () -> Unit, label? : String) ->
    RuleId

    Runtime::on_derived_event

    fn Runtime::on_derived_event(self : Runtime, f : (DerivedEvent) -> Unit) -> Unit raise Failure

    Registers a listener for derived recompute lifecycle events.

    Runtime::on_memo_event

    #deprecated("Use Runtime::on_derived_event")
    fn Runtime::on_memo_event(self : Runtime, f : (DerivedEvent) -> Unit) -> Unit raise Failure

    Deprecated alias of Runtime::on_derived_event.

    Runtime::read

    #deprecated("Use target facade read_or_abort()/watch() methods; Runtime::read is legacy compatibility")
    fn[T] Runtime::read(self : Runtime, memo : Memo[T]) -> T

    One-shot observe: observe a memo, read its value, dispose the observer.

    Convenience for cases where you need a single outside-the-graph read.

    Panics

    Aborts if the memo has been disposed.

    Runtime::read_hybrid

    #deprecated("Use target facade read_or_abort()/watch() methods; Runtime::read_hybrid is legacy compatibility")
    fn[T : Eq] Runtime::read_hybrid(self : Runtime, memo : HybridMemo[T]) -> T

    One-shot observe for HybridMemo.

    Runtime::read_reactive

    #deprecated("Use target facade read()/watch() methods; Runtime::read_reactive is legacy compatibility")
    fn[T] Runtime::read_reactive(self : Runtime, reactive : Reactive[T]) -> T

    One-shot observe for Reactive.

    Runtime::record_batch_rollback

    fn Runtime::record_batch_rollback(self : Runtime, cell_id :
    CellId
    , rollback : () -> Unit) -> Unit

    Registers a rollback action for the current batch frame.

    Only the first rollback registration per cell_id in the frame is stored. If not currently in a batch, this is a no-op.

    Runtime::set_on_change

    fn Runtime::set_on_change(self : Runtime, f : () -> Unit) -> Unit

    Registers a callback that fires whenever a revision bump occurs.

    Scope

    pub struct Scope {
    // private fields
    }

    Hierarchical cell ownership with bulk disposal.

    Scope owns cells and child scopes. Disposing a scope disposes all owned cells and children recursively. This enables UI component lifecycle patterns: create cells during mount, dispose the scope on unmount.

    Disposal Order

    1. Children (bottom-up, recursively)
    2. Dispose hooks (empty in Layer 2; populated by Scope::observe in Layer 4)
    3. Owned cells

    Example

    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()
    impl Debug for Scope

    Scope::accepted_derived

    fn[V : Eq, E : Eq] Scope::accepted_derived(self : Scope, compute : () -> Result[V, E], label? : String) -> AcceptedDerived[V, E]

    Scope-owned convenience, mirroring Scope::derived. The returned AcceptedDerived lives in a child scope, so disposing self disposes it.

    Scope::accepted_memo

    fn[V :
    BackdateEq
    +
    HasChangedAt
    , E : Eq] Scope::accepted_memo(self : Scope, compute : () -> Result[V, E], label? : String) -> AcceptedDerived[V, E]

    BackdateEq companion of Scope::accepted_derived: scope-owned, accepts by revision identity. Requires V : BackdateEq, E : Eq.

    Scope::accumulator

    fn[T : Eq] Scope::accumulator(self : Scope, label? : String) -> Accumulator[T]

    Creates an accumulator owned by this scope. The accumulator is disposed automatically when the scope is disposed (via dispose_hooks).

    Scope::add_cell_ids

    fn Scope::add_cell_ids(self : Scope, ids : Array[
    CellId
    ]) -> Unit

    Registers an array of CellIds with this scope for bulk disposal.

    When the scope is disposed, all registered cells are disposed. This is the low-level building block for add_tracked(scope, tracked).

    Scope::add_observer

    fn[T] Scope::add_observer(self : Scope, obs : Observer[T]) -> Observer[T]

    Registers an observer with this scope for automatic disposal.

    When the scope is disposed, the observer is disposed in the dispose_hooks phase (step 2 of disposal order — after children, before owned cells). This ensures observers are cleaned up before their potential targets.

    Returns the observer for immediate use.

    Scope::add_watch

    fn[T] Scope::add_watch(self : Scope, watch : Watch[T]) -> Watch[T]

    Registers a watch with this scope for automatic disposal.

    When the scope is disposed, the watch is disposed in the dispose_hooks phase (step 2 of disposal order — after children, before owned cells).

    Returns the watch for immediate use.

    Scope::child

    fn Scope::child(self : Scope) -> Scope

    Creates a child scope owned by this scope.

    Disposing the parent will dispose this child first (bottom-up order).

    Scope::derived

    fn[T : Eq] Scope::derived(self : Scope, f : () -> T raise Failure, label? : String) -> Derived[T]

    Creates a lazy derived facade owned by this scope.

    Scope::derived_map

    fn[K : Hash + Eq, V] Scope::derived_map(self : Scope, compute : (K) -> V raise Failure, label? : String) -> DerivedMap[K, V]

    Creates a keyed derived facade whose cached entries are cleared on dispose.

    Scope::dispose

    fn Scope::dispose(self : Scope) -> Unit

    Disposes this scope: children first, then hooks, then owned cells.

    Idempotent — disposing an already-disposed scope is a no-op.

    Scope::eager_derived

    fn[T : Eq] Scope::eager_derived(self : Scope, compute_fn : () -> T) -> EagerDerived[T]

    Creates an eager derived facade owned by this scope.

    Scope::effect

    fn Scope::effect(self : Scope, f : () -> Unit) -> Effect

    Creates an effect owned by this scope.

    Scope::hybrid_memo

    fn[T : Eq] Scope::hybrid_memo(self : Scope, f : () -> T raise Failure, label? : String) -> HybridMemo[T]

    Creates a hybrid memo owned by this scope.

    Scope::input

    fn[T] Scope::input(self : Scope, initial : T, durability? :
    Durability
    , label? : String) -> Input[T]

    Creates an input facade owned by this scope.

    Scope::input_field

    fn[T] Scope::input_field(self : Scope, initial : T, durability? :
    Durability
    , label? : String) -> InputField[T]

    Creates an input-field facade owned by this scope.

    Scope::is_disposed

    fn Scope::is_disposed(self : Scope) -> Bool

    Returns true if this scope has been disposed.

    Scope::memo

    fn[T : Eq] Scope::memo(self : Scope, f : () -> T raise Failure, label? : String) -> Memo[T]

    Creates a memo owned by this scope.

    Scope::new

    fn Scope::new(rt : Runtime) -> Scope

    Creates a new root scope.

    Scope::reachable_derived

    fn[T : Eq] Scope::reachable_derived(self : Scope, f : () -> T raise Failure, label? : String) -> ReachableDerived[T]

    Creates a reachable lazy derived facade owned by this scope.

    Scope::reactive

    fn[T : Eq] Scope::reactive(self : Scope, compute_fn : () -> T) -> Reactive[T]

    Creates a reactive owned by this scope.

    Scope::signal

    fn[T] Scope::signal(self : Scope, initial : T, durability? :
    Durability
    , label? : String) -> Signal[T]

    Creates a signal owned by this scope.

    Signal

    pub(all) struct Signal[T] {
    // private fields
    } derive(
    Debug
    )
    fn Signal::Signal(rt : Runtime, initial : T, durability? :
    Durability
    , label? : String) -> Signal[T]

    An input cell with an externally-set value.

    Signals are the leaves of the dependency graph — values you control directly. The T: Eq constraint enables same-value optimization: setting a signal to its current value is a no-op (no revision bump, no downstream recomputation).

    Example

    let rt = Runtime()
    let count = Signal(rt, 0)
    count.set(5)
    inspect(count.get(), content="5")

    Signal::clear_on_change

    fn[T] Signal::clear_on_change(self : Signal[T]) -> Unit

    Removes the on_change callback for this signal.

    Signal::dispose

    fn[T] Signal::dispose(self : Signal[T]) -> Unit

    Disposes this signal, releasing its resources and marking it as Disposed.

    After disposal, calling get() or set() will abort. Disposal is idempotent — calling it multiple times is a no-op.

    Signal::durability

    Returns the durability level of this signal.

    Durability indicates how often this signal is expected to change:
    • High: Rarely changes (e.g., configuration)
    • Medium: Moderately stable
    • Low: Frequently changes (e.g., user input)

    Returns

    The durability level set at construction time

    Signal::get

    fn[T] Signal::get(self : Signal[T]) -> T

    Returns the current value of the signal.

    If called inside a memo's compute function, this automatically records a dependency from the memo to this signal. When the signal changes, the memo will know to reverify.

    Returns

    The current value of the signal

    Signal::get_result

    fn[T] Signal::get_result(self : Signal[T]) -> Result[T,
    CycleError
    ]

    Returns the current value of the signal as a Result.

    This method exists for API consistency with Memo::get_result(). Since signals cannot have cycles (they are input cells with no dependencies), this method always returns Ok(value).

    Returns

    Ok(value) with the current value of the signal

    Signal::id

    Returns the unique identifier for this signal.

    The CellId can be used with Runtime::cell_info() to retrieve metadata, or to compare cell identities.

    Returns

    The cell identifier for this signal

    Example

    let sig = Signal(rt, 42)
    let id = sig.id()
    match rt.cell_info(id) {
    Some(info) => println("Signal changed at: " + info.changed_at.to_string())
    None => ()
    }

    Signal::is_disposed

    fn[T] Signal::is_disposed(self : Signal[T]) -> Bool

    Returns true if this signal has been disposed.

    Signal::is_up_to_date

    fn[T] Signal::is_up_to_date(self : Signal[T]) -> Bool

    Returns true. Signals are always up-to-date since they are input cells with directly-set values.

    Signal::new

    fn[T] Signal::new(rt : Runtime, initial : T, durability? :
    Durability
    , label? : String) -> Signal[T]

    Creates a new signal with the given initial value.

    Parameters

    • rt: The runtime that will manage this signal
    • initial: The initial value of the signal
    • durability: How often this signal is expected to change (default: Low)
    • label: An optional human-readable name for debugging and cycle error output

    Returns

    A new signal containing the initial value

    Example

    let count = Signal(rt, 0)

    let config = Signal(rt, "prod", durability=High)

    let named = Signal(rt, 0, label="count")

    Signal::on_change

    fn[T] Signal::on_change(self : Signal[T], f : (T) -> Unit) -> Unit

    Registers a callback that fires whenever this signal's value changes.

    The callback receives the new value. It fires after the value is updated but before Runtime::fire_on_change(). Only one callback can be registered at a time; calling this again replaces the previous callback.

    Note: When using set_unconditional, the callback fires even if the new value equals the current value, since set_unconditional bypasses the equality check.

    Parameters

    • f: Called with the new value whenever this signal changes

    Signal::peek

    fn[T] Signal::peek(self : Signal[T]) -> T

    Returns the current value of the signal without recording a dependency.

    Use peek() to read a signal's value from outside the dependency graph (e.g., in event handlers, logging, or tests). Unlike get(), this never records a dependency even when called inside a compute function.

    Returns

    The current value of the signal

    Signal::set

    fn[T : Eq] Signal::set(self : Signal[T], new_value : T) -> Unit

    Sets the signal to a new value.

    If the new value equals the current value (via Eq), this is a no-op: no revision bump occurs, and downstream memos won't reverify.

    During a batch (Runtime::batch), the write is deferred. At batch end, only signals whose final value differs from the pre-batch value trigger a revision bump. This enables revert detection.

    Parameters

    • new_value: The new value to set

    Same-Value Optimization

    let s = Signal(rt, 5)
    s.set(5) // No-op: value unchanged, no revision bump
    s.set(6) // Bumps revision, memos depending on s will reverify

    Signal::set_unconditional

    fn[T] Signal::set_unconditional(self : Signal[T], new_value : T) -> Unit

    Sets the signal to a new value, always bumping the revision.

    Unlike set, this does not check for equality. Use this when you want to force downstream memos to reverify even if the value is the same, or when your type doesn't implement Eq.

    During a batch, the value is stored as pending and committed at batch end.

    Parameters

    • new_value: The new value to set

    TrackedCell

    pub(all) struct TrackedCell[T] {
    // private fields
    } derive(
    Debug
    )
    fn TrackedCell::TrackedCell(rt : Runtime, initial : T, durability? :
    Durability
    , label? : String) -> TrackedCell[T]

    A named, tracked input cell — a thin wrapper around Signal[T].

    TrackedCell provides the same functionality as Signal but is intended for use cases where you want a more structured, named input cell. All operations delegate directly to the inner Signal.

    Example

    let rt = Runtime()
    let cell = TrackedCell(rt, 0, label="counter")
    cell.set(5)
    inspect(cell.get(), content="5")

    TrackedCell::as_signal

    fn[T] TrackedCell::as_signal(self : TrackedCell[T]) -> Signal[T]

    Returns the inner Signal for interop with APIs that expect a Signal[T].

    Returns

    The underlying Signal[T] wrapped by this TrackedCell

    TrackedCell::clear_on_change

    fn[T] TrackedCell::clear_on_change(self : TrackedCell[T]) -> Unit

    Removes the on_change callback for this cell.

    TrackedCell::dispose

    fn[T] TrackedCell::dispose(self : TrackedCell[T]) -> Unit

    Disposes this tracked cell by disposing its inner signal.

    TrackedCell::durability

    Returns the durability level of this cell.

    Durability indicates how often this cell is expected to change:
    • High: Rarely changes (e.g., configuration)
    • Medium: Moderately stable
    • Low: Frequently changes (e.g., user input)

    Returns

    The durability level set at construction time

    TrackedCell::get

    fn[T] TrackedCell::get(self : TrackedCell[T]) -> T

    Returns the current value of the cell.

    If called inside a memo's compute function, this automatically records a dependency from the memo to this cell. When the cell changes, the memo will know to reverify.

    Returns

    The current value of the cell

    TrackedCell::get_result

    fn[T] TrackedCell::get_result(self : TrackedCell[T]) -> Result[T,
    CycleError
    ]

    Returns the current value of the cell as a Result.

    This method exists for API consistency with Memo::get_result(). Since TrackedCells cannot have cycles (they are input cells with no dependencies), this method always returns Ok(value).

    Returns

    Ok(value) with the current value of the cell

    TrackedCell::id

    Returns the unique identifier for this cell.

    The CellId can be used with Runtime::cell_info() to retrieve metadata, or to compare cell identities.

    Returns

    The cell identifier for this cell

    TrackedCell::is_disposed

    fn[T] TrackedCell::is_disposed(self : TrackedCell[T]) -> Bool

    Returns true if this tracked cell has been disposed.

    TrackedCell::is_up_to_date

    fn[T] TrackedCell::is_up_to_date(self : TrackedCell[T]) -> Bool

    Returns true. TrackedCells are always up-to-date since they are input cells with directly-set values.

    TrackedCell::new

    fn[T] TrackedCell::new(rt : Runtime, initial : T, durability? :
    Durability
    , label? : String) -> TrackedCell[T]

    Creates a new TrackedCell with the given initial value.

    Parameters

    • rt: The runtime that will manage this cell
    • initial: The initial value of the cell
    • durability: How often this cell is expected to change (default: Low)
    • label: An optional human-readable name for debugging and cycle error output

    Returns

    A new TrackedCell containing the initial value

    Example

    let cell = TrackedCell(rt, 42)

    let config = TrackedCell(rt, "prod", durability=High)

    let named = TrackedCell(rt, 0, label="counter")

    TrackedCell::on_change

    fn[T] TrackedCell::on_change(self : TrackedCell[T], f : (T) -> Unit) -> Unit

    Registers a callback that fires whenever this cell's value changes.

    The callback receives the new value. Only one callback can be registered at a time; calling this again replaces the previous callback.

    Parameters

    • f: Called with the new value whenever this cell changes

    TrackedCell::peek

    fn[T] TrackedCell::peek(self : TrackedCell[T]) -> T

    Returns the current value of the cell without recording a dependency.

    Delegates to Signal::peek() on the inner signal. Use from outside the dependency graph when you don't want to trigger recomputation.

    TrackedCell::set

    fn[T : Eq] TrackedCell::set(self : TrackedCell[T], value : T) -> Unit

    Sets the cell to a new value.

    If the new value equals the current value (via Eq), this is a no-op: no revision bump occurs, and downstream memos won't reverify.

    During a batch (Runtime::batch), the write is deferred. At batch end, only cells whose final value differs from the pre-batch value trigger a revision bump.

    Parameters

    • value: The new value to set

    TrackedCell::set_unconditional

    fn[T] TrackedCell::set_unconditional(self : TrackedCell[T], value : T) -> Unit

    Sets the cell to a new value, always bumping the revision.

    Unlike set, this does not check for equality. Use this when you want to force downstream memos to reverify even if the value is the same, or when your type doesn't implement Eq.

    Parameters

    • value: The new value to set

    Watch

    pub struct Watch[T] {
    // private fields
    } derive(
    Debug
    )

    A typed keep-alive handle that reads Result values from outside the graph.

    Watch has the same GC-rooting semantics as Observer, but preserves read errors as values for target-facade read APIs.

    Watch::dispose

    fn[T] Watch::dispose(self : Watch[T]) -> Unit

    Releases this watch's keep-alive hold on the target cell.

    Idempotent: disposing an already-disposed watch is a no-op.

    Watch::is_disposed

    fn[T] Watch::is_disposed(self : Watch[T]) -> Bool

    Returns true if this watch has been disposed.

    Watch::read

    fn[T] Watch::read(self : Watch[T]) -> Result[T,
    ReadError
    ]

    Returns the current value or a mechanism error (cycle / disposed) from the watched cell.

    Watch::read_or_abort

    fn[T] Watch::read_or_abort(self : Watch[T]) -> T

    Returns the current value, aborting if a read error is detected.