The pure builders behind the DECLARATIVE combinator slots. Each turns a declarative spec — attribute atoms, fold keywords, side picks — into the same fn shape the per-slot escape hatches supply, so the recompute pipeline runs one code path however declarative the author went.
Fold semantics follow SQL aggregates: nil source values are excluded, and
a fold over no non-nil values yields nil (count counts rows and is never
nil). first is the first non-nil value in the group's read order.
Summary
Functions
The fold kinds into:'s declarative form accepts.
The CHILD (over-resource) field of each group entry — what is read and grouped.
The PARENT (this node's) column of each group entry — what the row carries.
group_by spec → (item -> group_term). A list groups by the TUPLE of entry
values. An entry names an attribute OR a CALCULATION on the over resource —
derived grouping values (a calendar bucket, a normalized code) are Ash
calculations, declared where the data lives; the library loads them in the
read (ReactiveDag.Calendar ships the calendar ones). The RELATIONAL-JOIN
spelling {parent_field, child_field} ([category: :expense_cat]) groups by
the CHILD field while the row carries the PARENT column — Ash's
source/destination-attribute pair, for the DAG edge.
The IDENTITY key serializer for a composite-primary-key node: the cell key is
the row's identity fields, in primary-key order, joined with "|" (the same
canonical serialization derived keys have always used) — the key is no longer
a stored column but the serialization of the row's Ash identity.
reduce into: spec → (group_term, items -> row). The declarative form
produces the group's attribute columns (requires a declarative group_by,
enforced at compile time) merged with the fold results, keyed by dest names.
join into: spec → (join_key, left, right -> row). The declarative form
picks columns per side ([left: [amount: :budget], right: [amount: :actual]]; bare atom = same-named dest); an absent side yields nils, so
left-join/outer gap semantics fall out.
key spec (+ key_prefix) → (group_term_or_join_key -> key string). The
default joins a tuple's values with "|" (a single value stringifies), and
prepends "<prefix>|" when a prefix is given.
join side spec → (item -> join_key | nil). An attribute projects it (nil
value = not on this side); [key: :acct, where: [kind: :budget]] puts the
item on the side iff every where pair matches, keyed by the key: attr.
Functions
@spec fold_kinds() :: [atom()]
The fold kinds into:'s declarative form accepts.
The CHILD (over-resource) field of each group entry — what is read and grouped.
The PARENT (this node's) column of each group entry — what the row carries.
group_by spec → (item -> group_term). A list groups by the TUPLE of entry
values. An entry names an attribute OR a CALCULATION on the over resource —
derived grouping values (a calendar bucket, a normalized code) are Ash
calculations, declared where the data lives; the library loads them in the
read (ReactiveDag.Calendar ships the calendar ones). The RELATIONAL-JOIN
spelling {parent_field, child_field} ([category: :expense_cat]) groups by
the CHILD field while the row carries the PARENT column — Ash's
source/destination-attribute pair, for the DAG edge.
The IDENTITY key serializer for a composite-primary-key node: the cell key is
the row's identity fields, in primary-key order, joined with "|" (the same
canonical serialization derived keys have always used) — the key is no longer
a stored column but the serialization of the row's Ash identity.
reduce into: spec → (group_term, items -> row). The declarative form
produces the group's attribute columns (requires a declarative group_by,
enforced at compile time) merged with the fold results, keyed by dest names.
join into: spec → (join_key, left, right -> row). The declarative form
picks columns per side ([left: [amount: :budget], right: [amount: :actual]]; bare atom = same-named dest); an absent side yields nils, so
left-join/outer gap semantics fall out.
key spec (+ key_prefix) → (group_term_or_join_key -> key string). The
default joins a tuple's values with "|" (a single value stringifies), and
prepends "<prefix>|" when a prefix is given.
join side spec → (item -> join_key | nil). An attribute projects it (nil
value = not on this side); [key: :acct, where: [kind: :budget]] puts the
item on the side iff every where pair matches, keyed by the key: attr.