Skip to main content
v0.14.0

Evaluation

The tree-walk over ElaboratedModuleGraph to program output. Owning crate: metel-interpreter. Defined in evaluator/ (mod.rs, call.rs, display.rs, lvalue.rs, pattern.rs, type_of.rs, builtins.rs) and pipeline.rs (stage orchestration).

Model​

Evaluation executes the elaborated graph with identity-keyed lexical frames. A local binding, closure capture, and module-level value share cells only when the earlier pipeline deliberately established that relationship; spelling is not a fallback semantic key (environment). The runtime registry extends the same principle to callable and nominal dispatch (registry).

Values retain language-level behaviour rather than exposing evaluator shortcuts: built-in sum types use the common enum representation (enum values); receiver modes determine mutation visibility (receivers); and arrays copy at binding boundaries (array values). Runtime reconstruction, dynamic aspects, and error call stacks are explicit mechanisms, not hidden evaluator fallbacks (generic calls, dynamic aspects, call stacks).

Verifiable architecture claims

Owner metel-interpreter unless a requirement names another.

Requirement №1​

Environment is keyed entirely by LocalId, not name: two distinct LocalIds never alias, and no name-map fallback exists. Each module evaluates in its own isolated Environment, seeded from its already-evaluated dependencies' environments.

Implemented by (1)
  • get_local metel-interpreter/src/evaluator/mod.rs
Tested by (4)
Requirement №3​

Perhaps and Result are ordinary Value::Enum { name, variant, fields, .. } values with no dedicated Value variants; name-specific handling is confined to display.rs, the built-in constructors, and the for-loop's end check on Perhaps::None.

Implemented by (1)
Tested by (2)
Requirement №4​

Method dispatch preserves the typed AST's receiver mode: value receivers use the ordinary call path, &self a read-only view, and &mut self shares the caller's receiver cell into the method frame, so mutations, including nested-field ones, are immediately visible.

Implemented by (1)
Tested by (2)
Requirement №5​

Array values use value semantics at evaluator binding and assignment boundaries: Environment::define and Environment::set deep-clone their stored value, recursively cloning arrays and nested aggregate contents rather than allowing an ordinary assignment to create a mutable array alias.

Implemented by (1)
Tested by (2)
Requirement №6​

The evaluator maintains call frames in thread-local storage. Call entry pushes its function name and source call-site, normal and error exits pop it, and diagnostic construction reads the resulting stack without adding a stack parameter through every evaluation helper.

Implemented by (1)
Tested by (6)
Requirement №7​

Generic functions and let-polymorphic closures retain an untyped body plus typechecking context at runtime. A call reconstructs the body with the call-site types before evaluation, rather than storing one incorrectly monomorphic typed body for all instantiations.

owner metel-interpreter, metel-frontend

Implemented by (1)
Tested by (2)
Requirement №8​

dyn Aspect values are an explicit Value::DynAspect wrapper holding the erased value and its resolved concrete type and principal-aspect identities, introduced by typed DynCoerce nodes; value_to_type rebuilds the dyn type from that metadata without exposing the concrete value.

owner metel-interpreter, metel-frontend

Implemented by (1)
Tested by (2)
Requirement №2​

RuntimeRegistry dispatches struct/enum entries and function values by stable SymbolId, not by name at call time. Only type_ids (name to SymbolId) and pattern_methods (receivers with no type_id) stay name-keyed by design; the no-semantic-lookup check does not yet cover the registry.

mitigatedpartial

Implemented by (1)
Tested by (2)

Known limitations​

acceptedLIMIT-EVALUATION-001 — Generic function dispatch re-constructs on every call. A generic function's body is re-constructed at every call instead of once per instantiation. Open record ↗knownLIMIT-EVALUATION-003 — RuntimeRegistry is not fully covered by the resolution-freeze checker. Two runtime registry maps are name-keyed by design and sit outside the check that forbids name lookups after resolution. Open record ↗knownLIMIT-EVALUATION-004 — The evaluator is a deliberate PoC, not the target implementation shape. The tree-walking evaluator is a proof of concept expected to be replaced, not a stable target shape. Open record ↗plannedLIMIT-EVALUATION-005 — Destructor invocation is not implemented; only empty drop bodies are accepted. Drop bodies never run, so only an empty drop body is accepted. Open record ↗plannedLIMIT-TYPES-003 — Record Display is a runtime built-in, not Metel code. Display for a record of Display fields is a built-in in the evaluator, a stand-in for RFC-0174's comptime for; no user aspect, and no Eq/Hash, can walk a record's fields; Clone is likewise a built-in. Open record ↗
Resolved (2)
resolvedLIMIT-EVALUATION-006 — RFC-0167's error-code reclassification and main-entry Legality Rule are not implemented. Six R00NN codes still fire instead of their I00NN replacements; the main-entry check still runs at evaluator startup instead of typechecking, under retired R0001/R0002. Open record ↗resolvedLIMIT-EVALUATION-007 — A record rest pattern's remainder kept its record brand at runtime, so aspect dispatch ignored its static type. The remainder bound by ..rest carried the scrutinee's runtime brand, so dispatch picked the nominal impl although the spec says the remainder is anonymous. Open record ↗