0%

Section 02: Evaluator Algorithmic DRY

Status: Not Started Goal: Extract shared control-flow skeletons into canonical helpers. Eliminate the 9-way iterator consumer duplication, 8-way Option/Result handler duplication, and 3-way iterator method name list duplication. Context: The evaluator has the most algorithmic duplication of any crate. Nine iterator consumers share an identical loop { eval_iter_next → match Some/None } skeleton. Eight Option/Result method handlers share an identical validate → extract → call closure → wrap skeleton. Iterator method names are maintained in 3 independent lists that must be manually kept in sync.

02.1 Extract Iterator Consumer Drive Function

File(s): compiler/ori_eval/src/interpreter/method_dispatch/iterator/consumers.rs

Nine functions share identical loop harness: eval_iter_fold, eval_iter_count, eval_iter_find, eval_iter_any, eval_iter_all, eval_iter_for_each, eval_iter_collect, eval_iter_collect_set, eval_iter_join.

  • Create drive_iterator() method on Interpreter:

    fn drive_iterator<A, F>(
        &mut self,
        iter_val: IteratorValue,
        init: A,
        mut step: F,
    ) -> EvalResult<A>
    where
        F: FnMut(&mut Self, A, Value) -> EvalResult<ControlFlow<A, A>>,
  • Rewrite all 9 consumers using drive_iterator() (fold, count, find, any, all, for_each, collect, collect_set, join)- [ ] Verify each consumer still produces identical results (existing tests)

  • /tpr-review passed — independent review found no critical or major issues (or all findings triaged)

  • /impl-hygiene-review passed — hygiene review clean. MUST run AFTER /tpr-review is clean.

  • Subsection close-out (02.1) — MANDATORY before starting the next subsection. Run /improve-tooling retrospectively on THIS subsection’s debugging journey (per .claude/skills/improve-tooling/SKILL.md “Per-Subsection Workflow”): which diagnostics/ scripts you ran, where you added dbg!/tracing calls, where output was hard to interpret, where test failures gave unhelpful messages, where you ran the same command sequence repeatedly. Forward-look: what tool/log/diagnostic would shorten the next regression in this code path by 10 minutes? Implement improvements NOW (zero deferral) and commit each via SEPARATE /commit-push using a valid conventional-commit type (build(diagnostics): ... — surfaced by section-02.1 retrospectivebuild/test/chore/ci/docs are valid; tools(...) is rejected by the lefthook commit-msg hook). Mandatory even when nothing felt painful. If genuinely no gaps, document briefly: “Retrospective 02.1: no tooling gaps”. Update this subsection’s status in section frontmatter to complete.

  • /sync-claude section-close doc sync — verify Claude artifacts across all section commits. Map changed crates to rules files, check CLAUDE.md, canon.md. Fix drift NOW.

  • Repo hygiene check — run diagnostics/repo-hygiene.sh --check and clean any detected temp files.


02.2 Extract Option/Result Method Handler

File(s): compiler/ori_eval/src/interpreter/method_dispatch/collection_ops.rs

Eight functions share the pattern: validate arg count, match receiver variant, call closure on inner value, wrap result. These are: eval_option_map, eval_option_and_then, eval_option_filter, eval_option_or_else, eval_result_map, eval_result_map_err, eval_result_and_then, eval_result_or_else.

  • Create a generic handler parameterized by:

    • Which variant to match (Some/Ok/Err)
    • What to do with the inner value (call closure, call predicate)
    • How to wrap the result (Some, Ok, Err, identity)
    • WHERE: compiler/ori_eval/src/interpreter/method_dispatch/collection_ops.rs lines 379-491
  • Note: the 8 handlers use inconsistent arg validation — some use wrong_arg_count(), some use Self::expect_arg_count(). Unify to a single pattern during extraction.- [ ] Rewrite the 8 functions using the shared handler

  • Verify all existing tests pass unchanged

  • /tpr-review passed — independent review found no critical or major issues (or all findings triaged)

  • /impl-hygiene-review passed — hygiene review clean. MUST run AFTER /tpr-review is clean.

  • Subsection close-out (02.2) — MANDATORY before starting the next subsection. Run /improve-tooling retrospectively on THIS subsection’s debugging journey (per .claude/skills/improve-tooling/SKILL.md “Per-Subsection Workflow”): which diagnostics/ scripts you ran, where you added dbg!/tracing calls, where output was hard to interpret, where test failures gave unhelpful messages, where you ran the same command sequence repeatedly. Forward-look: what tool/log/diagnostic would shorten the next regression in this code path by 10 minutes? Implement improvements NOW (zero deferral) and commit each via SEPARATE /commit-push using a valid conventional-commit type (build(diagnostics): ... — surfaced by section-02.2 retrospectivebuild/test/chore/ci/docs are valid; tools(...) is rejected by the lefthook commit-msg hook). Mandatory even when nothing felt painful. If genuinely no gaps, document briefly: “Retrospective 02.2: no tooling gaps”. Update this subsection’s status in section frontmatter to complete.

  • /sync-claude section-close doc sync — verify Claude artifacts across all section commits. Map changed crates to rules files, check CLAUDE.md, canon.md. Fix drift NOW.

  • Repo hygiene check — run diagnostics/repo-hygiene.sh --check and clean any detected temp files.


02.3 Unify Iterator Method Name Lists

File(s): compiler/ori_eval/src/methods/dispatch_check.rs (line 66: is_collection_dispatched()), compiler/ori_eval/src/interpreter/resolvers/collection/mod.rs (line ~110: resolve_iterator_method() if-chain), compiler/ori_eval/src/interpreter/resolvers/mod.rs (line 213: all_iterator_variants()) Three independent lists enumerate iterator methods: CollectionMethod::all_iterator_variants() (in resolvers/mod.rs), CollectionMethodResolver::resolve_iterator_method() (if-chain in resolvers/collection/mod.rs), and dispatch_check::is_collection_dispatched() (in methods/dispatch_check.rs). Note: there are already enforcement tests in resolvers/tests.rs that validate all_iterator_variants() against is_iterator_method() — the goal here is to extend this pattern to cover dispatch_check too, and ideally derive the routing from the canonical list.

  • Make resolve_iterator_method() derive its routing from all_iterator_variants() — use a lookup map built from the canonical list

  • Make is_collection_dispatched() derive from all_iterator_variants() — check membership in the same lookup map

  • Add an enforcement test: all_iterator_variants().iter().all(|(name, _)| is_collection_dispatched(name))

  • Verify: adding a new iterator method to all_iterator_variants() is the ONLY change needed (routing and dispatch check are derived)

  • /tpr-review passed — independent review found no critical or major issues (or all findings triaged)

  • /impl-hygiene-review passed — hygiene review clean. MUST run AFTER /tpr-review is clean.

  • Subsection close-out (02.3) — MANDATORY before starting the next subsection. Run /improve-tooling retrospectively on THIS subsection’s debugging journey (per .claude/skills/improve-tooling/SKILL.md “Per-Subsection Workflow”): which diagnostics/ scripts you ran, where you added dbg!/tracing calls, where output was hard to interpret, where test failures gave unhelpful messages, where you ran the same command sequence repeatedly. Forward-look: what tool/log/diagnostic would shorten the next regression in this code path by 10 minutes? Implement improvements NOW (zero deferral) and commit each via SEPARATE /commit-push using a valid conventional-commit type (build(diagnostics): ... — surfaced by section-02.3 retrospectivebuild/test/chore/ci/docs are valid; tools(...) is rejected by the lefthook commit-msg hook). Mandatory even when nothing felt painful. If genuinely no gaps, document briefly: “Retrospective 02.3: no tooling gaps”. Update this subsection’s status in section frontmatter to complete.

  • /sync-claude section-close doc sync — verify Claude artifacts across all section commits. Map changed crates to rules files, check CLAUDE.md, canon.md. Fix drift NOW.

  • Repo hygiene check — run diagnostics/repo-hygiene.sh --check and clean any detected temp files.


02.4 Eliminate Redundant Name Interning

File(s): compiler/ori_eval/src/interpreter/derived_methods.rs, compiler/ori_eval/src/interpreter/interned_names.rs, compiler/ori_eval/src/methods/names.rs

  • Merge overlapping entries between OpNames and BuiltinMethodNames — shared names (“add”, “compare”, “bit_and”, etc.) should be interned once

  • Replace self.interner.intern("to_str") in format_value_printable() (line 376 of derived_methods.rs) with self.builtin_method_names.to_str (already available — see consumers.rs:202 which uses it correctly)- [ ] Replace self.interner.intern("default") in eval_default_construct() (lines 446, 484 of derived_methods.rs) with a pre-interned name — add default to BuiltinMethodNames in interned_names.rs if not already present- [ ] Verify no runtime interning of known-at-startup method names remains in derived_methods.rs: grep -n 'interner\.intern\|\.intern(' compiler/ori_eval/src/interpreter/derived_methods.rs returns 0 matches

  • /tpr-review passed — independent review found no critical or major issues (or all findings triaged)

  • /impl-hygiene-review passed — hygiene review clean. MUST run AFTER /tpr-review is clean.

  • Subsection close-out (02.4) — MANDATORY before starting the next subsection. Run /improve-tooling retrospectively on THIS subsection’s debugging journey (per .claude/skills/improve-tooling/SKILL.md “Per-Subsection Workflow”): which diagnostics/ scripts you ran, where you added dbg!/tracing calls, where output was hard to interpret, where test failures gave unhelpful messages, where you ran the same command sequence repeatedly. Forward-look: what tool/log/diagnostic would shorten the next regression in this code path by 10 minutes? Implement improvements NOW (zero deferral) and commit each via SEPARATE /commit-push using a valid conventional-commit type (build(diagnostics): ... — surfaced by section-02.4 retrospectivebuild/test/chore/ci/docs are valid; tools(...) is rejected by the lefthook commit-msg hook). Mandatory even when nothing felt painful. If genuinely no gaps, document briefly: “Retrospective 02.4: no tooling gaps”. Update this subsection’s status in section frontmatter to complete.

  • /sync-claude section-close doc sync — verify Claude artifacts across all section commits. Map changed crates to rules files, check CLAUDE.md, canon.md. Fix drift NOW.

  • Repo hygiene check — run diagnostics/repo-hygiene.sh --check and clean any detected temp files.


Cleanup (fix while touching these files)

  • [WASTE] compiler/ori_eval/src/interpreter/method_dispatch/collection_ops.rs — 8 Option/Result handlers use unreachable!("...") without structured context; replace with unreachable!("eval_option_map dispatched on {receiver:?}") to aid debugging on panic
  • [WASTE] compiler/ori_eval/src/interpreter/method_dispatch/collection_ops.rs:130-136then_with arg check uses inline EvalError::new(format!(...)) instead of wrong_arg_count("then_with", 1, args.len()) error factory; inconsistent with rest of file

02.R Third Party Review Findings

  • None.

02.T Test Strategy

This section is pure structural refactoring with zero behavioral change. The test strategy focuses on:

  1. Existing test suite as regression gate: ./test-all.sh must pass identically before and after each sub-section.
  2. Unit tests for new canonical functions: Each extracted function gets direct unit tests.
  3. Enforcement tests for method name canonical source: Verify all consumers derive from the single list.
  • Add unit tests for drive_iterator() in compiler/ori_eval/src/interpreter/method_dispatch/iterator/tests.rs:
    • Empty iterator produces init value unchanged
    • ControlFlow::Break stops iteration early and returns the break value
    • ControlFlow::Continue processes all elements
    • Test with a 3-element iterator to verify ordering (first, second, third)
  • Add unit tests for the Option/Result shared handler:
    • Some(v) with identity closure returns Some(v)
    • None with any closure returns None
    • Ok(v) maps correctly; Err(e) passes through unchanged (and vice versa for map_err)
  • Add enforcement test: all_iterator_variants() entries are all present in is_collection_dispatched()
  • Verify timeout 150 cargo test -p ori_eval passes after each sub-section
  • Verify timeout 150 ./test-all.sh passes after all sub-sections complete

02.N Completion Checklist

  • drive_iterator() extracted — 9 consumers use it
  • Option/Result handler extracted — 8 functions use it
  • Iterator method name lists reduced to 1 canonical source
  • No runtime interning of known method names in derived_methods.rs
  • Unit tests for all new canonical functions pass
  • Enforcement test for iterator method name derivation passes
  • timeout 150 cargo test -p ori_eval passes
  • timeout 150 ./test-all.sh passes
  • ./clippy-all.sh clean
  • /tpr-review covering Section 02
  • /impl-hygiene-review
  • /improve-tooling retrospective completed — MANDATORY at section close, after both reviews are clean. Reflect on the section’s debugging journey (which diagnostics/ scripts you ran, which command sequences you repeated, where you added ad-hoc dbg!/tracing calls, where output was hard to interpret) and identify any tool/log/diagnostic improvement that would have made this section materially easier OR that would help the next section touching this area. Implement every accepted improvement NOW (zero deferral) and commit each via SEPARATE /commit-push. The retrospective is mandatory even when nothing felt painful — that is exactly when blind spots accumulate. See .claude/skills/improve-tooling/SKILL.md “Retrospective Mode” for the full protocol.