Value evaluation in Rust
The setup guide reads a word expression and evaluates it.
The reader returns a ValueExpression<Q> tree whose nodes retain query
locations. The evaluator checks scope and types, selects branches and builds the
result. Q is the type used for native queries inside that tree.
This page describes those Rust interfaces and their ownership rules. The value reference explains the expressions themselves.
Choose the evaluator
Section titled “Choose the evaluator”| Input | APIs | Adapter |
|---|---|---|
| Constructed expression tree | evaluate_value, validate_value_expression, free_value_names |
Immutable Bindings<ConstructedValue> |
| Shared reader output or native leaves | evaluate_query_value, query_value_types, free_query_value_names |
ValueQueryHost<Q> |
| Native programs and projections | evaluate_native_value, evaluate_projection and their *_with_types forms |
ProjectionHost |
| Calls to named pure functions | FunctionRequest, PreparedFunctions |
FunctionHost |
ValueExpression defaults to Q = (). With a native operand,
ValueExpressionKind::NativeQuery(Box<Q>) retains the normalized native tree.
ValueQueryHost supplies query type facts, binding reads and execution with
current generator locals and the active budget. Runtime branch selection
controls whether the native query executes.
The word example uses the common reader and a
ValueQueryHost with constructed current input and the words binding. The
generic expression type still allows native operands, so the adapter explicitly refuses them. No native
program or function host is needed for that model.
The host supplies input facts through current_input_types(range, budget) and
an immutable value through read_current_input(range, budget). The defaults
report unavailable constructed input. CurrentInput reads share one admitted
owner per evaluation invocation; sibling record fields each see that input.
Explicit sources do not read or replace it. Native ExpressionKind::Input
keeps its separate host-owned meaning. Pure functions receive collections as
explicit parameters and do not capture caller input.
The common reader lowers filter(_.active == "yes") to
Filter { array: CurrentInput, predicate }. PredicateArgument<Q> contains an
optional Pattern and a located value body. The matcher stages names in written
order and installs them together; a mismatch is a located error. The implicit
_ retains the whole subject regardless of its argument pattern.
PredicateSubject reads the nearest predicate frame, independently of ordinary bindings. Filter preparation
uses the same Boolean/type/scope rules and the same iteration runner as a
comprehension. Typed-empty element facts survive selection.
PredicateArgument::bind(subject) creates a PredicateScope expression for one
actual subject. Scalar hosts can evaluate it with evaluate_value or
evaluate_query_value. Native adapters retain the bound expression as a declared
argument and use the active NativeArguments::evaluate_predicate runner.
For an existing domain leaf’s non-Boolean argument, bind_value(subject) creates
SubjectScope and keeps the leaf’s own argument type check.
native_predicate_types and evaluate_native_predicate support ordinary native
bodies without a FunctionHost or function session. The latter accepts the raw
predicate argument and actual host subject, retaining its native bindings.
The native_predicate_value_types and evaluate_native_predicate_value forms
serve an ordinary domain leaf’s non-Boolean argument with the same scope.
read_value_suffix takes the host and enclosing lexical names so filters after
host-lowered prefixes retain the same name and call resolution.
Values, facts and declarations
Section titled “Values, facts and declarations”ConstructedValue holds absence, Booleans, signed integers, finite floats,
strings, nominal members, positional arrays, tuples, records, typed sets and
keyed arrays. Native handles remain in the host’s representation and require
explicit projections to become constructed values.
constructed_value_type discovers facts from a value. ValueType can retain
possible field schemas and element kinds even when a result is empty.
Unestablished means missing knowledge; it is neither null nor unknown truth.
DeclaredType expresses a required interface contract. Use admit_constructed
to check a borrowed value, or declare_constructed when the immutable value
must carry that declaration through further evaluation. Optional fields may be
omitted; required and extra labels are checked. Renderers inspect data() and
stored entries, preserving omission separately from stored absence.
See runtime contracts for native
admission and checked trusted calls.
Validate before executing
Section titled “Validate before executing”evaluate_value validates actual binding types before execution. The explicit
validation and free-name APIs are useful before obtaining domain data, but do
not replace runtime checks. Keep validation, binding reads, copying and execution
on the same Budget.
All written branches receive scope, purity and known-type checks. Selected runtime branches perform data access and host execution. For a binding used only in a default fallback, supply shallow kind or declaration facts without projecting or scanning the fallback’s data. Eagerly loading every free binding would break the lazy-default contract.
evaluate_value_with_host_validation invokes a trusted value-policy callback
on each successfully executed value before its parent consumes it. The callback
shares the budget and can enforce domain restrictions such as rectangular keyed
coverage. Unselected runtime branches do not call it; structural and known-type
preflight still covers those branches.
Map operations to nodes
Section titled “Map operations to nodes”| Language form | ValueExpressionKind representation |
|---|---|
| Scalar and collection construction | Literal, Array, Tuple, Record, Dictionary, Set |
| Scalar operations and exact string helpers | Unary, Binary with the corresponding operator |
| Field/position access and optional lookup | Field, ArrayAccess, TupleAccess, Lookup, HasKey |
| Absence checks and fallback | Occurs, Absent, Default |
| Conditional selection | Conditional |
| Computed string dictionary | Dictionary entry pairs or DictionaryComprehension { clauses, key, head } |
| Array or set comprehension | Comprehension with ValueConstructor and ValueGenerator clauses |
| Scoped element selection | Filter { array, predicate } |
| One bound predicate | PredicateScope { subject, predicate }, PredicateSubject |
| Summaries | Reduce with ValueReducer::{Length, Sum, Forall, Exists} |
| Requirement | Require { condition, value, message } |
| Grouping | GroupBy { rows, field } |
| Ordering | Sort { array, field, descending }, Extremum { operator, array, field } |
| Keyed inspection and pairing | Entries, ZipExact { left, right } |
| Exact-one extraction | One |
Selectors and requirement messages retain their original locations. Grouping
uses a static Located<String> field. Sorting and extrema have an optional
static field; no field means scalar input. Known incompatible kinds fail
preflight, while missing fields may remain deferred until runtime. Empty report
projections retain their declared row facts; an empty literal invents no schema.
One moves the only child of a positional array. take_one supplies the same
cardinality check for native extraction. count_value converts a native
usize count to an admitted signed integer only when it fits i64.
Record retains static quoted field schemas. Dictionary and
DictionaryComprehension evaluate string-key expressions and produce the same
ConstructedValue::Record data. Their prepared continuations check each key
and collision before running its value, including across generator candidates.
Zero emitted entries produce an empty record. Dynamic labels have RecordKind
facts until actual values or a declaration establish their fields.
ArrayAccess also accepts a record and string key. It is strict: a missing
stored field fails, while a stored absence succeeds. Lookup remains optional.
Both preserve declared child contracts when a record carries a schema.
Keyed construction
Section titled “Keyed construction”A nominal member carries NominalTypeId, NominalMemberId, names and declaration
order from one immutable host context. Contradictory metadata and mixed nominal
types fail. Equal printed names cannot replace those identities.
Native KeyedEntry values require unique full keys in row-major order.
KeyedArray expression pairs may arrive in written order; construction
normalizes them. Inferred row and column keys use numeric or member declaration
order. Explicit ordered domains are represented as KeyAxis values.
Both KeyedArray and KeyedComprehension accept located
declared_axis_sources: Option<Vec<ValueExpression<Q>>>. Each source runs once,
left to right, in surrounding scope before literal entries or generators.
Integer/member arrays and typed sets establish complete domains, including
unused keys and typed empties. The result retains those domains, their order
and native nominal identities. A literal’s concrete axes and declared sources
are mutually exclusive.
KeyedComprehension::empty_axis_sources is a separate lazy fallback for zero
emitted cells. Nonempty output does not execute it. It resolves outside the
comprehension’s binders too, but cannot coexist with declared sources. Existing
host lowerings can retain that fallback without changing its timing.
The common reader lowers [key: value], tuple keys and indexed comprehensions
to these nodes. Contextual axes(source, ...) follows the closing bracket,
before access or pipelines. [] remains positional; [] axes(...) constructs
an empty keyed value. See indexed examples.
index_set(a) extracts the ordered typed keys of a rank-one array. entries(a)
exposes present cells, and zip_exact(a, b) compares ordered domains and full
keys at runtime, including nominal metadata. Neither helper bypasses the host’s
shape policy. Declared-source evaluation, conversion and retained-domain copies
use the active work, result and nesting allowances.
Ordering, grouping and ownership
Section titled “Ordering, grouping and ownership”Sorting extracts each key once and uses a fallible stable merge. It uses O(n log n) comparisons and O(n) temporary descriptors and positions. An owned array moves its values by permutation; a shared input needs an admitted owned output copy. Extrema instead scan all keys and retain one winner, materializing only the selected value at an owned-result boundary.
Grouping evaluates its rows once. It copies new group keys and labels, then moves existing rows into the groups. Type discovery retains possible original row schemas without predicting which keys will occur. Keyed entries and exact pairing also move admitted cells into their new containers where ownership allows it.
New containers, metadata, comparisons and copies spend the continuing budget. See resource accounting for the exact boundary, including producer admission before allocation.
Immutable reads and owned results
Section titled “Immutable reads and owned results”Evaluation prepares local names once and reads them through resolved slots. Repeated reads can share unchanged strings, collections and selected fields. Reducers inspect shared input directly after eager operand evaluation; they still scan every required element. Generator sources retain current immutable owners, and completed local frames release their temporary references.
Returned ConstructedValue results remain owned. Separate result fields and
explicit snapshots receive admitted materialization, so changing an owned Rust
result cannot change an earlier result or binding. Fresh values still receive
runtime checks. Sharing does not scan or execute a default’s unselected fallback;
all written branches keep their structural and known-type preflight.
Native computational collections
Section titled “Native computational collections”A worklist computation can keep pending items in a queue and visited native
identities in a set. The Rust function API represents their contracts with
DeclaredType::Queue(Box<DeclaredType<K>>) and DeclaredType::IdentitySet(K).
A queue carries its whole element contract; an identity set accepts one native
kind. Scalar and nominal sets retain insertion order and support explicit
sorting; they remain separate from native identity sets.
| Value expression | Result |
|---|---|
Queue { items, element } |
A FIFO in positional input order |
PopFront(queue) |
opt tuple<T, queue<T>>; empty is absent |
PushBackAll { queue, items } |
Existing queue followed by every input item |
IdentitySet { element } |
Empty set of the explicit native type |
Insert { set, item } |
Set containing that identity; duplicates are unchanged |
Binary { operator: In, left: item, right: set } |
Boolean native identity membership |
Constructor type arguments use located SignatureType trees. An explicit
element supports empty queues. Function argument/result contracts and established
loop/accumulator state also supply queue element context. Nonempty input can
establish a homogeneous element declaration. An empty input without either an
explicit or contextual declaration fails. Identity-set elements must resolve to
native types; scalar and nominal set declarations are separate types.
Native handles retain their owner, side and source coordinates. Every fresh queue element, appended item and identity-set operand receives whole admission. A host context stamp identifies the immutable snapshot in which that admission remains valid. Reusing an unchanged queue or set checks this stamp and its small type metadata, without scanning stored elements. A stale or foreign context fails at the active expression. Printed names do not establish identity.
A successful pop is an ordinary native tuple. Existing tuple patterns and
optional matching extract its fields; there is no new presence state. Empty
pop uses the existing absent value. Native/mixed Array and Tuple expressions
use explicit host container hooks, preserving the same native values.
NativeQueue, NativeIdentitySet and ComputationalValue are public native
library values. Their budgeted operations preserve retained aliases, and
returned wrappers remain usable after the function frame ends. They are
computational values for use during evaluation. Direct scalar projection,
rendering and serialization must fail with a located error. Identity-set
iteration and general set algebra are absent. The common expression reader accepts
queue(items), queue<T>(items),
pop_front(q), push_back_all(q, items), identity_set<T>(), insert(seen, item)
and native membership. The host resolves native type names and supplies identity
and container hooks. See functions and control
flow.