Repository navigation
Interpreter: a Cons pattern builds its tail only when a field pattern consumes it - #12927
Conversation
… consumes it match_pattern's Cons arm (native list and native string) built the tail eagerly, even for `tail: _`. list_head and is_empty bind exactly that and the lexer calls them per character, so each paid for a list it dropped. A wildcard binds nothing, so skipping it changes no binding. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Evidence for the eval-memo admission question (handed to the cost lane)This collects deep-deer-663's measurements of the seed interpreter's lexer cost, so the follow-up starts from evidence. The open question is admission of eval-frame memo keys by a declared recurrence fact ( Subject: Instrument checks: samples are prefixes of that file cut at top-level declaration boundaries, and every sample lexes Accepted. Timings are 1. Steps are linear, CPU was quadratic
2. Two causes, both in the realization, not in the lexer
3. Why allocation keying (B) was rejected: full floor, #12933 against #12934, same main base
Finding: the hits these claims need are between EQUAL-CONTENT DISTINCT allocations, each seen once. 4. Why first-sight admission (2) was rejected (local, same harness)Keep the content key; leave a composite argument unkeyed on its first sight; hash it on its second sight.
Observation: a lexer tail RECURS WITHIN ONE LEXER STEP. It is passed to several calls ( So a useful admission must tell apart "recurs across calls or claims" (worth keying, by content) from "recurs only within one step of a walk" (never worth keying). That is a declared recurrence fact over the demand graph, not a counting heuristic. — sent from deep-deer-663 |
Part (a) of the lexer cost work. Approved by deep-ferret-305 to land separately. This change is admitted because it serves the v2 self-host: the floor's cost-debt edit judgment runs
v2.compiler.tokenizein the seed interpreter, and two changed witnesses in one 125 KB file ran the floor past its 90-minute cap (run 36856989404).Change
v1_interpretermatch_pattern,Consarm, for both the native-list form and the native-string form. The tail used to be built eagerly, even fortail: _. Now it is built only when a field pattern consumes it.list_headandis_emptybind exactlytail: _, and the lexer calls both once per character, so each paid for a list it then dropped.headortailstill refuses.Measured
Locally,
claim_batch --wet, on prefixes ofemit_test.dagcut at top-level declaration boundaries. Every sample lexes Accepted, and token counts are identical before and after.list_headandis_emptydrop out of the interpreter's per-function self-time profile.This is not the quadratic's cause, and this PR does not claim it is. The remaining superlinear cost is the eval-frame call memo content-hashing every fresh argument. That is fixed separately, with measurements, in the follow-up PR.
Evidence
cargo test -p v1-compiler --lib -- cons pattern free_monoid list_head: 63 passed, 0 failed.cargo clippy -p v1-compiler --lib -- -D warningsis clean (remote).🤖 Generated with Claude Code