Skip to content

Calls passing a source lambda to a non-recursive def inline the callee, through a leading one-arm match - #1866

Draft
nicolas-abril wants to merge 1 commit into
bendlang:mainfrom
nicolas-abril:callback-inline
Draft

nicolas-abril wants to merge 1 commit into
bendlang:mainfrom
nicolas-abril:callback-inline

Conversation

@nicolas-abril

@nicolas-abril nicolas-abril commented Oct 26, 2026 •

Copy link
Copy Markdown
Collaborator

comp.ts is 66,846 ttok, 846 over the 66,000 cap, so repo-gate fails until that is settled. Replaces #1356. It does the same job by inlining instead of minting specialized defs: +886 ttok against main, where #1356 cost +1,136, with the same speed.

What it does

When a call passes a lambda written in the source in a live parameter position, and the callee isn't recursive, the callee is inlined at the call site. The lambda is then applied in place, and no closure is built.

  • Through a leading match: inlining passes through a callee's leading match when the matched type has one constructor (tuples, records). A small helper, def_at, puts the callback into the match arm.
  • Beta: a lambda applied directly to its arguments is reduced in anf.
  • Emitter: when a let binds the result of a one-arm match, the rest of the code moves into that arm. A match in tail position is emitted directly. This also applies to one-arm matches written in source.

There are no new defs, keys, markers or fuel. Termination comes from the callee being non-recursive.

Results

The fixtures from #1288 (by @mizchi) are included unchanged, and all 5 print their expected output.

Speed (M4, 10⁸ steps, 7 runs each, measured before the rebase onto #1968; after it, a re-measured helper-with-a-capturing-lambda loop went from about 0.26 s on main to about 0.01 s):

program main #1356 this
helper called each step with a fresh capturing lambda 241 ms 51.4 ms 51.4 ms
bind-style callback 841 ms 102.6 ms 102.7 ms
destructures a tuple literal 866 ms 26.3 ms 26.5 ms
destructures a call result (get-style) 911 ms 51.7 ms 51.5 ms

Closures built in the fixtures (main / #1356 / this, measured before the rebase):

fixture main #1356 this
known_callback 5 0 0
known_callback_array 2 0 0
known_callback_layouts 3 0 0
known_callback_shapes 11 3 3
known_callback_edges 21 18 19

Checks:

  • test.ts passes 1803/1803 and perf.ts 124/124, on main at refactor(comp): shed tokens and compile large programs faster #1968.
  • Everything under bench/ compiles to the same C as main, and JS output is unchanged across the corpus. The C changes in 42 of the 1,383 programs that load, including the demos app_slash_boss_3d and io_tcp_echos.
  • tsc shows no new errors in comp.ts.
  • repo.ts fails only on the ttok cap.

Not covered

  • Matches with more than one arm (Bool, Nat, List helpers): the rest of the caller would have to be copied into every arm.
  • Recursive callees.
  • Callees with more than one non-callback argument left when the match is reached.

None of the fixtures need these.

Cost

+886 ttok. Getting under the cap needs about 846 tokens cut elsewhere in comp.ts, or a raise.

Not run: CUDA, Linux and the JS benches.

🤖 Generated with Claude Code

…e, through a leading one-arm match

A call whose argument in a live position is a source lambda inlines a
non-recursive callee at the call site, so the lambda is applied in place
and no closure is built. Inlining passes through a callee's leading match
on a one-constructor type (tuples, records): def_at puts the callback into
the match arm. A lambda applied directly to its arguments is reduced in
anf. In the emitter, a let bound to a one-arm match moves the rest of the
code into that arm, and a tail one-arm match is emitted directly.

Skipped: multi-arm matches, recursive callees, callees with more than one
non-callback argument left at the match.

Replaces bendlang#1356. Fixtures from bendlang#1288 (by @mizchi) included unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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