Server-side data/state diff.
Compares two view values and reports what changed. This is not a DOM diff
and not a hiccup diff: it answers "which paths changed, and which keyed
items were inserted, deleted, updated or moved". Turning that into HTML is
:render's job; reconciling HTML into the live DOM is datastar/idiomorph's.
Two properties carry the design:
identical? short-circuiting. Clojure's structural sharing means an
untouched subtree is pointer-identical between old and new, so it can be
pruned in O(1) without descending. Diff cost is proportional to what
changed, not to view size.
Keyed collections. A vector marked ^{:live/key :id} is diffed by key
rather than by position, so persisted items produce update/move ops and
never delete+insert. Positional diffing would cascade on a head
insertion and lose DOM state (focus, scroll, input contents).
Op vocabulary is deliberately constrained to what datastar's eight patch modes can express, so nothing emitted here is unshippable:
:replace -> mode outer :insert (:before/:after) -> mode before/after :assoc -> mode outer :insert (:after last) -> mode append :dissoc -> mode remove :insert (:before first) -> mode prepend :delete -> mode remove :move -> mode before/after
Server-side data/state diff.
Compares two view *values* and reports what changed. This is not a DOM diff
and not a hiccup diff: it answers "which paths changed, and which keyed
items were inserted, deleted, updated or moved". Turning that into HTML is
`:render`'s job; reconciling HTML into the live DOM is datastar/idiomorph's.
Two properties carry the design:
1. `identical?` short-circuiting. Clojure's structural sharing means an
untouched subtree is pointer-identical between old and new, so it can be
pruned in O(1) without descending. Diff cost is proportional to what
changed, not to view size.
2. Keyed collections. A vector marked `^{:live/key :id}` is diffed by key
rather than by position, so persisted items produce update/move ops and
never delete+insert. Positional diffing would cascade on a head
insertion and lose DOM state (focus, scroll, input contents).
Op vocabulary is deliberately constrained to what datastar's eight patch
modes can express, so nothing emitted here is unshippable:
:replace -> mode outer :insert (:before/:after) -> mode before/after
:assoc -> mode outer :insert (:after last) -> mode append
:dissoc -> mode remove :insert (:before first) -> mode prepend
:delete -> mode remove :move -> mode before/after(apply-ops view ops)Applies diff ops to a view, reconstructing the new view.
Reference implementation of op semantics, used to verify the round-trip
property. Ops are applied in order; diff emits deletes before updates
before moves before inserts so anchors resolve against a stable collection.
Applies diff ops to a view, reconstructing the new view. Reference implementation of op semantics, used to verify the round-trip property. Ops are applied in order; `diff` emits deletes before updates before moves before inserts so anchors resolve against a stable collection.
(diff old new)Diffs two view values, returning a vector of ops.
Ops carry a :path (an update-in-style vector) and, for keyed collection
ops, a :key identifying the item plus a :before/:after anchor.
Diffs two view values, returning a vector of ops. Ops carry a `:path` (an `update-in`-style vector) and, for keyed collection ops, a `:key` identifying the item plus a `:before`/`:after` anchor.
(diff-instrumented old new)Like diff, but also reports how the work was done.
Returns {:ops :visited :pruned :deep-compares :scalar-compares}.
Exists for tests. A broken identical? short-circuit yields the same ops as
a working one, so pruning is only observable through these counters — and
specifically through :deep-compares, which counts collections whose
equality had to be established the expensive way.
Like `diff`, but also reports how the work was done.
Returns `{:ops :visited :pruned :deep-compares :scalar-compares}`.
Exists for tests. A broken `identical?` short-circuit yields the same ops as
a working one, so pruning is only observable through these counters — and
specifically through `:deep-compares`, which counts collections whose
equality had to be established the expensive way.(keyed-collection? x)True if x is a vector marked as a keyed collection via :live/key
metadata.
Metadata is used rather than a wrapper type or a declaration in :state
because metadata does not participate in =. Keying therefore stays
invisible to value equality, and the convergence invariant remains testable
as plain = on views.
True if `x` is a vector marked as a keyed collection via `:live/key` metadata. Metadata is used rather than a wrapper type or a declaration in `:state` because metadata does not participate in `=`. Keying therefore stays invisible to value equality, and the convergence invariant remains testable as plain `=` on views.
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 |