Skip to content

Add mapr: short-circuit Result propagation across map#296

Merged
danieljohnmorris merged 8 commits into
mainfrom
fix/map-result-propagation
May 16, 2026
Merged

Add mapr: short-circuit Result propagation across map#296
danieljohnmorris merged 8 commits into
mainfrom
fix/map-result-propagation

Conversation

@danieljohnmorris
Copy link
Copy Markdown
Collaborator

Summary

Adds a new HOF mapr fn xs : R (L b) e that calls fn on each item, accumulates the inner Ok values on the all-Ok path, and short-circuits on the first Err. Mirrors Rust's collect::<Result<Vec<_>, _>>() pattern.

Originating friction: ilo_assessment_feedback.md line 2541 (html-scraper rerun3). The personas kept writing:

ton s:t>n;r=num s;?r{~v:v;^_:0}    -- swallow-err helper, ~30 tokens
ns=map ton xs                      -- and the error is lost

mapr collapses both lines to one and preserves the err shape:

ns=mapr num xs                     -- R (L n) t

Pair with ! to thread the err up into a Result-returning caller without per-item match boilerplate. The assessment doc says this "would remove the ton helper entirely" across every CSV/HTML-parsing program.

Repro before/after

Before:

$ ilo 'count xs:L t>R n t;ns=mapr! num xs;~len ns' --run-tree count '["1","2","3"]'
error: undefined function 'mapr'

After:

$ ilo 'count xs:L t>R n t;ns=mapr! num xs;~len ns' --run-tree count '["1","2","3"]'
3
$ ilo 'count xs:L t>R n t;ns=mapr! num xs;~len ns' --run-tree count '["1","bad","3"]'
^bad

Tree, VM, and Cranelift all agree byte-for-byte.

What's in the diff (per-commit)

  1. builtins: register mapr discriminant, name, and tag (b1ec0fa) - enum variant, name table both directions, ALL list, round-trip test fixtures.
  2. parser: mapr takes a fn-ref in slot 0 like other HOFs (5955b96) - parser knows to keep the first arg as a fn-ref (parallel to map, flt, flatmap).
  3. verify: type mapr as R (L b) e from fn return R b e (2a86130) - derives the outer Result shape from the callback. Rejects non-fn first args and callbacks that don't return Result with ILO-T013 plus a hint to use map for non-fallible fns.
  4. interpreter: implement mapr short-circuit Result propagation (6a41689) - tree-walker arm.
  5. vm: route mapr through the tree bridge on VM and Cranelift (0c2be43) - (Builtin::Mapr, 2) joins grp/uniqby/partition/srt in is_tree_bridge_eligible, and tree_bridge_returns_result includes Mapr so ! propagation works.
  6. test: cross-engine regression coverage for mapr (b645194) - 11 cross-engine cases covering happy path, empty, head/mid/tail Err, ! propagation, user-fn dispatch, and verifier rejections.
  7. example: mapr happy/bail/bang/empty paths (546f081) - examples/mapr.ilo picked up by the examples_engines harness.
  8. docs: document mapr in SPEC, ai.txt, and SKILL (95c59b5) - in-repo docs. Site reference is updated in ilo-lang/site#docs/mapr-builtin.

Test plan

  • cargo test --release --features cranelift --test regression_mapr (11 tests, all engines)
  • cargo test --release --features cranelift --test examples_engines (mapr.ilo runs across tree + vm with both out and err assertions)
  • cargo test --release --features cranelift (full suite green; AOT tests need target/release/libilo.a, which is the standard CI layout)
  • cargo clippy --release --features cranelift --all-targets -- -D warnings
  • cargo fmt --all

Follow-ups

  • Closure-bind ctx variant (mapr fn ctx xs) deliberately omitted; will add if a workload turns up that needs it.
  • Site PR for the builtins reference page lives in ilo-lang/site on branch docs/mapr-builtin.

@codecov
Copy link
Copy Markdown

codecov Bot commented May 16, 2026

Codecov Report

❌ Patch coverage is 80.82192% with 14 lines in your changes missing coverage. Please review.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/interpreter/mod.rs 50.00% 14 Missing ⚠️

📢 Thoughts on this report? Let us know!

@danieljohnmorris danieljohnmorris force-pushed the fix/map-result-propagation branch 2 times, most recently from f182f12 to d0f8b1c Compare May 16, 2026 10:16
Derives the outer Result shape from the callback's return type, rejects
non-fn first args (ILO-T013), and rejects callbacks that don't return a
Result with a hint to use map for non-fallible fns.
For each item the callback must return ~v or ^e. On ~v the inner value
is accumulated; on ^e the whole call returns that ^e immediately,
skipping the rest of the list. Final return on the all-Ok path is
~(L b). A callback that returns anything else surfaces ILO-R009.
Adds (Builtin::Mapr, 2) to is_tree_bridge_eligible and Mapr to
tree_bridge_returns_result. VM and Cranelift dispatch the HOF via the
same ACTIVE_AST_PROGRAM path grp/uniqby/partition/srt use; the bridge
unwrap epilogue handles ! propagation on the Result return.
Pins the all-Ok, empty, head/mid/tail Err short-circuit, ! propagation,
and user-fn dispatch cases across tree, VM, and Cranelift. Plus
verifier rejections for non-fn first arg, non-Result callback, and
wrong arity.
Demonstrates the persona-relevant scenarios: ~[list] on the all-Ok
path, first ^err short-circuit, ! propagation into the surrounding
fn, and the trivial empty case. Pulled into examples_engines harness
so every engine validates the contract.
Adds the mapr row to the builtins table in SPEC.md, the token-dense
ai.txt context line, and the HOF section in skills/ilo/SKILL.md with
the persona-facing motivation (retires the ton helper).
@danieljohnmorris danieljohnmorris force-pushed the fix/map-result-propagation branch from d0f8b1c to 723cbb8 Compare May 16, 2026 11:23
@danieljohnmorris danieljohnmorris merged commit 44dc868 into main May 16, 2026
4 checks passed
@danieljohnmorris danieljohnmorris deleted the fix/map-result-propagation branch May 16, 2026 11:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant