Skip to content

Eta-wrap user functions passed as higher-order callbacks (#343) - #344

Merged
taglotacks merged 1 commit into
mainfrom
fix-closure-eta-wrap
Aug 24, 2026
Merged

taglotacks merged 1 commit into
mainfrom
fix-closure-eta-wrap

Conversation

@jimmytacks

Copy link
Copy Markdown
Collaborator

Closes #343

Summary

Fixes the silent relative-reference re-anchoring Tim hit in GRIDWHERE (LAMBDA(v, v=A1) predicates returning #N/A). Every library call site that passed a user-supplied function directly as a MAP/SCAN/REDUCE/GROUPBY callback now eta-wraps it in an owned lambda of matching arity, which preserves the closure:

  • GRIDWHERE, CLUSTERAGG (predicate + value_fn): MAP(grid, LAMBDA(x_, predicate(x_)))
  • ALL / ANY / COUNTOF: same for pred
  • SCANLEAD / CUMSUMGRID / REDUCE1 / SCAN1: LAMBDA(acc_, x_, fn(acc_, x_))
  • MOSTOF: GROUPBY(_keys, _vals, LAMBDA(col_, _fn(col_)), 0, 0, -2) — COM-verified the wrap is transparent for the ROWS/SUM defaults and built-ins by name

Safe patterns left untouched: fns called inside owned callbacks (FOLDROWS, RUNGROUPBY, BIMAP, dispatchers), direct calls (BIROW/BICOL), built-ins passed by name where no user lambda can arrive.

The create-lambda skill best-practice section gains the eta-wrap rule with the diagnosis.

Testing

  • COM verification (the real test): all 10 patched call sites verified with relative-ref closures and formulas placed away from A1, full 139-name library injected with the loader's retry pattern — all correct. (The harness formula lives at A1, where the re-anchoring is the identity, so the bug is structurally invisible to it.)
  • Each affected lambda gains a closure regression test in its yaml (guards arity/semantics of the wrapped call; a comment in GRIDWHERE's yaml documents the A1 blind spot).
  • Format tests: 904 passed. Full harness suite: 1162/1162 passed.

A relative cell ref inside a user lambda (e.g. LAMBDA(v, v=C3)) silently
re-anchors when that lambda is passed directly as a MAP/SCAN/REDUCE/GROUPBY
callback inside a defined-name lambda: Excel re-reads it as an offset-from-A1
applied to the formula cell, usually hitting an empty cell - silent wrong
answers. Wrapping the call in an owned lambda of matching arity preserves
the closure.

Affected call sites fixed: GRIDWHERE, CLUSTERAGG (predicate + value_fn),
ALL, ANY, COUNTOF, SCANLEAD, MOSTOF (GROUPBY forward), CUMSUMGRID (x2),
REDUCE1, SCAN1.

Each gains a closure regression test (arity/semantics only - the harness
formula lives at A1 where the re-anchoring is the identity, so the bug
itself is invisible there; verification was COM-based with formulas away
from A1, full 139-name library injected). The create-lambda skill gains
the eta-wrap rule.

Closes #343

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@taglotacks
taglotacks merged commit f3bc3eb into main Aug 24, 2026
2 checks passed
@taglotacks
taglotacks deleted the fix-closure-eta-wrap branch August 24, 2026 14:36
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.

Relative refs in user-lambda closures break when passed directly as MAP/SCAN callbacks inside library lambdas

2 participants