Prerequisites. Direct starred prerequisites: Chapter 32. No later core chapter depends on this route.
The wrong handler on the right stack
An operation name does not identify a lexical handler instance. Consider two handlers for one operation 𝖺𝗌𝗄: the outer instance returns 7, and the inner instance returns 9. A function is defined while only the outer instance is in scope: 𝗐𝗂𝗍𝗁 ℎout=𝗁𝖺𝗇𝖽𝗅𝖾𝗋(𝖺𝗌𝗄↦7) 𝗂𝗇𝗅𝖾𝗍 𝑓=𝜆().𝗋𝖺𝗂𝗌𝖾 ℎout 𝖺𝗌𝗄 () 𝗂𝗇𝗐𝗂𝗍𝗁 ℎin=𝗁𝖺𝗇𝖽𝗅𝖾𝗋(𝖺𝗌𝗄↦9) 𝗂𝗇 𝑓(). Lexical resolution records ℎout at the raise site, so the result is 7. A runtime that searches only for the nearest frame named 𝖺𝗌𝗄 selects ℎin and returns 9. Both handlers are live. The failure is loss of binding identity, not absence of a handler.
Let a runtime stack be a finite sequence of frames, written from innermost to outermost. A handler frame has the form 𝗁𝖽𝗅(𝐿,𝐹,𝑐), where 𝐿 is a runtime handler identity, 𝐹 is an operation name, and 𝑐 is its clause. Define 𝖿𝗂𝗇𝖽𝖭𝖺𝗆𝖾(𝐹,𝑆):=the first 𝗁𝖽𝗅(𝐿,𝐹,𝑐) in 𝑆,𝖿𝗂𝗇𝖽𝖨𝖽(𝐿,𝑆):=the first 𝗁𝖽𝗅(𝐿,𝐹,𝑐) in 𝑆. Both functions are partial. Lexical handling uses 𝖿𝗂𝗇𝖽𝖨𝖽 after the source binder has supplied 𝐿; dynamic name search uses 𝖿𝗂𝗇𝖽𝖭𝖺𝗆𝖾.
Referenced from 3 locations
For 𝑆=𝗁𝖽𝗅(𝐿in,𝖺𝗌𝗄,9)::𝖼𝖺𝗅𝗅(𝑓)::𝗁𝖽𝗅(𝐿out,𝖺𝗌𝗄,7)::𝜖, the two searches calculate to 𝖿𝗂𝗇𝖽𝖭𝖺𝗆𝖾(𝖺𝗌𝗄,𝑆)=𝗁𝖽𝗅(𝐿in,𝖺𝗌𝗄,9),𝖿𝗂𝗇𝖽𝖨𝖽(𝐿out,𝑆)=𝗁𝖽𝗅(𝐿out,𝖺𝗌𝗄,7). The compiled program must preserve the second result even when the target represents both handler frames by ordinary stack data.
★☆☆ Insert a third 𝖺𝗌𝗄 handler between 𝖼𝖺𝗅𝗅(𝑓) and the outer handler in 𝑆. Calculate both searches, and state the least datum a compiler must preserve to keep the result 7.
Referenced from 3 locations
The untyped Lexa source machine
The direct compiler begins below typing. This matters: the first preservation theorem is a theorem about observable behavior of an untyped intermediate language, not a source type-safety theorem.
Lexa is the A-normal, closure-converted, hoisted intermediate language of Ma, Ge, Lee, and Zhang. Code labels 𝑃 are static; data labels 𝐿 are generated at run time. Each formal handler has one unary operation, and a captured resumption is dynamically one-shot. Programs contain closed top-level functions, while handler code and handled code receive an explicit closure environment. The calculus is untyped. Its operational semantics is the abstract machine in [MGLZ24].
Referenced from 4 locations
The complete Lexa source-machine rules are collected in subappendix A.31; the calculations below name the rule used at each transition.
The value and term grammar is 𝑐::=𝑖∣𝑃∣𝗇𝗌,𝑣::=𝑥∣𝑐,𝑒::=𝑣∣𝑣1+𝑣2∣𝗇𝖾𝗐𝗋𝖾𝖿(¯𝑣)∣𝜋𝑖(𝑣)∣𝑣1[𝑖]←𝑣2∣𝑣0(¯𝑣)∣𝗁𝖺𝗇𝖽𝗅𝖾 𝑃𝑏 𝗐𝗂𝗍𝗁 𝑃𝑜 𝗎𝗇𝖽𝖾𝗋 𝑣∣𝗋𝖺𝗂𝗌𝖾 𝑣1 𝑣2∣𝗋𝖾𝗌𝗎𝗆𝖾 𝑣1 𝑣2∣𝖾𝗑𝗂𝗍 𝑣,𝑡::=𝑣 𝖾𝗇𝖽∣𝗅𝖾𝗍 𝑥=𝑒 𝗂𝗇 𝑡,𝐺::=𝗅𝖾𝗍𝗋𝖾𝖼 𝑃1=𝜆¯𝑥1.𝑡1,…,𝑃𝑛=𝜆¯𝑥𝑛.𝑡𝑛. The constant 𝗇𝗌 is a nonsense word used to invalidate a consumed resumption. It is not a source exception.
A Lexa configuration is ⟨𝑀∣𝐻∣𝐾∣𝐸∣𝑡⟩. The code memory 𝑀 maps code labels to closed functions. The heap 𝐻 maps data labels to tuples or captured contexts 𝖼𝗈𝗇𝗍(𝐾). A local environment 𝐸 maps variables to values. Frames and contexts are 𝐹::=(𝐸,𝗅𝖾𝗍 𝑥=[] 𝗂𝗇 𝑡)∣𝗁𝖽𝗅(𝐿,𝑃𝑜,𝐿env,[]),𝐾::=𝜖∣𝐾⋅𝐹. Write 𝐶 ⟶𝐶′ for one Lexa-machine transition and 𝐶 ⟶∗𝐶′ for its reflexive–transitive closure.
Referenced from 2 locations
Installing a handler generates its identity. Suppressing unchanged 𝑀,𝐻, the root has the form ⟨𝐾∣𝐸∣𝗅𝖾𝗍 𝑥=𝗁𝖺𝗇𝖽𝗅𝖾 𝑃𝑏 𝗐𝗂𝗍𝗁 𝑃𝑜 𝗎𝗇𝖽𝖾𝗋 𝑣env 𝗂𝗇 𝑡⟩𝐿−𝐻𝑎𝑛𝑑𝑙𝑒⟶⟨𝐾⋅(𝐸,𝗅𝖾𝗍 𝑥=[] 𝗂𝗇 𝑡)⋅𝗁𝖽𝗅(𝐿,𝑃𝑜,𝐿env,[])∣[𝑥env↦𝐿env,𝑥hdl↦𝐿]∣𝑡𝑏⟩, where 𝐿 is fresh, 𝑀(𝑃𝑏) =𝜆(𝑥env,𝑥hdl).𝑡𝑏, and 𝐸(𝑣env) =𝐿env.
In the next two roots only the unchanged code memory 𝑀 is suppressed. Suppose the active context uniquely factors as 𝐾⋅𝗁𝖽𝗅(𝐿,𝑃𝑜,𝐿env,[])⋅𝐾′. Raising to 𝐿 captures the suffix through the suspended let frame: ⟨𝐻∣𝐾⋅𝗁𝖽𝗅(𝐿,𝑃𝑜,𝐿env,[])⋅𝐾′∣𝐸∣𝗅𝖾𝗍 𝑥=𝗋𝖺𝗂𝗌𝖾 𝐿 𝑣 𝗂𝗇 𝑡⟩𝐿−𝑅𝑎𝑖𝑠𝑒⟶⟨𝐻[𝐿𝑘↦𝖼𝗈𝗇𝗍(𝗁𝖽𝗅(𝐿,𝑃𝑜,𝐿env,[])⋅𝐾′⋅(𝐸,𝗅𝖾𝗍 𝑥=[] 𝗂𝗇 𝑡))]∣𝐾∣𝐸𝑜∣𝑡𝑜⟩, where 𝐿𝑘 is fresh, 𝑀(𝑃𝑜) =𝜆(𝑥env,𝑦,𝑘).𝑡𝑜, and 𝐸𝑜 =[𝑥env ↦𝐿env,𝑦 ↦𝐸(𝑣),𝑘 ↦𝐿𝑘]. The factorization is by identity 𝐿. A nearer frame with the same operation name but a different identity remains inside 𝐾′ and is captured rather than selected.
If 𝐻(𝐿𝑘)=𝖼𝗈𝗇𝗍(𝐾′⋅(𝐸′,𝗅𝖾𝗍 𝑥′=[] 𝗂𝗇 𝑡′)), then resumption reinstalls those frames and writes 𝗇𝗌 at 𝐿𝑘: ⟨𝐻∣𝐾∣𝐸∣𝗅𝖾𝗍 𝑥=𝗋𝖾𝗌𝗎𝗆𝖾 𝐿𝑘 𝑣 𝗂𝗇 𝑡⟩𝐿−𝑅𝑒𝑠𝑢𝑚𝑒⟶⟨𝐻[𝐿𝑘↦𝗇𝗌]∣𝐾⋅(𝐸,𝗅𝖾𝗍 𝑥=[] 𝗂𝗇 𝑡)⋅𝐾′∣𝐸′[𝑥′↦𝐸(𝑣)]∣𝑡′⟩. The update makes a second resume stuck in this untyped formal calculus. The implementation has a separately stated, limited multishot extension; it is not part of the 2024 theorem.
Let the stack 𝑆 from definition 34.1 be the handler-bearing part of 𝐾, and raise to 𝐿out. Rule L-Raise factors at the outer frame. The captured suffix therefore begins with the inner frame: 𝐾′=𝗁𝖽𝗅(𝐿in,𝖺𝗌𝗄,9)⋅𝖼𝖺𝗅𝗅(𝑓)⋅𝜖. The selected code is the clause stored at 𝐿out, so it returns 7. If that clause resumes, the inner frame becomes active again. Lexical selection and deep resumption are therefore separate actions.
Referenced from 2 locations
★☆☆ Starting from a heap with 𝐻(𝐿𝑘) =𝖼𝗈𝗇𝗍(𝐾′), apply L-Resume twice to the same 𝐿𝑘. State the heap after the first step and identify the failed premise at the second attempt.
Referenced from 3 locations
Salt: ordinary instructions and three trampolines
The target does not acquire a distinguished handler instruction. Salt is an assembly-like machine with heap-allocated stacks. Its words, operands, and instruction sequences are ℓ::=𝐿∣𝑃∣𝗇𝖾𝗑𝗍(ℓ),𝑤::=ℓ∣𝑖∣𝗇𝗌,𝑜::=𝑟∣𝑤,𝜄::=𝖺𝖽𝖽 𝑟,𝑜∣𝗆𝗄𝗌𝗍𝗄 𝑟∣𝗌𝖺𝗅𝗅𝗈𝖼 𝑖∣𝗌𝖿𝗋𝖾𝖾 𝑖∣𝗆𝖺𝗅𝗅𝗈𝖼 𝑟𝑑,𝑖∣𝗆𝗈𝗏 𝑟𝑑,𝑜∣𝗅𝗈𝖺𝖽 𝑟𝑑,[𝑟𝑠+𝑖]∣𝗌𝗍𝗈𝗋𝖾 [𝑟𝑑+𝑖],𝑜∣𝗉𝗎𝗌𝗁 𝑜∣𝗉𝗈𝗉 𝑟∣𝖼𝖺𝗅𝗅 𝑜∣𝗃𝗆𝗉 𝑜∣𝗋𝖾𝗍𝗎𝗋𝗇∣𝗁𝖺𝗅𝗍,𝐼::=𝜖∣𝜄;𝐼. A Salt state is ⟨𝑀 ∣𝐻 ∣𝑅⟩. Its register file has distinguished stack and instruction pointers 𝗌𝗉 and 𝗂𝗉. If 𝐻(𝐿) =𝗇𝗂𝗅 ::𝑤1 ::⋯ ::𝑤𝑚, then 𝐿𝑚 names its top. In particular, 𝗆𝗈𝗏 𝗌𝗉,𝐿𝑗 both changes stacks and truncates the selected stack above 𝐿𝑗.
The target of the published translation is exactly the Salt machine just displayed. It has general heap, register, call, return, and stack operations; it has no handler-specific opcode. The translated program adds code labels 𝑃𝗁𝖺𝗇𝖽𝗅𝖾, 𝑃𝗁𝖺𝗇𝖽𝗅𝖾_𝗌𝗉𝖾𝖼𝗂𝖺𝗅, 𝑃𝗋𝖺𝗂𝗌𝖾, and 𝑃𝗋𝖾𝗌𝗎𝗆𝖾. The machine and the complete translation are fixed by [MGLZ24].
Referenced from 4 locations
For an ordinary handler annotation 𝐴, abbreviate the displayed semantic translation by TΓ(𝑒):=T𝖫𝖾𝗑𝖺→𝖲𝖺𝗅𝗍(𝑒)Γ and its register-valued form by T𝑟Γ(𝑣):=T𝖫𝖾𝗑𝖺→𝖲𝖺𝗅𝗍(𝑣)𝑟Γ. The three source forms compile at their redexes as follows: TΓ(𝗁𝖺𝗇𝖽𝗅𝖾 𝑃𝑏 𝗐𝗂𝗍𝗁 𝑃𝑜 𝗎𝗇𝖽𝖾𝗋 𝑣env)=T𝑟4Γ(𝐴); T𝑟3Γ(𝑣env);𝗆𝗈𝗏 𝑟2,𝑃𝑜; 𝗆𝗈𝗏 𝑟1,𝑃𝑏; 𝖼𝖺𝗅𝗅 𝑃𝗁𝖺𝗇𝖽𝗅𝖾,TΓ(𝗋𝖺𝗂𝗌𝖾 𝑣1 𝑣2)=T𝑟2Γ(𝑣2); T𝑟1Γ(𝑣1); 𝖼𝖺𝗅𝗅 𝑃𝗋𝖺𝗂𝗌𝖾,TΓ(𝗋𝖾𝗌𝗎𝗆𝖾 𝑣1 𝑣2)=T𝑟2Γ(𝑣2); T𝑟1Γ(𝑣1); 𝖼𝖺𝗅𝗅 𝑃𝗋𝖾𝗌𝗎𝗆𝖾. These equations expose where lexical identity travels: 𝑟1 contains an exchanger address for raise and a one-cell resumption object for resume. A trampoline is the short target routine that switches stacks around a handle, raise, or resume action.
The handle trampoline saves the parent 𝗌𝗉, allocates a new stack, and pushes a four-word header 𝑃𝑜::𝐿env::𝐴::ℓexch. The exchanger initially points to the parent stack. Its body call therefore runs on a fresh stack whose header remembers exactly which operation code and closure environment belong to this handler instance. On normal return the trampoline pops the exchanged parent pointer, removes the other three header words, switches back, and returns.
At each reachable translated handler header, the exchanger contains either the top of its parent stack or the top of the suspended resumption stack. The raise and resume trampolines exchange these alternatives without copying the captured frames.
Referenced from 2 locations
Proof of Proposition 34.6 — The exchanger invariant
Proof. Immediately after 𝑃𝗁𝖺𝗇𝖽𝗅𝖾 constructs the header, the fourth word is the saved parent 𝗌𝗉. On raise, 𝑃𝗋𝖺𝗂𝗌𝖾 loads that word, stores the current 𝗌𝗉 there, and moves the loaded word into 𝗌𝗉. Thus the header now points to the suspended stack while execution has returned to the handler stack. The fresh resumption object points to the same exchanger. On resume, 𝑃𝗋𝖾𝗌𝗎𝗆𝖾 loads the exchanger through that object, loads its saved stack top, stores the current handler-stack top back into the exchanger, and switches to the loaded top. These are the only instructions that alter an exchanger after construction, so induction over target steps establishes the alternatives. ◻
The one-shot update is equally concrete. The first two resume instructions are 𝗅𝗈𝖺𝖽 𝑟3,[𝑟1];𝗌𝗍𝗈𝗋𝖾 [𝑟1],𝗇𝗌. Only then does the trampoline dereference the exchanger in 𝑟3 and switch stacks. A second use reads 𝗇𝗌, matching the failed premise of the Lexa L-Resume rule.
★★☆ Let a handler header’s exchanger contain 𝐿𝑚𝑝, while the active handled stack has top 𝐿𝑛ℎ. Trace the exchanger and 𝗌𝗉 through one raise and one resume. State which location the resumption object contains after raise and why the second resume cannot reach a well-formed exchanger.
Referenced from 3 locations
What the 2024 theorem proves
For either machine, a program behavior is 𝐵::=𝖼𝗈𝗇𝗏𝖾𝗋𝗀𝖾(𝑖)∣𝗌𝗍𝗎𝖼𝗄∣𝖽𝗂𝗏𝖾𝗋𝗀𝖾. Convergence reaches the language’s designated final state with integer 𝑖; stuckness reaches a nonfinal state with no successor; divergence is an infinite reduction. Semantic preservation deliberately excludes stuck source programs: 𝐺⇓𝐵 ∧ 𝐵≠𝗌𝗍𝗎𝖼𝗄⟹T𝖫𝖾𝗑𝖺→𝖲𝖺𝗅𝗍(𝐺)⇓𝐵.
Let R relate source and target states. Suppose initial states are related, related final states have the same integer, and 𝐶𝑠R𝐶𝑡𝐶𝑠⟶𝐶′𝑠⟹∃𝐶′𝑡. 𝐶𝑡⟶+𝐶′𝑡 ∧ 𝐶′𝑠R𝐶′𝑡. Then the translation preserves convergence and divergence.
Referenced from 4 locations
Proof of Lemma 34.7 — Positive simulation criterion
Proof. For a finite source run, induction concatenates the nonempty target segments. The final-state premise supplies the same integer. For an infinite source run, repeatedly choose the target segment supplied by simulation. Every segment is nonempty, so their concatenation is an infinite target run. The claim says nothing about source stuckness: a stuck source state supplies no step to simulate. ◻
The 2024 translation from the untyped Lexa program signature of definition 34.2 to the Salt signature of definition 34.5 preserves every non-stuck observable behavior.
Referenced from 5 locations
Proof of Theorem 34.8 — Lexa-to-Salt semantic preservation
Proof boundary. This is the published theorem of [MGLZ24]. Its configuration relation simultaneously relates source instruction position, evaluation context and environment, heaps and captured resumptions, and code memory. The source and target initial states are related; related final states expose the same integer; and every Lexa step is matched by one or more Salt steps. Lemma 34.7 then yields the stated behaviors. The appendix proof is imported at those exact signatures; it is not reproduced as a new type theorem or as correctness of the closure-conversion, LLVM, garbage collector, or native-code pipeline. ◻
★★☆ For each of the following claims, say whether theorem 34.8 proves it: preservation of a terminating integer result; preservation of divergence; reflection of target stuckness; source type safety; correctness of LLVM code generation. Cite the premise or the missing signature in each case.
Referenced from 3 locations
A typed zero-mainline-overhead refinement
The 2025 development is not a typed retrofit of the preceding theorem. It uses a new source calculus 𝖲𝖫, a new target calculus 𝖳𝖫, and a distinct simulation relation.
In 𝖲𝖫, handlers bind generative lexical labels, functions may abstract over capability variables and label variables, and applications instantiate both. Types include 𝜏::=𝗎𝗇𝗂𝗍∣∀[¯𝛼;¯ℓ:¯𝐹].(¯𝜏)𝑇⟶𝜏∣𝖼𝗈𝗇𝗍𝑇(𝜏,𝜏), where 𝑇 records a captured capability variable or labels. Typing and translation have the joint shape Θ∣Δ∣Σ∣Γ⊢𝑡:𝜏⇝𝑡――. The target 𝖳𝖫 erases source labels and capabilities from terms; a raise carries a clue, and call and handler frames carry statically derived call-site metadata used only while searching. This is the system of [MGJZ25].
Referenced from 2 locations
A target clue is ⟨𝑞,𝐹⟩, with 𝑞::=̂𝑖∣˚𝑖∣∞. Here ̂𝑖 follows the callee’s 𝑖-th label parameter, ˚𝑖 follows its 𝑖-th capability parameter, and ∞ searches a captured label of effect 𝐹. Call-site metadata has the form H=𝑇0;𝑇;¯ℓ:¯𝐹, recording the callee’s capture set, capability instantiations, and label instantiations. The partial hopper function 𝗁𝗈𝗉𝗉𝖾𝗋H rewrites a clue when search crosses the call frame.
For example, if the callee’s label parameter ℓ𝑖 :𝐹 is instantiated by the caller’s label index ̂𝑗, then 𝗁𝗈𝗉𝗉𝖾𝗋H(⟨̂𝑖,𝐹⟩)=⟨̂𝑗,𝐹⟩. If a capability instantiation lists a unique label index with effect name 𝐹, a clue ⟨˚𝑖,𝐹⟩ becomes that label clue. If the matching label is captured, ⟨∞,𝐹⟩ is resolved from the callee’s recorded capture set. The source typing rules require nonambiguity—labels in the relevant capture set have distinct effect names—and nontrivial capability instantiations. Those premises make the needed hopper cases single-valued; the function is not claimed total on arbitrary metadata.
Suppose 𝑔’s label parameter ℓ0 :𝐹 is instantiated at one call site by its caller’s ℓ2 :𝐹, and that caller’s ℓ2 is instantiated one frame higher by ℓ1 :𝐹. Search starts with 𝐶0 =⟨̂0,𝐹⟩. Put 𝐶1 =⟨̂2,𝐹⟩ and 𝐶2 =⟨̂1,𝐹⟩. The two hoppers calculate 𝗁𝗈𝗉𝗉𝖾𝗋H1(𝐶0)=𝐶1,𝗁𝗈𝗉𝗉𝖾𝗋H2(𝐶1)=𝐶2. A nearer handler for a different label of the same effect name is skipped: the clue records provenance through the call sites, rather than the nearest-named 𝐹. At the installing frame the index becomes ̂0, the distinguished signal that this is the selected handler.
Referenced from 2 locations
If a well-typed 𝖲𝖫 configuration M𝑠 is related to a 𝖳𝖫 configuration M𝑡, then each source step is matched by zero or more target steps to a related configuration. Hence, if a closed joint judgment translates 𝑡 to 𝑡―― and source evaluation terminates at 𝑣, target evaluation terminates at a translated value 𝑣――.
Referenced from 4 locations
Proof of Theorem 34.11 — SL-to-TL simulation and terminating preservation
Proof boundary. These are Theorem 1 and Corollary 1 of [MGJZ25]. The relation is stated on a typing-enriched source 𝖲𝖫∗; promises relate static source parameters to target indices, while evidence relates run-time source labels to target clues. The paper’s displayed source typing is deliberately described as strengthening previously sound systems and does not publish a new independent SL type-safety theorem in the main development. We import only the stated simulation and terminating corollary. ◻
For code translated by the 2025 compiler, a mainline call—one executed before any raise begins handler search—neither creates nor passes a reified handler identity and does not run handler search. The compiled implementation places hopper data in a static table keyed by return address; it does not construct or consult that data on mainline calls. Stackwalking and hopper lookup begin only after a raise.
Referenced from 3 locations
Proof of Proposition 34.12 — The exact zero-mainline property
Proof. Inspection of the translation erases source label and capability binders and their application arguments. A target raise retains only its initial clue. The target semantics annotates call frames with H in order to state search, while the implementation recovers the corresponding hopper from the return address and a global data-section table. Thus the metadata is static program data, not a mainline stack or register value. This is a syntactic and implementation-layout statement. The cited work supplies measurements, not a cost semantics, so zero is not a proved equation between running times. ◻
★★☆ Let a callee have label parameters ℓ0 :𝐹,ℓ1 :𝐺, instantiated by the caller’s indices ̂3,̂0. Calculate the hopper results for ⟨̂0,𝐹⟩ and ⟨̂1,𝐺⟩. Then duplicate effect name 𝐹 inside the relevant capture set and explain which nonambiguity premise, rather than an operational rule, rejects the metadata.
Referenced from 3 locations
★★☆ Separate the following into a proved syntactic property, a proved semantic property, and engineering evidence: no reified handler identities on mainline paths; terminating SL-to-TL preservation; benchmark speedups for effect-infrequent programs. Explain why none alone establishes a formal constant-factor cost theorem.
Referenced from 3 locations
Generalised continuations for deep and shallow handlers
Lexical binding is not the only axis in the matrix. Hillerström, Lindley, and Atkey give one calculus in which deep and shallow handlers can be compared without identifying them.
The source 𝜆† is fine-grain call-by-value with value types, computation types 𝐴!𝐸, row-polymorphic effect types, and handler types 𝐶⇒𝛿𝐷,𝛿∈{𝖽𝖾𝖾𝗉,†}. Its characteristic terms are 𝗋𝖾𝗍𝗎𝗋𝗇 𝑉, 𝗅𝖾𝗍 𝑥 ←𝑀 𝗂𝗇 𝑁, 𝖽𝗈 ℓ 𝑉, and 𝗁𝖺𝗇𝖽𝗅𝖾𝛿𝑀 𝗐𝗂𝗍𝗁 𝐻. A deep resumption reinstalls its handler; a shallow resumption does not.
The target of the higher-order CPS translation is the untyped two-level calculus of Figure 9 in [HLA20]. Dynamic terms include two-argument application 𝑈@𝑉@𝑊, 𝖺𝗉𝗉 𝑉 𝑊, and 𝗅𝖾𝗍 𝑟 =𝗋𝖾𝗌𝛿𝑉 𝗂𝗇 𝑁. A generalised continuation is a nonempty stack 𝜅=⟨𝜃,⟨𝜒ret,𝜒ops⟩⟩::𝜅′, whose top frame separates a pure-frame stack 𝜃, a return clause 𝜒ret, and an operation dispatcher 𝜒ops. The translation is higher-order: static abstractions and applications are reduced while translating, while underlined dynamic constructs remain target code.
Referenced from 3 locations
Writing C[ −] for the Figure 10 higher-order translation, its load-bearing clauses are C[𝗋𝖾𝗍𝗎𝗋𝗇 𝑉]=𝜆𝜅. 𝖺𝗉𝗉 (↓𝜅) C[𝑉],C[𝗁𝖺𝗇𝖽𝗅𝖾𝛿𝑀 𝗐𝗂𝗍𝗁 𝐻]=𝜆𝜅. C[𝑀]@(⟨↑[],C𝛿[𝐻]⟩::𝜅). The operation clause projects the top 𝜒ops, passes it the operation label, payload, and reversed resumption stack, then passes the remaining 𝜅. The deep 𝗋𝖾𝗌 root prepends the captured pure frames to the current top frame, thereby retaining its handler. The shallow 𝗋𝖾𝗌† root instead restores the captured pure and handler frames ahead of the caller’s continuation; it does not reinstall the handler that captured the operation. This is the exact semantic difference, not a flag erased from the proof.
At the card of definition 34.13:
𝜆† has type soundness;
the higher-order CPS translation positively simulates every source step, has a backward simulation for terminating target runs, and therefore preserves and reflects termination at translated values;
the deep-to-shallow and shallow-to-deep encodings each have their own type and simulation results; and
parameterised handlers locally translate to ordinary deep handlers.
Referenced from 4 locations
Proof of Theorem 34.14 — The generalised-continuation endpoints
Proof boundary. These are, respectively, Theorem 1 (§3.4), Theorem 7 and Lemma 7 with Corollary 1 (§5.4.4), Theorems 2–3 (§4.1) and Theorems 4–5 (§4.2), and Theorem 9 (§7) of [HLA20]. The CPS target is untyped, so item 2 is not a target type-preservation theorem. Parameterised handlers extend a continuation frame with a state component and their CPS translation threads it; the paper explicitly notes that this is not a zero-cost CPS translation for the other variants. None of these statements mentions lexical handler identity, Lexa, Salt, SL, or TL. ◻
★★☆ An operation is captured under a handler 𝐻 with pure suffix 𝜃. Describe the continuation installed by a deep resume and by a shallow resume. Which one can handle a second occurrence of the same operation after resumption? Explain why theorem 34.14 supplies two simulation cases rather than an equation identifying the resumptions.
Referenced from 3 locations
A typed CPS route, kept separate
There is also a higher-level typed compiler. Schuster, Brachthäuser, and Ostermann translate their lexical-handler calculus Λ𝖼𝖺𝗉 to pure System F [SBMO22]. Its region and capability discipline gives source progress and preservation; the typed CPS translation has a target-typing theorem and a source-to-target operational simulation. Precisely, source progress and preservation are Theorems 4–5, effect safety is Corollary 6, evidence correspondence is Corollary 7, translated-term typing is Theorem 8 in §4.1, simulation is Theorem 10 in §4.2, and evaluation is Corollary 11 of [SBMO22]. The target is System F, not Salt or 𝖳𝖫, and the source is neither the untyped Lexa IR nor 2025 𝖲𝖫. Consequently these theorems do not fill a typing premise in theorem 34.8, and the Lexa-to-Salt theorem does not validate this CPS translation.
Three implementation choices
Once the theorem cards are fixed, the implementation trade-off is visible. Capability passing makes handler authority an explicit value, so ordinary calls transmit that value and a raise follows it directly. Tunnelling gives effect-polymorphic or parametric code an authority boundary that prevents an unrelated intermediate handler from intercepting the operation. Direct Lexa reifies a lexical handler instance by its stack address; ordinary execution passes the address, and raise reaches the instance in constant-time stack switching. Zero Lexa erases that run-time identity from mainline paths and reconstructs its provenance by stackwalking only when an effect is raised. The last two therefore optimize opposite frequency regimes; neither dominates without a workload and cost model.
| Mechanism |
Mainline datum |
Raise action |
Formal target |
Proved boundary |
| Capability passing |
capability |
apply/follow value |
source-specific |
typing/translation card |
| Tunnelling |
effect authority |
cross oblivious code |
source-specific |
tunnelling card |
| Direct Lexa |
stack address |
switch at exchanger |
Salt |
non-stuck behavior |
| Zero Lexa |
none dynamic |
stackwalk hoppers |
𝖳𝖫 |
typed simulation |
| Typed CPS |
CPS capability |
invoke continuation |
System F |
typing and simulation |
Handler families and translation boundaries
| Family |
|
|
|
| interface |
|
|
|
| here |
|
|
|
| boundary |
|
|
|
| Deep algebraic |
handler reinstalled |
𝜆† card |
higher-order CPS; deep/shallow encoding |
| Shallow algebraic |
handler not reinstalled |
𝜆† card |
higher-order CPS; deep/shallow encoding |
| Parameterized |
handler state parameter |
𝜆† extension |
local translation to deep |
| Row-typed |
effects tracked by rows |
𝜆† card |
higher-order CPS at that signature |
| Lexical, untyped |
generative identity; one-shot |
Lexa/Salt |
behavior preservation |
| Lexical, typed |
label/capability capture |
SL/TL |
typed simulation |
| Higher-order |
operations accept computations |
comparison only |
none added |
| Lexical CPS |
region/capability discipline |
Λ𝖼𝖺𝗉/F |
typed CPS simulation |
The remaining none added entry is mathematical information. Deep versus shallow handling changes whether the handler surrounds a resumed computation. Parameterized handlers pass state through clauses; row types classify possible operations; higher-order signatures admit computations inside operation parameters. None of those choices follows merely from lexical binding. A generalized continuation translation or a typed CPS compiler must therefore state its own source grammar, target grammar, typing theorem, and simulation. The four frozen cards above provide no universal handler compiler.
Executable evidence and source boundary
The executable supplement checks the chapter’s finite calculations: name search versus identity search, exchanger selection, one-shot invalidation, two hopper rewrites, and absence of search instructions from a mainline trace. These tests are witness calculations for the displayed models. They do not rerun the native Lexa compiler or reproduce published benchmark tables. Appendix E records the frozen sources, toolchains, integrity pins, and reproduction boundary. The published measurements remain cited engineering evidence, kept separate from theorem 34.8, theorem 34.11.
This chapter used the extended 2024 operational account and proof, the 2025 typed zero-overhead account and its extended appendix, and the typed Λ𝖼𝖺𝗉-to-System-F development. It has not proved whole-compiler correctness, principal effect inference, a universal deep/shallow translation, or a cost theorem for every handler implementation.
★★☆ Choose one row of section 34.9 whose translation entry is none added. List the four signatures that a new preservation theorem would have to fix before it could be compared with the Lexa-to-Salt theorem. Why is changing only the handler’s resumption convention already enough to invalidate a proof by citation?
Referenced from 3 locations
Suggested first pass.
Begin with exercise 34.9, exercise 34.10; continue with exercise 34.11; then use the cost model and executable project to test the implementation boundary. No problem in this optional seminar is a prerequisite for a later chapter.
★★★ Formalize stacks as lists and prove: if handler identities are unique, the factorization at a requested identity is unique. Give a counterexample after identities are replaced by operation names. State precisely whether the failure is nonexistence or nonuniqueness.
Referenced from 4 locations
★★★ Prove lemma 34.7 in full. Your divergence case must explain why a zero-step target match would be insufficient and why source stuckness is outside the conclusion.
Referenced from 4 locations
★★★ Construct metadata for two labels of the same effect name reachable through one capability parameter. Show the two possible successor clues. Then strengthen the metadata judgment with the published nonambiguity condition and prove that the corresponding hopper case is a partial function.
Referenced from 4 locations
★★☆ Design a two-parameter symbolic cost comparison. Let 𝑛 be the number of ordinary calls and 𝑚 the number of raises; charge direct Lexa 𝑑 per ordinary call and 𝑟𝑑 per raise, and zero Lexa 𝑧 per ordinary call and 𝑟𝑧(ℎ) for a raise crossing ℎ frames. Derive the inequality under which zero Lexa wins. Explain why this calculation is a model chosen for the exercise, not a theorem imported from benchmark measurements.
Referenced from 3 locations
★★★ Practical project.lexical-handler-traces Implement the finite checker and trace generator from section 34.10. The oracle must distinguish nearest-name from requested-identity lookup, reject a consumed resumption, calculate two successive hopper clues, and certify that an effect-free mainline trace contains no handler search. Add two mutants: replace identity lookup by name lookup, and omit resumption invalidation. Each mutant must fail a named oracle. The mainline case checks membership in a hand-written instruction list; it is not a compiler-generated trace. Emit one named oracle result for each finite calculation so this boundary is inspectable. Appendix E records the commands, and appendix F gives the implementation stages.
Referenced from 4 locations