Public reducer descriptors for graph state fields, plus the pure reduction engine the runtime applies at every commit.
A reducer folds the field's prior committed value with the step's writes (deterministically sorted by writer node ID):
new_value = reduce(reducer, current_committed_value, sorted_writes)Built-in reducers
| Reducer | Semantics |
|---|---|
:last_value | last write wins; the prior value is ignored |
:first_value | keep the prior value once set; otherwise the first write |
:append | list accumulation: current ++ writes |
:merge | map merge: writes fold into the prior map |
:sum | numeric accumulation: prior value plus every write |
:union | append + dedupe (first occurrence wins) |
For :append and :union, a list write concatenates its elements and
any other write appends as a single element; to append one element that is
itself a list, wrap it in an outer list.
Options
:append—unique: truedrops duplicate elements (first occurrence wins);max_length: nkeeps only the lastnelements (sliding window), applied afterunique.:union—by: keydedupes elements by the value atkey(a string or a list of strings forming a path); elements missing the key dedupe by their whole value.:merge—deep: truemerges nested maps recursively instead of replacing them.
Option keys are stored as strings (constructors normalize atom keys), matching the wire format. Unknown option keys are stored but ignored.
Initial values
Accumulating reducers have a natural zero used as the field's effective
default when it declares none: [] for :append/:union, %{} for
:merge, 0 for :sum. A field's explicit default acts as the base the
first commit folds into — including :first_value, where an explicit
default counts as the value already being set.
Write validation
Node writes to a field validate against a reducer-dependent shape rather
than the committed field schema (which stays the truth for the committed
value): :append/:union writes validate against the list schema's
item (a list write validates each element), :merge writes validate as
a map fragment of the field schema with top-level required fields
relaxed, and :sum writes must be a number of the schema's type.
Summary
Functions
Normalizes reducer shorthand into a Docket.Reducer struct: a built-in
type atom (:append) or a {type, opts} tuple ({:append, max_length: 50}).
Folds the prior committed value with one step's writes.
All built-in reducer types.
The schema a single node write validates against, given the field schema
and the write itself. nil means the write is not schema-validated.
The natural zero for accumulating reducers, used as a field's effective
default when it declares none. nil for non-accumulating reducers.
Types
Functions
Normalizes reducer shorthand into a Docket.Reducer struct: a built-in
type atom (:append) or a {type, opts} tuple ({:append, max_length: 50}).
Real reducers and values matching no shorthand pass through unchanged; the compiler reports the latter as invalid reducers.
Folds the prior committed value with one step's writes.
current is {:ok, value} when the field has a committed value or an
effective default, :unset otherwise. values are the step's writes in
sorted writer order and must be non-empty. A nil reducer reduces as
:last_value.
@spec types() :: [type()]
All built-in reducer types.
@spec write_schema(t() | nil, Docket.Schema.t() | nil, term()) :: Docket.Schema.t() | nil
The schema a single node write validates against, given the field schema
and the write itself. nil means the write is not schema-validated.
The natural zero for accumulating reducers, used as a field's effective
default when it declares none. nil for non-accumulating reducers.