From c322ef60bf452b217605ed0707f8e9585bcf6361 Mon Sep 17 00:00:00 2001 From: Joseph Ferano Date: Fri, 11 Sep 2026 12:24:05 +0700 Subject: [PATCH] A handler's capture is the non-escaping kind The refusal said milestone 5, which lumped it in with escaping closures - and plan.org has just deferred those until a concrete use case appears. A handler frame does not outlive the function that pushed it, so what a handler clause needs is spec-memory.md's case 2, a non-escaping fn capturing by value into a stack environment, which is settled rather than deferred. Worth recording before anyone schedules it, because open decision #5 says in as many words that without this conditions are not worth building, and the deferral it just received does not apply to it. --- NEXT.md | 16 ++++++++++++---- lib/check.ml | 14 ++++++++------ 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/NEXT.md b/NEXT.md index b20e314..781a365 100644 --- a/NEXT.md +++ b/NEXT.md @@ -923,10 +923,18 @@ Three decisions worth keeping: Which gives the two refusals, both by the house rule rather than by accident: - **A handler cannot see the establishing function's locals.** That is a - closure with an explicit environment — milestone 5's work — so a reference to - one is refused *for that reason* rather than reported as an unknown name. - Globals and the condition are in scope, which is what the accumulation case - needs. + closure with an explicit environment, so a reference to one is refused *for + that reason* rather than reported as an unknown name. Globals and the + condition are in scope, which is what the accumulation case needs. + + What it needs is narrower than it looks, and worth getting right before + anyone schedules it: a handler frame does not outlive the function that + established it, so this is spec-memory.md's **case 2** — a non-escaping `fn` + capturing by value into a stack environment — and *not* the escaping closure + that plan.org's open decision #5 defers until a concrete use case. Case 2 is + settled, and #5 says in as many words that without it "conditions are not + worth building". So the biggest usability limit in conditions is not behind + the thing that was just deferred. - **`return` inside a `handler-bind` body is refused.** The frames are popped on the way out and an early exit would leave them on the stack pointing into a function that has gone. Same shape as `defer` inside a block. diff --git a/lib/check.ml b/lib/check.ml index c6ee9df..4fb170e 100644 --- a/lib/check.ml +++ b/lib/check.ml @@ -126,9 +126,11 @@ let lookup ctx name = List.assoc_opt name ctx.scope (* A handler clause is lifted into a function of its own, so the establishing function's locals are simply not there. Capturing them is a closure with an - explicit environment — milestone 5's work — and until it exists a reference - to one is refused for the reason it is really refused for, rather than as a - name nobody has heard of. *) + explicit environment — spec-memory.md's case 2, a non-escaping [fn] capturing + by value into a stack environment, since a handler frame does not outlive the + function that pushed it — and until that exists a reference to one is refused + for the reason it is really refused for, rather than as a name nobody has + heard of. *) let captured ctx loc name = if ctx.in_handler && List.mem_assoc name ctx.outer then raise @@ -539,9 +541,9 @@ and block ctx ?want loc body = function of its own and reached through a pointer. Which means it cannot see the establishing function's locals. Capturing them - is a closure with an explicit environment, which is real work and is - milestone 5's; until then a reference to one is rejected by name rather than - silently resolving to something else. Globals and the condition itself are + is a closure with an explicit environment — the non-escaping kind, captured + by value onto this frame — and until that exists a reference to one is + rejected by name rather than silently resolving to something else. Globals and the condition itself are in scope, which is enough for the accumulation case §1 is about. The body may not [return] either. The frames are pushed and popped around