Skip to content

Wrap higher-order predicate errors with source text#18

Merged
myzie merged 1 commit into
mainfrom
higher-order-predicate-error-wrap
May 8, 2026
Merged

Wrap higher-order predicate errors with source text#18
myzie merged 1 commit into
mainfrom
higher-order-predicate-error-wrap

Conversation

@myzie
Copy link
Copy Markdown
Contributor

@myzie myzie commented May 8, 2026

Summary

Predicate errors raised inside map/filter/any/all/find/count are now wrapped with the form name, the predicate's source text (printed back from the AST), and the failing element's index.

map predicate `it.Nmae` failed on element 0: expr: evaluate error: key "Nmae" not found (did you mean "Name"?)

Nested forms add their own layer, top-down through the iteration tree. The internal __expr_map__ rewrite is reversed before printing, so users see the source they typed:

map predicate `map(it, it.bad)` failed on element 0: map predicate `it.bad` failed on element 0: ...

Behavior details

  • Trapped: anything wrapping ErrEvaluate. Wrapping preserves the chain, so errors.Is(err, ErrEvaluate) still matches.
  • Not wrapped: raw context.Canceled / context.DeadlineExceeded so cancellation stays observable.
  • Backtick guard: predicates whose printed source contains a backtick fall back to a position-only form (map predicate failed on element 0: ...), since backticks are the surrounding delimiters.
  • Fast-path parity: tryFilterIndex in program.go (the filter(xs, p)[N] fast path) uses the same helper, so the fast path is not a debuggability regression.

Implementation

  • New wrapPredicateErr(name, predicate, index, err) in higher_order.go.
  • New formatPredicate(node) uses go/printer.Fprint with a fresh FileSet and post-processes the map sentinel back to map. No new external deps; go/printer is stdlib.
  • forEach now takes a form name and the predicate AST. Each of the six forms passes its own user-visible name.

Tests

higher_order_test.go gained six new tests:

  • TestHigherOrder_PredicateError (updated): pins the new format on a map(...) typo.
  • TestHigherOrder_PredicateErrorReportsIndex: failure on the 3rd element reports element 2.
  • TestHigherOrder_PredicateErrorNestedForms: both layers appear; sentinel does not leak.
  • TestHigherOrder_PredicateErrorFormNames: spot-check filter / find / count / any / all.
  • TestHigherOrder_PredicateErrorFilterIndexFastPath: fast path uses the same wrapping.
  • TestHigherOrder_PredicateErrorBacktickFallback: predicate with a backtick falls back cleanly.
  • TestHigherOrder_PredicateErrorIgnoresCancellation: cancellation is not wrapped.

Test plan

  • go test ./...
  • go test -race ./...
  • FuzzCompile 20s
  • FuzzEval 20s

What:
- Predicate errors raised inside map/filter/any/all/find/count are
  wrapped with the form name, the predicate's source text printed
  back from the AST, and the failing element's index. Example:
    map predicate `it.Nmae` failed on element 0: ...
- Nested forms each add their own layer, top-down through the
  iteration tree. The internal `map` keyword rewrite is reversed
  before printing so users see the source as they typed it.
- Errors that do not wrap ErrEvaluate (context.Canceled,
  context.DeadlineExceeded) pass through unchanged so cancellation
  stays observable.
- Predicates whose source text contains a backtick fall back to a
  position-only message to avoid clashing with the surrounding
  delimiters.
- Same wrapping is applied in tryFilterIndex (the filter(xs, p)[N]
  fast path) so the fast path and slow path stay consistent.

Why:
- "at index N" alone left users guessing what N referred to. Showing
  the predicate text plus an "element N" wording connects the failure
  to a specific iteration step inside a specific form, which is the
  whole win this change is supposed to deliver. See
  docs/design/language-evolution.md (step 3).

Spec and llms.txt updated.
@myzie myzie merged commit 7c547a2 into main May 8, 2026
1 check passed
@myzie myzie deleted the higher-order-predicate-error-wrap branch May 8, 2026 14:35
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