The change feed's registry and its accumulator — the leaf seam a settle files its relabelled region into, and the one place a listener list lives.
An application driving the KB otherwise learns that belief changed only by asking
again, which misses whatever happened between two asks and costs the most on the KBs
where the least is moving. Everything a feed needs is already computed: a settle
knows the region it relabelled and which of that region was believed when it
first touched it (jtms/touched / jtms/touched-in), and it throws both away. This
namespace catches them.
Why here. vaelii.impl.observe is the precedent and the shape is the same: a
choke point deep in the stack has to reach a consumer defined above it, so the
indirection is a leaf both can see. What differs is the altitude. observe's
observers fire on storage, which is what an alpha memory mirrors; a feed is about
belief, and belief is decided at settle time — an assert can store a sentex whose
label several later justifications settle. So nothing here is notified from
kb/create-sentex, and the region is the unit rather than the record.
What is split where. The registry, the accumulator and the reentrancy guard are
here, on the KB's :feed atom. Turning a region into the {:believed-added :believed-removed} entries a listener receives is vaelii.core's job — those are
preview's entry shapes, built from why-not and the supporting justifications, and
they belong beside the code that already renders them. core installs that renderer
with install-dispatch! when it loads, and deliver! calls it.
What a KB with no listener pays. note-region! is one deref and a seq on the
listener vector, and nothing accumulates. A KB with one pays per relabelled region
— the same region a preview diffs — and never per stored sentex; see
lein perf's feed-listener-scaling. See docs/feed.md.
The change feed's registry and its accumulator — the leaf seam a settle files its
relabelled region into, and the one place a listener list lives.
An application driving the KB otherwise learns that belief changed only by asking
again, which misses whatever happened between two asks and costs the most on the KBs
where the least is moving. Everything a feed needs is already computed: a settle
knows the **region** it relabelled and which of that region was believed when it
first touched it (`jtms/touched` / `jtms/touched-in`), and it throws both away. This
namespace catches them.
**Why here.** `vaelii.impl.observe` is the precedent and the shape is the same: a
choke point deep in the stack has to reach a consumer defined above it, so the
indirection is a leaf both can see. What differs is the altitude. `observe`'s
observers fire on *storage*, which is what an alpha memory mirrors; a feed is about
**belief**, and belief is decided at settle time — an `assert` can store a sentex whose
label several later justifications settle. So nothing here is notified from
`kb/create-sentex`, and the region is the unit rather than the record.
**What is split where.** The registry, the accumulator and the reentrancy guard are
here, on the KB's `:feed` atom. Turning a region into the `{:believed-added
:believed-removed}` entries a listener receives is `vaelii.core`'s job — those are
`preview`'s entry shapes, built from `why-not` and the supporting justifications, and
they belong beside the code that already renders them. `core` installs that renderer
with `install-dispatch!` when it loads, and `deliver!` calls it.
**What a KB with no listener pays.** `note-region!` is one deref and a `seq` on the
listener vector, and nothing accumulates. A KB *with* one pays per relabelled region
— the same region a `preview` diffs — and never per stored sentex; see
`lein perf`'s `feed-listener-scaling`. See docs/feed.md.Does a settle file its region into the feed at all?
True everywhere except inside core/preview, which stores, settles, reads the diff
and then takes every write back. A preview that fed listeners would tell an
application that belief changed and then that it changed back, which is worse than
silence: the application learned nothing and has probably already acted. Bound
around the rollback too, since the rollback is the half that would send the
retraction.
Does a settle file its region into the feed at all? True everywhere except inside `core/preview`, which stores, settles, reads the diff and then takes every write back. A preview that fed listeners would tell an application that belief changed and then that it changed back, which is worse than silence: the application learned nothing and has probably already acted. Bound around the rollback too, since the rollback is the half that would send the retraction.
Is delivery deferred to an enclosing operation?
Most mutations settle once, so the settle is the operation and its region is the
whole story. A teardown is the exception: core/retract! and core/edit settle,
re-derive what the removal released, and settle again — and a datum that goes OUT in
the first pass and revives in the second moved no net belief at all. Delivering per
settle would report both halves of that flicker. So those two hold the feed for
their duration; the regions union, and one event is delivered with the batch's net
answer, which is the same answer core/edit-with-consequences gives for the same
batch.
Nests: an inner hold is a no-op wrapper (remove-orphaned-nats! retracts inside a
retraction), and the outermost one delivers.
Is delivery deferred to an enclosing operation? Most mutations settle once, so the settle *is* the operation and its region is the whole story. A teardown is the exception: `core/retract!` and `core/edit` settle, re-derive what the removal released, and settle again — and a datum that goes OUT in the first pass and revives in the second moved no net belief at all. Delivering per settle would report both halves of that flicker. So those two hold the feed for their duration; the regions union, and one event is delivered with the batch's net answer, which is the same answer `core/edit-with-consequences` gives for the same batch. Nests: an inner hold is a no-op wrapper (`remove-orphaned-nats!` retracts inside a retraction), and the outermost one delivers.
(create-feed)The atom a KB's :feed slot holds.
The atom a KB's `:feed` slot holds.
(deliver! kb)Hand every accumulated region to the installed renderer, then let go of the claim. Called at the tail of every settle — after the relabel is finished, the caches are reconciled and the touched set is cleared, so a listener that writes starts a fresh settle rather than relabelling inside one.
A no-op while *held?* (an enclosing teardown owns the delivery), while *enabled?*
is false, when nothing above this layer installed a renderer, and when nothing
accumulated — four cheap reads, in that order.
Re-drains until the accumulator is empty, so a listener's own writes are reported
too, and gives up at max-delivery-rounds with a :warn rather than spinning.
Hand every accumulated region to the installed renderer, then let go of the claim. Called at the tail of every settle — after the relabel is finished, the caches are reconciled and the touched set is cleared, so a listener that writes starts a fresh settle rather than relabelling inside one. A no-op while `*held?*` (an enclosing teardown owns the delivery), while `*enabled?*` is false, when nothing above this layer installed a renderer, and when nothing accumulated — four cheap reads, in that order. Re-drains until the accumulator is empty, so a listener's own writes are reported too, and gives up at `max-delivery-rounds` with a `:warn` rather than spinning.
(install-dispatch! f)Register the renderer deliver! hands each region to — vaelii.core's, once, when
it loads. Global rather than per-KB for observe's reason: the function dispatches
on the kb it is handed, so every live KB shares one installation and each keeps its
own listeners.
Register the renderer `deliver!` hands each region to — `vaelii.core`'s, once, when it loads. Global rather than per-KB for `observe`'s reason: the function dispatches on the `kb` it is handed, so every live KB shares one installation and each keeps its own listeners.
(listeners kb)The registered listeners, in registration order.
The registered listeners, in registration order.
(note-region! kb region was-in)A settle relabelled region, of which was-in was believed when first touched.
Union both into the accumulator, for the next delivery.
Accumulates rather than queues, and that is what makes an event the net answer: two regions unioned and diffed once against belief-now reports what changed, where two separately-diffed regions would report a datum that went OUT and came back as a removal followed by an addition. Union is also why order does not matter — the event's content is a function of the state the delivery reads, never of which settle filed which handle.
Called behind wants-region?, which is what keeps the caller from assembling a region
for a KB nobody is listening to — the re-check here is so this is safe to call anyway,
not the gate that makes it free.
A settle relabelled `region`, of which `was-in` was believed when first touched. Union both into the accumulator, for the next delivery. Accumulates rather than queues, and that is what makes an event the *net* answer: two regions unioned and diffed once against belief-now reports what changed, where two separately-diffed regions would report a datum that went OUT and came back as a removal followed by an addition. Union is also why order does not matter — the event's content is a function of the state the delivery reads, never of which settle filed which handle. Called behind `wants-region?`, which is what keeps the caller from *assembling* a region for a KB nobody is listening to — the re-check here is so this is safe to call anyway, not the gate that makes it free.
(register! kb entry)Add entry — a map carrying at least :f, plus :goal / :context for a standing
query — and return its token. Appended, so the vector is registration order, which
is the order deliver! calls in.
Add `entry` — a map carrying at least `:f`, plus `:goal` / `:context` for a standing query — and return its token. Appended, so the vector is registration order, which is the order `deliver!` calls in.
(unregister! kb token)Drop the listener token names; true if there was one. Idempotent — a token
already dropped, or one from another KB, removes nothing and says so.
Drop the listener `token` names; true if there was one. Idempotent — a token already dropped, or one from another KB, removes nothing and says so.
(wants-region? kb)Would a region filed now reach anybody? The gate settle-finish reads before
assembling one, so a KB nobody is listening to never builds the two sets it would have
had nowhere to put — see note-region!. Whether the feed is on is the feed's own
question, which is why both halves of it live here rather than at the call site.
Would a region filed now reach anybody? The gate `settle-finish` reads **before** assembling one, so a KB nobody is listening to never builds the two sets it would have had nowhere to put — see `note-region!`. Whether the feed is on is the feed's own question, which is why both halves of it live here rather than at the call site.
(watched? kb)Is anything listening? One deref and a seq — what a KB with no listener pays for
the whole feature.
Is anything listening? One deref and a `seq` — what a KB with no listener pays for the whole feature.
(with-one-event kb & body)Run body with delivery deferred, then deliver once — the teardown's shape (see
*held?*). Returns body's value.
The delivery runs even when body throws, so a half-applied batch still reports the
belief it did move. Often that is nothing: core/edit throws during its deferred
phase, before any settle, so no region was ever filed — and nothing is lost, because the
touched set is still uncleared and the next settle reports it.
Run `body` with delivery deferred, then deliver once — the teardown's shape (see `*held?*`). Returns `body`'s value. The delivery runs even when `body` **throws**, so a half-applied batch still reports the belief it did move. Often that is nothing: `core/edit` throws during its deferred phase, before any settle, so no region was ever filed — and nothing is lost, because the touched set is still uncleared and the next settle reports it.
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |