Opt-in build history — "how did I get here?" — for commensura values.
When recording is on, each public verb tags its result with a self-contained provenance node in
the value's Clojure metadata: the operation, the result value, and the operand sub-nodes nested
inline. So (meta result) holds the entire history as one ::node map — you can read the whole tree
by eye, no walker required.
(require '[commensura.core :refer [with-provenance by to]] '[commensura.provenance :refer [explain]] '[commensura.units :as u])
(with-provenance (explain (to (by (u/feet 10) (u/feet 12) (u/feet 8)) u/gallons)))
The two entry points for making history — with-provenance (turn recording on) and defstep
(define a fn that records as one node) — live in commensura.core, alongside the verbs they drive.
This namespace holds the mechanism (step/record-node, the *record-provenance-on-step* var) and
everything for reading history back.
Off by default, zero-cost when off. Recording is gated by the dynamic var
*record-provenance-on-step* (default false); commensura.core/with-provenance binds it true for
its body. When it's off the verbs do no vary-meta at all — they don't even build the operand vector.
Each step forgets its internals. A step evaluates its body with recording suppressed, then
records exactly one node for itself. So a verb records a single node over its operands, and a
defstep'd function (like commensura.math/sqrt) shows only its own name — never the pow it calls
underneath. The operands' own histories, built before the step ran, are nested in unchanged.
It's a DAG, not a tree. The nested sub-nodes are shared immutable maps, so a let-bound value
reused in two operands is the same node object in both places (identical? holds); explain shows
the repeat once, citing it thereafter as a back-reference (↑ [n]).
Only real values carry history, and it's process-local. Metadata rides on IObj values;
raw scalars, exponents and keyword targets appear as inline operand leaves, never as nodes. Because
pr/read ignore metadata, a recorded value is still = to, and prints identically to, its
unrecorded self — history lives only in memory.
Inspect with node (the nested map), history (its nodes as a flat seq), history-zip (a
clojure.zip cursor over the whole tree — nodes and leaves), and explain/explain-str/
explain-lines (a readable outline — printed, as one string, or as a seq of line-strings — itself
written on history-zip).
Opt-in **build history** — "how did I get here?" — for commensura values.
When recording is on, each public verb tags its result with a self-contained provenance *node* in
the value's Clojure metadata: the operation, the result value, and the operand *sub-nodes* nested
inline. So `(meta result)` holds the entire history as one `::node` map — you can read the whole tree
by eye, no walker required.
(require '[commensura.core :refer [with-provenance by to]]
'[commensura.provenance :refer [explain]]
'[commensura.units :as u])
(with-provenance
(explain (to (by (u/feet 10) (u/feet 12) (u/feet 8)) u/gallons)))
The two entry points for *making* history — `with-provenance` (turn recording on) and `defstep`
(define a fn that records as one node) — live in `commensura.core`, alongside the verbs they drive.
This namespace holds the mechanism (`step`/`record-node`, the `*record-provenance-on-step*` var) and
everything for *reading* history back.
**Off by default, zero-cost when off.** Recording is gated by the dynamic var
`*record-provenance-on-step*` (default `false`); `commensura.core/with-provenance` binds it true for
its body. When it's off the verbs do no `vary-meta` at all — they don't even build the operand vector.
**Each step forgets its internals.** A `step` evaluates its body with recording *suppressed*, then
records exactly one node for itself. So a verb records a single node over its operands, and a
`defstep`'d function (like `commensura.math/sqrt`) shows only its own name — never the `pow` it calls
underneath. The operands' *own* histories, built before the step ran, are nested in unchanged.
**It's a DAG, not a tree.** The nested sub-nodes are shared immutable maps, so a `let`-bound value
reused in two operands is the *same* node object in both places (`identical?` holds); `explain` shows
the repeat once, citing it thereafter as a back-reference (`↑ [n]`).
**Only real values carry history**, and it's **process-local.** Metadata rides on `IObj` values;
raw scalars, exponents and keyword targets appear as inline operand leaves, never as nodes. Because
`pr`/read ignore metadata, a recorded value is still `=` to, and prints identically to, its
unrecorded self — history lives only in memory.
Inspect with `node` (the nested map), `history` (its nodes as a flat seq), `history-zip` (a
`clojure.zip` cursor over the whole tree — nodes and leaves), and `explain`/`explain-str`/
`explain-lines` (a readable outline — printed, as one string, or as a seq of line-strings — itself
written on `history-zip`).When true, the public verbs record a provenance node on each result. Default false — bind it with
commensura.core/with-provenance (or directly) to record. Kept off by default so ordinary use pays
nothing.
When true, the public verbs record a provenance node on each result. Default false — bind it with `commensura.core/with-provenance` (or directly) to record. Kept off by default so ordinary use pays nothing.
(child-nodes x)The inputs of x that are themselves nodes — the DAG children (inline leaves dropped).
The inputs of `x` that are themselves nodes — the DAG children (inline leaves dropped).
(explain-lines x)x's build history as a seq of outline line-strings — the data explain-str joins and explain
prints. A node line reads [n] <value> ← <verb>; inline operands sit unnumbered beneath their
verb; a value that recurs is shown once, then cited as ↑ [n] (and its operands omitted). Returned
as data so you can count/filter/re-indent it or feed it to a viewer.
Walks history-zip, dogfooding the cursor: zip/path gives the indentation depth; seen (a plain
map threaded through the loop) numbers nodes and de-duplicates them by value; and once a repeat is
cited, hide (the depth it sat at) omits every deeper loc until the walk climbs back out — so no
manual subtree-skipping is needed.
`x`'s build history as a seq of outline line-strings — the data `explain-str` joins and `explain` prints. A node line reads `[n] <value> ← <verb>`; inline operands sit unnumbered beneath their verb; a value that recurs is shown once, then cited as `↑ [n]` (and its operands omitted). Returned as data so you can count/filter/re-indent it or feed it to a viewer. Walks `history-zip`, dogfooding the cursor: `zip/path` gives the indentation depth; `seen` (a plain map threaded through the loop) numbers nodes and de-duplicates them *by value*; and once a repeat is cited, `hide` (the depth it sat at) omits every deeper loc until the walk climbs back out — so no manual subtree-skipping is needed.
(history x)Every node reachable from x, x's node first, depth-first. A shared node repeats (it's the same
object each time — distinct collapses them; explain shows it once with a back-reference).
Every node reachable from `x`, `x`'s node first, depth-first. A shared node repeats (it's the same object each time — `distinct` collapses them; `explain` shows it once with a back-reference).
(history-zip x)A clojure.zip cursor over x's history — pass a recorded value or a node map. Unlike history
(nodes only), this walks the whole tree: a branch's children are all its :inputs, so zip/down
reaches the inline-operand leaves too. Read the node/leaf under the cursor with clojure.zip/node,
move with clojure.zip/next/down/up/right/left. explain is written on top of it.
A `clojure.zip` cursor over `x`'s history — pass a recorded value *or* a node map. Unlike `history` (nodes only), this walks the **whole** tree: a branch's children are all its `:inputs`, so `zip/down` reaches the inline-operand leaves too. Read the node/leaf under the cursor with `clojure.zip/node`, move with `clojure.zip/next`/`down`/`up`/`right`/`left`. `explain` is written on top of it.
(inputs x)The operand sub-nodes/leaves of x, in order (nil: a leaf).
The operand sub-nodes/leaves of `x`, in order (nil: a leaf).
(node x)The provenance node — a nested {:op :value :inputs} map — for x, or nil if it carries none.
Accepts a value (reads its metadata) or a node map (returns it), so the inspection fns take either.
The provenance node — a nested `{:op :value :inputs}` map — for `x`, or nil if it carries none.
Accepts a value (reads its metadata) or a node map (returns it), so the inspection fns take either.(op x)The verb that produced x (nil for a leaf).
The verb that produced `x` (nil for a leaf).
(record-node op inputs result)Attach a provenance node to result (an IObj value): its op, the result value, and each input
folded in as a nested sub-node or a leaf. A non-IObj result (a bare number, a boolean) is returned
unchanged. Callers gate on *record-provenance-on-step* first (see step).
Attach a provenance node to `result` (an IObj value): its op, the result value, and each input folded in as a nested sub-node or a leaf. A non-IObj result (a bare number, a boolean) is returned unchanged. Callers gate on `*record-provenance-on-step*` first (see `step`).
(recorded? x)Does x carry a provenance node?
Does `x` carry a provenance node?
(step op inputs & body)Record body's result as one provenance node labelled op over inputs (its operand values).
A no-op — and zero allocation — unless *record-provenance-on-step* is on. The body runs with
recording suppressed, so a step keeps no trace of the ops it calls internally: (step #'sqrt [x] …)
records a lone sqrt node, and the operands' own histories (built before the step) nest in. op is
conventionally the fn's #'var (a fully-qualified reference), so a node names exactly what made it.
Record `body`'s result as one provenance node labelled `op` over `inputs` (its operand values). A no-op — and zero allocation — unless `*record-provenance-on-step*` is on. The body runs with recording *suppressed*, so a step keeps no trace of the ops it calls internally: `(step #'sqrt [x] …)` records a lone `sqrt` node, and the operands' own histories (built before the step) nest in. `op` is conventionally the fn's `#'var` (a fully-qualified reference), so a node names exactly what made it.
(value x)The result value stored on x's node (nil for a leaf).
The result value stored on `x`'s node (nil for a leaf).
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 |