You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Proposal for bend.ts: content-keyed template instance names, for parallel and cached checking #1633
AGENTS.md keeps bend2/bend.ts human-written, so this is a proposal for the team, not a PR. The measurements are on main (e5527d5). A companion issue, #1632, covers error messages.
Idea.book_valid(book, done) already treats the first done entries as validated. So a worker can check a contiguous range [a, b) by loading the file, keeping book.order up to b and calling book_valid(book, a). The result of a parallel check is then the failure with the smallest entry index. Every entry before it passed, so its context is the sequential one, and its error is the one the sequential check reports. Workers past that index can be cancelled. Loading is cheap next to checking (13–112 ms against up to 3.7 s here). And since HTerms hold closures and can't cross workers, each worker reloading from source is the natural design. That part needs no change to bend.ts.
Measured (contiguous ranges, 8 workers simulated in one process, maine5527d5):
File
Sequential
Slowest of 8
Speed-up
bend-mathlib sort.bend
148 ms
79 ms
1.9×
typed/examples.bend
2,476 ms
1,816 ms
1.4×
dynamic_array/proof.bend
541 ms
424 ms
1.3×
aes/inverse.bend
3,719 ms
4,935 ms
0.8×
The gains are capped by two things:
single heavy defs (one def is 51–61% of inverse.bend and examples.bend);
each worker re-checking the template instances it calls.
def_inst names an instance by first use (tm.k + "~" + String(is.size)), so the same instance gets different names in different workers, and in different runs, and its check can't be shared.
Proposed bend.ts change: name an instance by its content, for example tm.k + "~" + <short hash of key> (key is already computed there). With deterministic names:
workers, and separate runs, can share an instance's checked result, which is what parallel checking needs to beat sequential on template-heavy files;
per-instance caching gets stable keys;
error messages and -o output stop depending on check order.
The cost is that instance names in messages and -o text become hash-based (e.g. keep~3f2a instead of keep~0).
My suggestion is to treat this as an enabler. Cross-file caching of whole import prefixes doesn't need it: #1631 does that today for --check-only. Per-instance caching, and parallel checking that beats sequential on template-heavy files, both do.
Found and measured with Claude Code, running Claude Opus 5.5, during an audit of --verdict.
A local prototype on my fork to test the feasibility of #1633's idea. Not a
PR — bend2/bend.ts is human-written per AGENTS.md, and both #1632/#1633 say
they're proposals for the team. This branch just makes the idea concrete and
measurable.
with a collision guard (an O(n) scan of the bucket) that in practice never
fires at 64 bits.
Measured
Order-independence, on a template keep(~T: Type, x: T) used at U32 and Bool under two def orders:
instance
before (U32 first)
before (Bool first)
after (both orders)
keep @ U32
keep~0
keep~1
keep~1ke1chrsw52qf
keep @ Bool
keep~1
keep~0
keep~1rvzz5wxctext
Before: the same U32 instance is ~0 or ~1 depending only on which def is
checked first — so parallel workers (and separate runs) can't share it.
After: the name is a pure function of the arguments. Identical md5 of the
checked book across repeated runs.
The C and JS lanes still emit, build and run correctly with the hashed names
(keep~<base36> sanitizes exactly like today's keep~0).
Regression: full tests/**.bend--check-only sweep (1661 files), before vs
after — identical: 588 pre-existing failures both ways, 0 new failures, 0
changed. (Those 588 fail --check-only for reasons unrelated to naming —
run-lane tests etc. — but the set is byte-identical before and after, so the
change is a no-op on the suite.)
Tradeoffs / notes for the team
Name length. base-36 64-bit ≈ 13–16 chars (keep~1ke1chrsw52qf) vs the
issue's sketched keep~3f2a. Shorter is prettier but needs a collision
check + a deterministic tiebreak to stay order-independent; 64-bit makes the
guard dead code. Truncating to ~48 bits (~10 chars) at base36 would still be
collision-safe for any real file.
It aligns with an existing convention.safe.ts already names its own
elaborated constants content-keyed (k + "~" + F.k, safe.ts:337). This brings def_inst's instance names in line with that.
Companion, not duplicate: #1632 is message wording, this is instance naming.
The overlap is exactly the stability point above — content-keyed names make the
instance names that appear in messages deterministic, which is a prerequisite
for #1632's diagnostics (and any per-instance cache) to be stable. They remain
two independent changes to bend.ts.
Thank you for prototyping this, and for measuring it. It's good to see the names come out the same whichever def is checked first, the checked book identical across runs, and the test sweep unchanged.
Activity
adeerkhan commentedon Oct 18, 2026
Prototype: content-keyed template instance names (#1633)
A local prototype on my fork to test the feasibility of #1633's idea. Not a
PR —
bend2/bend.tsis human-written per AGENTS.md, and both #1632/#1633 saythey're proposals for the team. This branch just makes the idea concrete and
measurable.
Prototype branch (on my fork, no PR): https://github.com/adeerkhan/bend/tree/proto/1633-content-keyed-instances
The diff is one function —
hash_key+ the two lines indef_inst.The change (one function)
def_instalready computes a contentkeyfor each instance (the~arguments,
term_key'd and joined). It then names the instance by first-useorder:
The prototype names it by a 64-bit FNV-1a hash of that same
keyinstead:with a collision guard (an O(n) scan of the bucket) that in practice never
fires at 64 bits.
Measured
Order-independence, on a template
keep(~T: Type, x: T)used atU32andBoolunder two def orders:keep~0keep~1keep~1ke1chrsw52qfkeep~1keep~0keep~1rvzz5wxctext~0or~1depending only on which def ischecked first — so parallel workers (and separate runs) can't share it.
md5of thechecked book across repeated runs.
(
keep~<base36>sanitizes exactly like today'skeep~0).tests/**.bend--check-onlysweep (1661 files), before vsafter — identical: 588 pre-existing failures both ways, 0 new failures, 0
changed. (Those 588 fail
--check-onlyfor reasons unrelated to naming —run-lane tests etc. — but the set is byte-identical before and after, so the
change is a no-op on the suite.)
Tradeoffs / notes for the team
keep~1ke1chrsw52qf) vs theissue's sketched
keep~3f2a. Shorter is prettier but needs a collisioncheck + a deterministic tiebreak to stay order-independent; 64-bit makes the
guard dead code. Truncating to ~48 bits (~10 chars) at base36 would still be
collision-safe for any real file.
safe.tsalready names its ownelaborated constants content-keyed (
k + "~" + F.k, safe.ts:337). This bringsdef_inst's instance names in line with that.its name for cross-worker / cross-run caching, and
-o/ error output stopsdepending on check order. That's the enabler both this issue and Proposals for bend.ts: clearer messages for seven common mistakes #1632 nod at:
Proposals for bend.ts: clearer messages for seven common mistakes #1632's clearer messages become reproducible only if the names they quote are
stable.
Relationship to #1632
Companion, not duplicate: #1632 is message wording, this is instance naming.
The overlap is exactly the stability point above — content-keyed names make the
instance names that appear in messages deterministic, which is a prerequisite
for #1632's diagnostics (and any per-instance cache) to be stable. They remain
two independent changes to
bend.ts.ScottMoore0 commentedon Oct 18, 2026
Thank you for prototyping this, and for measuring it. It's good to see the names come out the same whichever def is checked first, the checked book identical across runs, and the test sweep unchanged.