Prerequisites. Direct starred prerequisites: none. No later core chapter depends on this route.
An exception discards the rest of a computation. It cannot save that rest, pass it to a function, and invoke it twice. A continuation is a value representing that remaining computation; it can be saved, passed to a function, and invoked more than once. A control operator captures or invokes continuations, enabling early return, backtracking, and coroutines; under propositions as types it also validates classical principles which have no intuitionistic proof.
The mechanism reifies the current call-by-value evaluation context as a value. Under propositions as types this value implements double-negation elimination, and hence excluded middle.
A stack that can become a value
The complete control-machine and proof-term rule sheets are collected in subappendix A.32; the main text derives the rules needed by each worked trace.
We write 𝑒⇝0𝑒′ for one root contraction. We extend the call-by-value simply typed calculus of chapter 2. Products, sums, the empty type, unit, and arrows retain their earlier rules. We add the base type 𝖭𝖺𝗍, its numerals, and, for the calculations below, 𝖺𝖽𝖽𝑚:𝖭𝖺𝗍→𝖭𝖺𝗍, a primitive value with 𝖺𝖽𝖽𝑚𝑛⇝0𝑚+𝑛. This harmless constant avoids hiding control behind an arithmetic encoding.
We write 𝜋1,𝜋2 for the earlier 𝖿𝗌𝗍,𝗌𝗇𝖽, and write a case with named 𝗂𝗇𝗅 and 𝗂𝗇𝗋 branches. Only the typography changes; the retained typing and reduction rules do not. We retain 𝟎 and 𝟏 for the inherited empty and unit types, and write () for the earlier unit value ⋆.
The new type and surface terms are 𝐴::=⋯∣𝖢𝗈𝗇𝗍𝐴,𝑒::=⋯∣𝗅𝖾𝗍𝖼𝖼𝑘:𝖢𝗈𝗇𝗍𝐴𝗂𝗇𝑒∣𝗍𝗁𝗋𝗈𝗐𝑒1𝗍𝗈𝑒2. The statics adds
Γ,𝑘:𝖢𝗈𝗇𝗍𝐴⊢𝑒:𝐴
Γ⊢𝗅𝖾𝗍𝖼𝖼𝑘:𝖢𝗈𝗇𝗍𝐴𝗂𝗇𝑒:𝐴
T-Letcc
Γ⊢𝑒1:𝐴Γ⊢𝑒2:𝖢𝗈𝗇𝗍𝐴Γ⊢𝐵𝗍𝗒𝗉𝖾
Γ⊢𝗍𝗁𝗋𝗈𝗐𝑒1𝗍𝗈𝑒2:𝐵
T-Throw
The inherited judgments include type formation Γ⊢𝐴𝗍𝗒𝗉𝖾 and the variable rule Var. Contexts are finite maps; weakening and exchange are admissible, and an assumption may be used more than once. Thus every premise of a displayed rule is read under the same ambient context. The result type 𝐵 of a throw is arbitrary because evaluation never returns to the throw site.
A run-time value may additionally be 𝖼𝗈𝗇𝗍(𝐾), where 𝐾 is a stack. This form is not source syntax. The complete run-time value grammar is 𝑣::=()∣𝑛∣𝖺𝖽𝖽𝑚∣𝜆𝑥:𝐴.𝑒∣⟨𝑣,𝑣⟩∣𝗂𝗇𝗅𝑣∣𝗂𝗇𝗋𝑣∣𝖼𝗈𝗇𝗍(𝐾). A frame is one layer of an evaluation context, and a stack is a finite sequence of such frames. Their grammars are 𝐹::=[]𝑒∣𝑣[]∣⟨[],𝑒⟩∣⟨𝑣,[]⟩∣𝜋𝑖[]∣𝗂𝗇𝗅[]∣𝗂𝗇𝗋[]∣𝖼𝖺𝗌𝖾[]𝗈𝖿{𝗂𝗇𝗅𝑥↦𝑒1;𝗂𝗇𝗋𝑦↦𝑒2}∣𝖺𝖻𝗈𝗋𝗍𝐴([])∣𝗍𝗁𝗋𝗈𝗐[]𝗍𝗈𝑒∣𝗍𝗁𝗋𝗈𝗐𝑣𝗍𝗈[],𝐾::=∙∣𝐾;𝐹. A state 𝐾▹𝑒, read “𝐾 evaluates 𝑒,” evaluates a term; a state 𝐾◃𝑣, read “𝐾 returns 𝑣,” returns a value to the top frame.
If 𝖢𝗈𝗇𝗍𝐴 is read operationally as an object-language representation of a refutation of 𝐴, T-Letcc is the classical step known as consequentia mirabilis: from a derivation of 𝐴 under a temporary refutation of 𝐴, conclude 𝐴. Removing T-Letcc, T-Throw, and 𝖢𝗈𝗇𝗍 leaves the inherited intuitionistic simply typed calculus.
The pure transitions are written out because the stack captured by 𝗅𝖾𝗍𝖼𝖼 includes precisely these frames. Values change the direction of the machine: 𝐾▹𝑣𝑀−𝑅𝑒𝑡𝑢𝑟𝑛⟶𝐾◃𝑣. Application and pairs use 𝐾▹𝑒1𝑒2⟶𝐾;[]𝑒2▹𝑒1,𝑀−𝐴𝑝𝑝𝐹𝑢𝑛𝐾;[]𝑒2◃𝑣1⟶𝐾;𝑣1[]▹𝑒2,𝑀−𝐴𝑝𝑝𝐴𝑟𝑔𝐾;(𝜆𝑥:𝐴.𝑒)[]◃𝑣⟶𝐾▹𝑒[𝑣/𝑥],𝑀−𝐵𝑒𝑡𝑎𝐾▹⟨𝑒1,𝑒2⟩⟶𝐾;⟨[],𝑒2⟩▹𝑒1,𝑀−𝑃𝑎𝑖𝑟𝐿(⟨𝑒1,𝑒2⟩isnotavalue)𝐾;⟨[],𝑒2⟩◃𝑣1⟶𝐾;⟨𝑣1,[]⟩▹𝑒2,𝑀−𝑃𝑎𝑖𝑟𝑅𝐾;⟨𝑣1,[]⟩◃𝑣2⟶𝐾◃⟨𝑣1,𝑣2⟩,𝑀−𝑃𝑎𝑖𝑟𝑅𝑒𝑡𝐾▹𝜋𝑖𝑒⟶𝐾;𝜋𝑖[]▹𝑒,𝑀−𝑃𝑟𝑜𝑗𝐾;𝜋𝑖[]◃⟨𝑣1,𝑣2⟩⟶𝐾◃𝑣𝑖.𝑀−𝑃𝑟𝑜𝑗𝑅𝑒𝑡 Injections first evaluate their payload. A case first evaluates its scrutinee, then selects and substitutes: 𝐾▹𝗂𝗇𝗅𝑒⟶𝐾;𝗂𝗇𝗅[]▹𝑒,𝑀−𝐼𝑛𝑙(𝑒isnotavalue)𝐾;𝗂𝗇𝗅[]◃𝑣⟶𝐾◃𝗂𝗇𝗅𝑣,𝑀−𝐼𝑛𝑙𝑅𝑒𝑡𝐾▹𝗂𝗇𝗋𝑒⟶𝐾;𝗂𝗇𝗋[]▹𝑒,𝑀−𝐼𝑛𝑟(𝑒isnotavalue)𝐾;𝗂𝗇𝗋[]◃𝑣⟶𝐾◃𝗂𝗇𝗋𝑣,𝑀−𝐼𝑛𝑟𝑅𝑒𝑡 and, writing B for the displayed pair of case branches, 𝐾▹𝖼𝖺𝗌𝖾𝑒𝗈𝖿B⟶𝐾;𝖼𝖺𝗌𝖾[]𝗈𝖿B▹𝑒,𝑀−𝐶𝑎𝑠𝑒𝐾;𝖼𝖺𝗌𝖾[]𝗈𝖿B◃𝗂𝗇𝗅𝑣⟶𝐾▹𝑒1[𝑣/𝑥],𝑀−𝐶𝑎𝑠𝑒𝐿𝐾;𝖼𝖺𝗌𝖾[]𝗈𝖿B◃𝗂𝗇𝗋𝑣⟶𝐾▹𝑒2[𝑣/𝑦].𝑀−𝐶𝑎𝑠𝑒𝑅 Empty elimination evaluates its impossible premise: 𝐾▹𝖺𝖻𝗈𝗋𝗍𝐴(𝑒)𝑀−𝐴𝑏𝑜𝑟𝑡⟶𝐾;𝖺𝖻𝗈𝗋𝗍𝐴([])▹𝑒. There is no return transition for this frame, because no well-typed value has type 𝟎. Unit, numerals, lambda abstractions, and primitive functions are values. The primitive arithmetic return is 𝐾;𝖺𝖽𝖽𝑚[]◃𝑛𝑀−𝐴𝑑𝑑⟶𝐾◃(𝑚+𝑛).
Only the following transitions mention control: 𝐾▹𝗅𝖾𝗍𝖼𝖼𝑘:𝖢𝗈𝗇𝗍𝐴𝗂𝗇𝑒⟶𝐾▹𝑒[𝖼𝗈𝗇𝗍(𝐾)/𝑘],𝑀−𝐶𝑎𝑝𝑡𝑢𝑟𝑒𝐾▹𝗍𝗁𝗋𝗈𝗐𝑒1𝗍𝗈𝑒2⟶𝐾;𝗍𝗁𝗋𝗈𝗐[]𝗍𝗈𝑒2▹𝑒1,𝑀−𝑇ℎ𝑟𝑜𝑤𝐴𝑟𝑔𝐾;𝗍𝗁𝗋𝗈𝗐[]𝗍𝗈𝑒2◃𝑣⟶𝐾;𝗍𝗁𝗋𝗈𝗐𝑣𝗍𝗈[]▹𝑒2,𝑀−𝑇ℎ𝑟𝑜𝑤𝐶𝑜𝑛𝑡𝐾;𝗍𝗁𝗋𝗈𝗐𝑣𝗍𝗈[]◃𝖼𝗈𝗇𝗍(𝐾′)⟶𝐾′◃𝑣.𝑀−𝑅𝑒𝑠𝑡𝑜𝑟𝑒 The last line visibly discards 𝐾, including the two throw frames, and restores 𝐾′. A continuation is persistent: throwing to it does not consume the stored stack.
This is the typed core of the familiar 𝖼𝖺𝗅𝗅/𝖼𝖼 idiom. The term 𝗅𝖾𝗍𝖼𝖼𝑘𝗂𝗇𝑒 captures the current continuation, binds it to 𝑘, and evaluates 𝑒. In particular, the usual typed shape, Peirce’s law, is definable: 𝜆𝑓:(𝐴→𝐵)→𝐴.𝗅𝖾𝗍𝖼𝖼𝑘:𝖢𝗈𝗇𝗍𝐴𝗂𝗇𝑓(𝜆𝑎:𝐴.𝗍𝗁𝗋𝗈𝗐𝑎𝗍𝗈𝑘):((𝐴→𝐵)→𝐴)→𝐴. The inner throw is assigned result type 𝐵. The hypothetical dependent 𝖼𝖺𝗅𝗅𝖼𝖼𝑘 of the final section is a different, explicitly call-by-name construct.
A nonlocal trace
Put 𝐾0:=∙;𝖺𝖽𝖽1[],𝐾1:=𝐾0;𝖺𝖽𝖽2[]. Call by value evaluates 𝑝:=𝖺𝖽𝖽1(𝗅𝖾𝗍𝖼𝖼𝑘:𝖢𝗈𝗇𝗍𝖭𝖺𝗍𝗂𝗇𝖺𝖽𝖽2(𝗍𝗁𝗋𝗈𝗐40𝗍𝗈𝑘)) as follows. The initial application first reaches ∙▹𝑝𝑀−𝐴𝑝𝑝𝐹𝑢𝑛⟶∙;[]𝑒2▹𝖺𝖽𝖽1𝑀−𝑅𝑒𝑡𝑢𝑟𝑛⟶∙;[]𝑒2◃𝖺𝖽𝖽1𝑀−𝐴𝑝𝑝𝐴𝑟𝑔⟶𝐾0▹𝑒2, where 𝑒2=𝗅𝖾𝗍𝖼𝖼𝑘𝗂𝗇𝖺𝖽𝖽2(𝗍𝗁𝗋𝗈𝗐40𝗍𝗈𝑘). Rule M-Capture substitutes the whole outer addition stack: 𝐾0▹𝑒2𝑀−𝐶𝑎𝑝𝑡𝑢𝑟𝑒⟶𝐾0▹𝖺𝖽𝖽2(𝗍𝗁𝗋𝗈𝗐40𝗍𝗈𝖼𝗈𝗇𝗍(𝐾0))𝑀−𝐴𝑝𝑝𝐹𝑢𝑛⟶𝐾0;[]𝑒3▹𝖺𝖽𝖽2𝑀−𝑅𝑒𝑡𝑢𝑟𝑛⟶𝐾0;[]𝑒3◃𝖺𝖽𝖽2𝑀−𝐴𝑝𝑝𝐴𝑟𝑔⟶𝐾1▹𝑒3𝑀−𝑇ℎ𝑟𝑜𝑤𝐴𝑟𝑔⟶𝐾1;𝗍𝗁𝗋𝗈𝗐[]𝗍𝗈𝖼𝗈𝗇𝗍(𝐾0)▹40𝑀−𝑅𝑒𝑡𝑢𝑟𝑛⟶𝐾1;𝗍𝗁𝗋𝗈𝗐[]𝗍𝗈𝖼𝗈𝗇𝗍(𝐾0)◃40𝑀−𝑇ℎ𝑟𝑜𝑤𝐶𝑜𝑛𝑡⟶𝐾1;𝗍𝗁𝗋𝗈𝗐40𝗍𝗈[]▹𝖼𝗈𝗇𝗍(𝐾0)𝑀−𝑅𝑒𝑡𝑢𝑟𝑛⟶𝐾1;𝗍𝗁𝗋𝗈𝗐40𝗍𝗈[]◃𝖼𝗈𝗇𝗍(𝐾0)𝑀−𝑅𝑒𝑠𝑡𝑜𝑟𝑒⟶𝐾0◃40𝑀−𝐴𝑑𝑑⟶∙◃41, where 𝑒3=𝗍𝗁𝗋𝗈𝗐40𝗍𝗈𝖼𝗈𝗇𝗍(𝐾0). The result is 41, not 43. Reinstating 𝐾0 erases the pending 𝖺𝖽𝖽2 frame. An exception can produce the same early return; the difference appears when 𝖼𝗈𝗇𝗍(𝐾0) is stored and invoked again.
Typing frames and states
Fix an ambient answer type 𝑅 for one run. The runtime judgment Γ⊢𝑅𝑒:𝐴 has all the surface rules and the internal continuation rule displayed below. On source terms it agrees with Γ⊢𝑒:𝐴. The frame judgment 𝐹:𝐴⇒𝐵 is defined so that plugging a value 𝑣:𝐴 into 𝐹 produces the next computation of type 𝐵. Its term premises use the fixed ambient 𝑅: frametypingdataandjudgment[]𝑒2⊢𝑅𝑒2:𝐴,[]𝑒2:(𝐴→𝐵)⇒𝐵𝑣[]⊢𝑅𝑣:𝐴→𝐵,𝑣[]:𝐴⇒𝐵⟨[],𝑒2⟩⊢𝑅𝑒2:𝐵,⟨[],𝑒2⟩:𝐴⇒𝐴×𝐵⟨𝑣,[]⟩⊢𝑅𝑣:𝐴,⟨𝑣,[]⟩:𝐵⇒𝐴×𝐵𝜋𝑖[]𝜋𝑖[]:𝐴1×𝐴2⇒𝐴𝑖𝗂𝗇𝗅[]𝗂𝗇𝗅[]:𝐴⇒𝐴+𝐵𝗂𝗇𝗋[]𝗂𝗇𝗋[]:𝐵⇒𝐴+𝐵𝖺𝖻𝗈𝗋𝗍𝐵([])𝖺𝖻𝗈𝗋𝗍𝐵([]):𝟎⇒𝐵𝖼𝖺𝗌𝖾[]𝗈𝖿B⊢𝑅𝑒1:𝐷[𝑥:𝐴],⊢𝑅𝑒2:𝐷[𝑦:𝐵],𝖼𝖺𝗌𝖾[]𝗈𝖿B:𝐴+𝐵⇒𝐷𝗍𝗁𝗋𝗈𝗐[]𝗍𝗈𝑒2⊢𝑅𝑒2:𝖢𝗈𝗇𝗍𝐴,𝗍𝗁𝗋𝗈𝗐[]𝗍𝗈𝑒2:𝐴⇒𝐵𝗍𝗁𝗋𝗈𝗐𝑣𝗍𝗈[]⊢𝑅𝑣:𝐴,𝗍𝗁𝗋𝗈𝗐𝑣𝗍𝗈[]:𝖢𝗈𝗇𝗍𝐴⇒𝐵. The bracketed notation in the case row means the branch has the displayed single free variable and becomes closed when that variable is replaced. The result 𝐵 in the two throw rows is arbitrary, exactly as in T-Throw.
Stacks compose by
∙:𝑅⇒𝑅
K-Empty
𝐾:𝐵⇒𝑅𝐹:𝐴⇒𝐵
𝐾;𝐹:𝐴⇒𝑅
K-Push
The internal value rule and state judgments are
𝐾:𝐴⇒𝑅
⊢𝑅𝖼𝗈𝗇𝗍(𝐾):𝖢𝗈𝗇𝗍𝐴
T-Cont
𝐾:𝐴⇒𝑅⊢𝑅𝑒:𝐴
⊢𝑅𝐾▹𝑒
S-Eval
𝐾:𝐴⇒𝑅⊢𝑅𝑣:𝐴
⊢𝑅𝐾◃𝑣
S-Ret
The subscript is essential: it prevents a continuation captured in a run returning 𝑅 from being installed in a run returning an unrelated type. Only closed runs are needed here. An open machine can instead thread one term context through every frame while retaining the same ambient answer.
★☆☆ Start from the state 𝐿▹𝗍𝗁𝗋𝗈𝗐5𝗍𝗈𝖼𝗈𝗇𝗍(𝐾0), where 𝐿:𝖭𝖺𝗍⇒𝖭𝖺𝗍. Write every machine transition. Repeat from a different stack 𝐿′:𝖭𝖺𝗍⇒𝖭𝖺𝗍, using the same continuation value, and identify the transition which proves that continuations are persistent rather than one-shot.
Proof of Lemma 35.2 — Closed value substitution and canonical continuations
Proof. Substitution is induction on typing. In the T-Letcc case, rename its bound continuation away from 𝑥 and the free variables of 𝑣; apply the induction hypothesis to the body and rebuild T-Letcc. In T-Throw, apply the two induction hypotheses to the value and continuation premises; the arbitrary result type is unchanged. The pure binder case is the same alpha-renaming argument for lambda abstraction, and products, sums, and elimination rules follow premise by premise.
For canonical forms, inspect the value grammar and invert the introduction rule for each form. Lambdas and the primitive 𝖺𝖽𝖽𝑚 are the only arrow values; pairs and the two injections are the only product and sum values; no introduction rule concludes 𝟎; and the only continuation form is 𝖼𝗈𝗇𝗍(𝐾), whose T-Cont premise gives the required stack typing. The ground forms have base types and cannot enter the other cases. ◻
Proof. The proof is by cases on the transition. Pushing a frame factors a stack typing through K-Push; returning to it composes the type promised by the frame with the rest of the stack. Application beta uses substitution. Pair, projection, injection, case, empty elimination, and arithmetic transitions use the corresponding frame row and, for case, substitution into the selected branch. Empty elimination has only its push case. These cases account for every pure transition displayed above.
For capture, inversion gives 𝐾:𝐴⇒𝑅 and 𝑘:𝖢𝗈𝗇𝗍𝐴⊢𝑒:𝐴. Rule T-Cont types 𝖼𝗈𝗇𝗍(𝐾):𝖢𝗈𝗇𝗍𝐴; substitution gives the reduct type 𝐴, so S-Eval restores state type 𝑅.
For the first throw transition, inversion of T-Throw gives 𝑒1:𝐴, 𝑒2:𝖢𝗈𝗇𝗍𝐴, and the arbitrary source result 𝐵. The frame 𝗍𝗁𝗋𝗈𝗐[]𝗍𝗈𝑒2:𝐴⇒𝐵 extends the stack. Returning the first value changes to the second throw frame, whose typing is 𝖢𝗈𝗇𝗍𝐴⇒𝐵. In the final control transition, canonical forms gives 𝑣2=𝖼𝗈𝗇𝗍(𝐾′) with 𝐾′:𝐴⇒𝑅; the stored value has type 𝐴. Hence 𝐾′◃𝑣 has the original state type 𝑅, even though the current stack was discarded. ◻
Proof. An evaluation state containing a value takes the direction-change rule. Every nonvalue has a unique outer constructor and therefore takes its unique displayed push, capture, or throw transition.
For a return state, an empty stack is final. Otherwise inspect its unique top frame. The first application, pair, injection, case, and throw frames advance to their second phase. The second application frame contains a function value, hence a lambda or the appropriate primitive by canonical forms. Projection receives a pair; case receives an injection; the second throw frame receives 𝖼𝗈𝗇𝗍(𝐾′) by lemma 35.2. Thus each has exactly one transition. An abort frame would have to receive a value of type 𝟎, which canonical forms excludes. The frame grammar has no remaining case. ◻
★★☆ Reconstruct the three throw cases of theorem 35.3. Write the type of each intermediate stack, including the arbitrary result type of the discarded throw site, and show where canonical forms is required.
The stack judgment 𝐾:𝐴⇒𝑅 is represented in the target by a function 𝐴⋆→𝑅. This is continuation-passing style (CPS): each computation receives an extra function describing what to do with its result, and calls that function instead of returning directly. The target is the pure simply typed calculus with the same base types, products, sums, and empty type 𝟎, but without 𝖢𝗈𝗇𝗍, 𝗅𝖾𝗍𝖼𝖼, or 𝗍𝗁𝗋𝗈𝗐. Its compatible 𝛽𝛿-reduction is strongly normalizing.
Proof of Proposition 35.6 — Normalization of the CPS target
Proof. The reducibility proof of theorem 2.43 applies unchanged to products, sums, unit, empty type, and arrows. Interpret the new base type 𝖭𝖺𝗍 by the strongly normalizing terms of that type. Numerals belong to this candidate. To add 𝖺𝖽𝖽𝑚, the only new candidate obligation says that 𝖺𝖽𝖽𝑚𝑡 is strongly normalizing whenever 𝑡 is. Compatible reduction of a finite term is finitely branching, with successors canonically enumerated by the finitely many redex positions and the finite rule list. Thus no choice function is used. The elementary finitely-branching form of König’s lemma says that an unbounded-height reduction tree would have an infinite branch; strong normalization excludes that branch, so the tree from 𝑡 has a maximum branch length. Induction on that height handles steps inside 𝑡; when 𝑡 is the numeral 𝑛, the sole new root step is 𝖺𝖽𝖽𝑚𝑛⟶𝛽𝛿𝑚+𝑛, whose reduct is a numeral. Thus the fundamental reducibility lemma, and hence strong normalization, extends to the target used here. ◻
The value translation 𝐴𝗏 and computation translation 𝐴𝖼 are 𝟎𝗏=𝟎,𝟏𝗏=𝟏,𝖭𝖺𝗍𝗏=𝖭𝖺𝗍,(𝐴×𝐵)𝗏=𝐴𝗏×𝐵𝗏,(𝐴+𝐵)𝗏=𝐴𝗏+𝐵𝗏,(𝐴→𝐵)𝗏=𝐴𝗏→𝐵𝖼,(𝖢𝗈𝗇𝗍𝐴)𝗏=𝐴𝗏→𝟎,𝐴𝖼=(𝐴𝗏→𝟎)→𝟎. For Γ=𝑥1:𝐴1,…,𝑥𝑛:𝐴𝑛, put Γ𝗏=𝑥1:𝐴𝗏1,…,𝑥𝑛:𝐴𝗏𝑛. The fixed target answer 𝟎 records that a CPS computation returns only by calling its supplied continuation.
With 𝟎 read as falsehood, the computation type is the explicit double-negation translation 𝐴𝖼=¬¬𝐴𝗏. This is a call-by-value CPS translation rather than Kolmogorov’s formula translation: sums, products, and arrows are translated first as value types and the outer double negation is added to a computation. The theorem proved here is typed source-to-target simulation. No converse translation, reflection of provability, or completeness theorem for a separately presented classical proof system is claimed.
The translation separates source values from computations. On values it is homomorphic except at functions and reified stacks: 𝑥𝗏=𝑥,()𝗏=(),𝑛𝗏=𝑛,⟨𝑣1,𝑣2⟩𝗏=⟨𝑣𝗏1,𝑣𝗏2⟩,(𝗂𝗇𝗅𝑣)𝗏=𝗂𝗇𝗅𝑣𝗏,(𝗂𝗇𝗋𝑣)𝗏=𝗂𝗇𝗋𝑣𝗏,(𝜆𝑥:𝐴.𝑒)𝗏=𝜆𝑥:𝐴𝗏.𝑒𝖼. In chapter 22, a superscript 𝗏 named the call-by-value translation into CBPV. Here 𝗏 and 𝖼 name the value and computation parts of this CPS translation. For every source value, the computation clause is uniformly 𝑣𝖼=𝜆𝑘.𝑘𝑣𝗏. The composite clauses below apply only when their whole subject is not a value. This disjointness matches the value-first side conditions of M-PairL, M-Inl, and M-Inr, so the translation is a function on syntax rather than two overlapping equations. The arithmetic function has the exact value translation 𝖺𝖽𝖽𝗏𝑚=𝜆𝑛:𝖭𝖺𝗍.𝜆𝑘:𝖭𝖺𝗍→𝟎.𝑘(𝖺𝖽𝖽𝑚𝑛). A stack value is translated below, because its function depends on the final answer continuation of the whole run.
The translation 𝑒𝖼:𝐴𝖼 is defined by 𝑣𝖼=𝜆𝑘.𝑘𝑣𝗏(𝑣asourcevalue),(𝑒1𝑒2)𝖼=𝜆𝑘.𝑒𝖼1(𝜆𝑓.𝑒𝖼2(𝜆𝑎.𝑓𝑎𝑘)),⟨𝑒1,𝑒2⟩𝖼=𝜆𝑘.𝑒𝖼1(𝜆𝑎.𝑒𝖼2(𝜆𝑏.𝑘⟨𝑎,𝑏⟩))(⟨𝑒1,𝑒2⟩notavalue),(𝜋𝑖𝑒)𝖼=𝜆𝑘.𝑒𝖼(𝜆𝑝.𝑘(𝜋𝑖𝑝)),(𝗂𝗇𝗅𝑒)𝖼=𝜆𝑘.𝑒𝖼(𝜆𝑎.𝑘(𝗂𝗇𝗅𝑎))(𝑒notavalue),(𝗂𝗇𝗋𝑒)𝖼=𝜆𝑘.𝑒𝖼(𝜆𝑏.𝑘(𝗂𝗇𝗋𝑏))(𝑒notavalue). For a case with branches 𝑥.𝑒1 and 𝑦.𝑒2, (𝖼𝖺𝗌𝖾𝑒𝗈𝖿{𝗂𝗇𝗅𝑥↦𝑒1;𝗂𝗇𝗋𝑦↦𝑒2})𝖼=𝜆𝑘.𝑒𝖼(𝜆𝑠.𝖼𝖺𝗌𝖾𝑠𝗈𝖿{𝗂𝗇𝗅𝑥↦𝑒𝖼1𝑘;𝗂𝗇𝗋𝑦↦𝑒𝖼2𝑘}). Empty elimination has the clause (𝖺𝖻𝗈𝗋𝗍𝐴(𝑒))𝖼=𝜆𝑘:𝐴𝗏→𝟎.𝑒𝖼(𝜆𝑧:𝟎.𝑧). The outer continuation is unreachable; the target empty eliminator is the identity continuation on 𝟎. Finally, the two control clauses are (𝗅𝖾𝗍𝖼𝖼𝑘:𝖢𝗈𝗇𝗍𝐴𝗂𝗇𝑒)𝖼=𝜆𝜅:𝐴𝗏→𝟎.(𝑒𝖼[𝜅/𝑘])𝜅,(𝗍𝗁𝗋𝗈𝗐𝑒1𝗍𝗈𝑒2)𝖼=𝜆𝜅.𝑒𝖼1(𝜆𝑎.𝑒𝖼2(𝜆𝑤.𝑤𝑎)). The second clause deliberately ignores 𝜅. It nevertheless evaluates 𝑒1 before 𝑒2, matching the source stack machine.
The machine example has a short CPS image. For fresh ℎ:𝖭𝖺𝗍→𝟎, set 𝑞:=𝜆𝑎.𝖺𝖽𝖽𝗏1𝑎ℎ. Unfolding the application and capture clauses, then contracting administrative beta redexes, gives 𝑝𝖼ℎ⟶∗𝛽𝛿((𝖺𝖽𝖽2(𝗍𝗁𝗋𝗈𝗐40𝗍𝗈𝑘))𝖼[𝑞/𝑘])𝑞⟶∗𝛽𝛿𝑞40⟶∗𝛽𝛿𝖺𝖽𝖽𝗏140ℎ⟶∗𝛽𝛿ℎ41. The continuation supplied while translating 𝖺𝖽𝖽2 is ignored by the translated throw, exactly as the machine discarded the pending 𝖺𝖽𝖽2 frame. Thus the target calculation displays the same 41, rather than 43, as the source trace.
Fix a source result type 𝑅 and a fresh target variable ℎ:𝑅𝗏→𝟎. Extend the translations to runtime syntax by 𝑣𝗏ℎ:=𝑣𝗏,𝑒𝖼ℎ:=𝑒𝖼 on surface values and terms, recursively using the same structural clauses, and put 𝖼𝗈𝗇𝗍(𝐾)𝗏ℎ:=𝐾𝗄ℎ,𝖼𝗈𝗇𝗍(𝐾)𝖼ℎ:=𝜆𝑤.𝑤𝐾𝗄ℎ. Thus the runtime extension, unlike the surface translation, is indexed by the final continuation. The translation 𝐾𝗄ℎ:𝐴𝗏→𝟎 of a stack 𝐾:𝐴⇒𝑅 is defined by ∙𝗄ℎ=ℎ,(𝐾;[]𝑒)𝗄ℎ=𝜆𝑓.𝑒𝖼ℎ(𝜆𝑎.𝑓𝑎𝐾𝗄ℎ),(𝐾;𝑣[])𝗄ℎ=𝜆𝑎.𝑣𝗏ℎ𝑎𝐾𝗄ℎ,(𝐾;⟨[],𝑒⟩)𝗄ℎ=𝜆𝑎.𝑒𝖼ℎ(𝜆𝑏.𝐾𝗄ℎ⟨𝑎,𝑏⟩),(𝐾;⟨𝑣,[]⟩)𝗄ℎ=𝜆𝑏.𝐾𝗄ℎ⟨𝑣𝗏ℎ,𝑏⟩,(𝐾;𝜋𝑖[])𝗄ℎ=𝜆𝑝.𝐾𝗄ℎ(𝜋𝑖𝑝),(𝐾;𝗂𝗇𝗅[])𝗄ℎ=𝜆𝑎.𝐾𝗄ℎ(𝗂𝗇𝗅𝑎),(𝐾;𝗂𝗇𝗋[])𝗄ℎ=𝜆𝑏.𝐾𝗄ℎ(𝗂𝗇𝗋𝑏). For a case frame with branches 𝑥.𝑒1 and 𝑦.𝑒2, (𝐾;𝖼𝖺𝗌𝖾[]𝗈𝖿𝐶)𝗄ℎ=𝜆𝑠.𝖼𝖺𝗌𝖾𝑠𝗈𝖿{𝗂𝗇𝗅𝑥↦(𝑒1)𝖼ℎ𝐾𝗄ℎ;𝗂𝗇𝗋𝑦↦(𝑒2)𝖼ℎ𝐾𝗄ℎ}. The abort frame discards its unreachable surrounding stack: (𝐾;𝖺𝖻𝗈𝗋𝗍𝐴([]))𝗄ℎ=𝜆𝑧:𝟎.𝑧. The two throw frames forget the continuation outside the throw site: (𝐾;𝗍𝗁𝗋𝗈𝗐[]𝗍𝗈𝑒)𝗄ℎ=𝜆𝑎.𝑒𝖼ℎ(𝜆𝑤.𝑤𝑎),(𝐾;𝗍𝗁𝗋𝗈𝗐𝑣𝗍𝗈[])𝗄ℎ=𝜆𝑤.𝑤𝑣𝗏ℎ. The translations of machine states are ‖𝐾▹𝑒‖ℎ:=𝑒𝖼ℎ𝐾𝗄ℎ,‖𝐾◃𝑣‖ℎ:=𝐾𝗄ℎ𝑣𝗏ℎ.
For runtime syntax at a fixed final continuation ℎ, (𝑒[𝑣/𝑥])𝖼ℎ=𝑒𝖼ℎ[𝑣𝗏ℎ/𝑥]. For surface syntax the subscripts erase, giving the ordinary source-value substitution equation.
Proof. Induct on 𝑒, alpha-renaming lambda and 𝗅𝖾𝗍𝖼𝖼 binders before descending. Every structural clause distributes substitution to its subterms. In the control clauses, a continuation variable is an ordinary target variable of function type 𝐴𝗏→𝟎. The internal continuation case is the same target substitution after replacing the stack by its function. ◻
The fresh ℎ is necessary. There is no closed target function 𝑅𝗏→𝟎 in general, so an empty source stack cannot be represented by a closed target continuation. Strong normalization holds for well-typed open target terms as well, so keeping ℎ free costs nothing.
Proof. The three claims are simultaneous induction on runtime value, term, frame, and stack typing. A variable or base value is passed to a continuation of its value type. The internal T-Cont case uses the stack induction hypothesis to type 𝐾𝗄ℎ:𝐴𝗏→𝟎. For a lambda, the induction hypothesis gives 𝑒𝖼ℎ:𝐵𝖼 under 𝑥:𝐴𝗏; hence the value inside the outer continuation has type 𝐴𝗏→𝐵𝖼=(𝐴→𝐵)𝗏. For application, the successive binders in 𝜆𝑘.𝑒𝖼1(𝜆𝑓.𝑒𝖼2(𝜆𝑎.𝑓𝑎𝑘)) have types 𝐵𝗏→𝟎, 𝐴𝗏→𝟎, and (𝐴𝗏→𝐵𝖼)→𝟎, in that order from the inside out. Thus the whole term has type 𝐵𝖼.
Pairing sequences values of types 𝐴𝗏 and 𝐵𝗏 before passing their pair. Projection passes an 𝐴𝗏×𝐵𝗏 value to a target projection. Injection and case use the corresponding target sum rules; in the two case branches the corresponding induction hypothesis is applied under the same assumption 𝑘:𝐶𝗏→𝟎. For empty elimination, apply the premise translation to 𝜆𝑧:𝟎.𝑧; its result is 𝟎, so abstracting the unused continuation gives 𝐴𝖼. Variables, abstractions, applications, pairs, projections, injections, case, and empty elimination exhaust the pure source constructors.
For T-Letcc, the target variable 𝜅:𝐴𝗏→𝟎=(𝖢𝗈𝗇𝗍𝐴)𝗏 has exactly the type assigned to the translated source variable. The body induction hypothesis types 𝑒𝖼[𝜅/𝑘]:(𝐴𝗏→𝟎)→𝟎; applying it to 𝜅 gives 𝟎, and abstracting over 𝜅 gives 𝐴𝖼. For T-Throw, write the two source premises as 𝑒1:𝐴 and 𝑒2:𝖢𝗈𝗇𝗍𝐴. The innermost target application has 𝑐:𝐴𝗏→𝟎,𝑎:𝐴𝗏,𝑐𝑎:𝟎. It is a continuation accepted by 𝑒𝖼2; that result is a continuation accepted by 𝑒𝖼1. The unused outer binder may have type 𝐵𝗏→𝟎, so the result is 𝐵𝖼 for the arbitrary source type 𝐵.
For the stack cases, each clause of definition 35.9 has as domain the translated input of its top frame and calls 𝐾𝗄ℎ only with the frame’s translated output. The abort frame and two throw clauses have result 𝟎 without calling 𝐾𝗄ℎ, exactly as their arbitrary frame result requires. Applying the term or value translation to the resulting function proves the state claim. ◻
Proof of Theorem 35.12 — Positive-step machine simulation
Proof. Expand the term and top-frame translations. The direction change for a value is (𝜆𝑘.𝑘𝑣𝗏ℎ)𝐾𝗄ℎ⟶𝛽𝐾𝗄ℎ𝑣𝗏ℎ. The application push is (𝑒1𝑒2)𝖼ℎ𝐾𝗄ℎ⟶𝛽(𝑒1)𝖼ℎ(𝜆𝑓.(𝑒2)𝖼ℎ(𝜆𝑎.𝑓𝑎𝐾𝗄ℎ)), which is exactly the translation of 𝐾;[]𝑒2▹𝑒1. Returning a function contracts the outer binder of this displayed continuation; returning its argument contracts the next binder and then target beta. By lemma 35.10, the endpoint is (𝑒[𝑣/𝑥])𝖼ℎ𝐾𝗄ℎ, the translation of the source beta state.
The remaining pure pushes are not implicit. Expanding their outer CPS binders gives ‖𝐾▹⟨𝑒1,𝑒2⟩‖ℎ⟶𝛽‖𝐾;⟨[],𝑒2⟩▹𝑒1‖ℎ,‖𝐾▹𝜋𝑖𝑒‖ℎ⟶𝛽‖𝐾;𝜋𝑖[]▹𝑒‖ℎ,‖𝐾▹𝗂𝗇𝗅𝑒‖ℎ⟶𝛽‖𝐾;𝗂𝗇𝗅[]▹𝑒‖ℎ,‖𝐾▹𝗂𝗇𝗋𝑒‖ℎ⟶𝛽‖𝐾;𝗂𝗇𝗋[]▹𝑒‖ℎ,‖𝐾▹𝖼𝖺𝗌𝖾𝑒𝗈𝖿𝐶‖ℎ⟶𝛽‖𝐾;𝖼𝖺𝗌𝖾[]𝗈𝖿𝐶▹𝑒‖ℎ,‖𝐾▹𝖺𝖻𝗈𝗋𝗍𝐴(𝑒)‖ℎ⟶𝛽‖𝐾;𝖺𝖻𝗈𝗋𝗍𝐴([])▹𝑒‖ℎ. Returning the first pair component contracts the 𝑎-binder and produces the second pair frame; returning the second contracts the 𝑏-binder and produces 𝐾𝗄ℎ⟨𝑣𝗏1,𝑣𝗏2⟩. Projection return contracts the frame binder and the target projection. Injection return contracts its payload binder. Either case return contracts the scrutinee binder, takes the matching target sum contraction, and uses CPS substitution in the selected branch. No abort return is typable.
For the primitive, put 𝑠𝖺𝖽𝖽:=𝐾;𝖺𝖽𝖽𝑚[]◃𝑛. Also abbreviate the translated frame by 𝐿ℎ:=𝜆𝑎.𝖺𝖽𝖽𝗏𝑚𝑎𝐾𝗄ℎ. The generic application-frame clause and the displayed translation of 𝖺𝖽𝖽𝑚 give ‖𝑠𝖺𝖽𝖽‖ℎ𝑓𝑟𝑎𝑚𝑒𝑡𝑟𝑎𝑛𝑠𝑙𝑎𝑡𝑖𝑜𝑛=𝐿ℎ𝑛⟶𝛽𝖺𝖽𝖽𝗏𝑚𝑛𝐾𝗄ℎ⟶+𝛽𝐾𝗄ℎ(𝖺𝖽𝖽𝑚𝑛)⟶𝛽𝛿𝐾𝗄ℎ(𝑚+𝑛)𝑠𝑡𝑎𝑡𝑒𝑡𝑟𝑎𝑛𝑠𝑙𝑎𝑡𝑖𝑜𝑛=‖𝐾◃(𝑚+𝑛)‖ℎ. Thus the primitive source step is represented by beta steps followed by the target primitive’s 𝛿-step. Every pure transition family is now covered.
Capture gives the characteristic equation (𝗅𝖾𝗍𝖼𝖼𝑘𝗂𝗇𝑒)𝖼ℎ𝐾𝗄ℎ⟶𝛽𝑒𝖼ℎ[𝐾𝗄ℎ/𝑘]𝐾𝗄ℎ=(𝑒[𝖼𝗈𝗇𝗍(𝐾)/𝑘])𝖼ℎ𝐾𝗄ℎ, where the equality is the internal case of CPS substitution. The initial throw push is (𝗍𝗁𝗋𝗈𝗐𝑒1𝗍𝗈𝑒2)𝖼ℎ𝐾𝗄ℎ⟶𝛽(𝑒1)𝖼ℎ(𝜆𝑎.(𝑒2)𝖼ℎ(𝜆𝑤.𝑤𝑎)), which is precisely the first throw-frame translation. The next return contracts 𝑎, giving the second evaluation state. If its continuation value is 𝖼𝗈𝗇𝗍(𝐾′), the last return reduces as (𝜆𝑤.𝑤𝑣𝗏ℎ)𝐾′ℎ𝗄⟶𝛽𝐾′ℎ𝗄𝑣𝗏ℎ, the translation of 𝐾′◃𝑣. This enumerates every rule of the machine. ◻
The requirement ⟶+𝛽𝛿, rather than ⟶∗𝛽𝛿, is load bearing. A translation which represented some source steps by no target step would not by itself transfer termination of the deterministic machine.
Proof of Theorem 35.13 — Termination of the λ _ K machine
Proof. Suppose 𝑠0⟶𝑠1⟶⋯ were an infinite well-typed run. Preservation keeps one result type 𝑅. Choose fresh ℎ:𝑅𝗏→𝟎. Positive-step simulation concatenates to an infinite 𝛽𝛿-reduction ‖𝑠0‖ℎ⟶+𝛽𝛿‖𝑠1‖ℎ⟶+𝛽𝛿⋯. CPS typing gives a well-typed pure target term. This contradicts proposition 35.6. ◻
Proof of Corollary 35.14 — Relative consistency of the continuation calculus
Proof. If ⊢𝑒:𝟎, strong normalization and progress take ∙▹𝑒 to a final state ∙◃𝑣 with 𝑣:𝟎. The value grammar has no value of empty type, a contradiction. ◻
★★★ Starting only from the two CPS clauses for 𝗅𝖾𝗍𝖼𝖼 and 𝗍𝗁𝗋𝗈𝗐, derive their target types. Then reproduce the capture case and the three throw cases of theorem 35.12, marking the target step which makes each source simulation positive.
Under propositions as types, read 𝟎 as falsehood ⊥, 𝐴+𝐵 as disjunction 𝐴∨𝐵, and define ¬𝐴:=𝐴→⊥. The relevant intuitionistic natural-deduction rules are
Γ,𝐴⊢⊥
Γ⊢¬𝐴
I
Γ⊢¬𝐴Γ⊢𝐴
Γ⊢⊥
E
Γ⊢𝐴
Γ⊢𝐴∨𝐵
I_1
Γ⊢𝐵
Γ⊢𝐴∨𝐵
I_2
Disjunction elimination is Γ⊢𝐴∨𝐵Γ,𝐴⊢𝐶Γ,𝐵⊢𝐶Γ⊢𝐶(∨𝐸). Empty elimination is Γ⊢⊥Γ⊢𝐶(⊥𝐸), and implication introduction and elimination are the inherited lambda and application rules. These rules do not include Γ⊢¬¬𝐴Γ⊢𝐴(𝖣𝖭𝖤),nordotheyderiveΓ⊢𝐴∨¬𝐴 in general. A two-world Kripke frame 𝑤0≤𝑤1, with 𝑃 forced only at 𝑤1 and ⊥ forced nowhere, refutes 𝑃∨¬𝑃 at 𝑤0: neither 𝑃 nor 𝑃→⊥ is forced there. Kripke soundness for exactly the displayed propositional rules is a rule induction (one case per rule): assumptions are monotone, implication introduction quantifies over extensions, implication elimination and the disjunction rules preserve forcing, and the empty-elimination case is vacuous. Soundness therefore rules out an intuitionistic derivation.
With the displayed ⊥𝐸, the two classical extensions are equivalent. From 𝑛:¬¬𝐴, case-analyze 𝐴∨¬𝐴; the left branch returns 𝐴, and the right branch derives ⊥ as 𝑛𝑞 and eliminates it. Conversely, from 𝑛:¬(𝐴∨¬𝐴), the function 𝜆𝑎.𝑛(𝗂𝗇𝗅𝑎) proves ¬𝐴, so 𝑛(𝗂𝗇𝗋(𝜆𝑎.𝑛(𝗂𝗇𝗅𝑎))) proves ⊥; DNE at 𝐴∨¬𝐴 completes the other direction.
Now read 𝖢𝗈𝗇𝗍𝐴 as an operational program representation of ¬𝐴, not as its definitional equal. It is represented in CPS by 𝐴𝗏→𝟎, and throwing an 𝐴-value to such a continuation produces no local result. The control typing rules then give double-negation elimination directly.
For every type 𝐴, the term 𝖽𝗇𝖾𝐴:=𝜆𝑛:𝖢𝗈𝗇𝗍(𝖢𝗈𝗇𝗍𝐴).𝗅𝖾𝗍𝖼𝖼𝑘:𝖢𝗈𝗇𝗍𝐴𝗂𝗇𝗍𝗁𝗋𝗈𝗐𝑘𝗍𝗈𝑛 has type 𝖢𝗈𝗇𝗍(𝖢𝗈𝗇𝗍𝐴)→𝐴. If 𝑛=𝖼𝗈𝗇𝗍(𝑁), its characteristic machine trace under a stack 𝐾:𝐴⇒𝑅 is 𝐾▹𝗅𝖾𝗍𝖼𝖼𝑘𝗂𝗇𝗍𝗁𝗋𝗈𝗐𝑘𝗍𝗈𝑛𝑀−𝐶𝑎𝑝𝑡𝑢𝑟𝑒⟶𝐾▹𝗍𝗁𝗋𝗈𝗐𝖼𝗈𝗇𝗍(𝐾)𝗍𝗈𝖼𝗈𝗇𝗍(𝑁)𝑀−𝑇ℎ𝑟𝑜𝑤𝐴𝑟𝑔,𝑀−𝑅𝑒𝑡𝑢𝑟𝑛,𝑀−𝑇ℎ𝑟𝑜𝑤𝐶𝑜𝑛𝑡,𝑀−𝑅𝑒𝑡𝑢𝑟𝑛,𝑀−𝑅𝑒𝑠𝑡𝑜𝑟𝑒⟶∗𝑁◃𝖼𝗈𝗇𝗍(𝐾). Thus 𝑛, which claims to refute every refutation of 𝐴, receives the current 𝐴-continuation and must do something with it.
Proof of Proposition 35.15 — Continuation-form double negation
Proof. The body has the complete derivation 𝑛:𝖢𝗈𝗇𝗍(𝖢𝗈𝗇𝗍𝐴),𝑘:𝖢𝗈𝗇𝗍𝐴⊢𝑘:𝖢𝗈𝗇𝗍𝐴𝑛:𝖢𝗈𝗇𝗍(𝖢𝗈𝗇𝗍𝐴),𝑘:𝖢𝗈𝗇𝗍𝐴⊢𝑛:𝖢𝗈𝗇𝗍(𝖢𝗈𝗇𝗍𝐴)𝐴𝗍𝗒𝗉𝖾𝑛:𝖢𝗈𝗇𝗍(𝖢𝗈𝗇𝗍𝐴),𝑘:𝖢𝗈𝗇𝗍𝐴⊢𝗍𝗁𝗋𝗈𝗐𝑘𝗍𝗈𝑛:𝐴T−Throw𝑛:𝖢𝗈𝗇𝗍(𝖢𝗈𝗇𝗍𝐴)⊢𝗅𝖾𝗍𝖼𝖼𝑘:𝖢𝗈𝗇𝗍𝐴𝗂𝗇𝗍𝗁𝗋𝗈𝗐𝑘𝗍𝗈𝑛:𝐴T−Letcc. Lambda introduction derives 𝖢𝗈𝗇𝗍(𝖢𝗈𝗇𝗍𝐴)→𝐴. For the trace, capture substitutes 𝖼𝗈𝗇𝗍(𝐾); the two throw frames evaluate that value and 𝖼𝗈𝗇𝗍(𝑁), after which the last rule of (35.1) discards 𝐾 and restores 𝑁. ◻
Excluded middle uses the same mechanism twice. It first returns a continuation as evidence for the right summand. The only way to refute that evidence is to supply an 𝐴-value; doing so re-enters the original caller with the value in the left summand.
For every type 𝐴, define 𝗅𝖾𝗆𝐴:=𝗅𝖾𝗍𝖼𝖼𝑘:𝖢𝗈𝗇𝗍(𝐴+𝖢𝗈𝗇𝗍𝐴)𝗂𝗇𝗂𝗇𝗅(𝗅𝖾𝗍𝖼𝖼𝑞:𝖢𝗈𝗇𝗍𝐴𝗂𝗇𝗍𝗁𝗋𝗈𝗐(𝗂𝗇𝗋𝑞)𝗍𝗈𝑘). Then ⊢𝗅𝖾𝗆𝐴:𝐴+𝖢𝗈𝗇𝗍𝐴. For every stack 𝐾:𝐴+𝖢𝗈𝗇𝗍𝐴⇒𝑅, put 𝐾′=𝐾;𝗂𝗇𝗅[]. Its first return is 𝐾▹𝗅𝖾𝗆𝐴𝑀−𝐶𝑎𝑝𝑡𝑢𝑟𝑒,𝑀−𝐼𝑛𝑙,𝑀−𝐶𝑎𝑝𝑡𝑢𝑟𝑒,𝑀−𝑇ℎ𝑟𝑜𝑤𝐴𝑟𝑔,𝑀−𝑅𝑒𝑡𝑢𝑟𝑛,𝑀−𝑇ℎ𝑟𝑜𝑤𝐶𝑜𝑛𝑡,𝑀−𝑅𝑒𝑡𝑢𝑟𝑛,𝑀−𝑅𝑒𝑠𝑡𝑜𝑟𝑒⟶∗𝐾◃𝗂𝗇𝗋(𝖼𝗈𝗇𝗍(𝐾′)). If any later stack 𝐿 throws 𝑎:𝐴 to this returned continuation, then 𝐿▹𝗍𝗁𝗋𝗈𝗐𝑎𝗍𝗈𝖼𝗈𝗇𝗍(𝐾′)𝑀−𝑇ℎ𝑟𝑜𝑤𝐴𝑟𝑔,𝑀−𝑅𝑒𝑡𝑢𝑟𝑛,𝑀−𝑇ℎ𝑟𝑜𝑤𝐶𝑜𝑛𝑡,𝑀−𝑅𝑒𝑡𝑢𝑟𝑛,𝑀−𝑅𝑒𝑠𝑡𝑜𝑟𝑒⟶∗𝐾′◃𝑎𝑀−𝐼𝑛𝑙𝑅𝑒𝑡⟶𝐾◃𝗂𝗇𝗅𝑎.
Proof of Proposition 35.16 — Continuation-form excluded middle with a change-of-mind trace
Proof. Put 𝐵:=𝐴+𝖢𝗈𝗇𝗍𝐴 and Γ0:=𝑘:𝖢𝗈𝗇𝗍𝐵,𝑞:𝖢𝗈𝗇𝗍𝐴. The complete derivation of the body is Γ0⊢𝑞:𝖢𝗈𝗇𝗍𝐴Γ0⊢𝗂𝗇𝗋𝑞:𝐵InrΓ0⊢𝑘:𝖢𝗈𝗇𝗍𝐵𝐴𝗍𝗒𝗉𝖾Γ0⊢𝗍𝗁𝗋𝗈𝗐(𝗂𝗇𝗋𝑞)𝗍𝗈𝑘:𝐴T−Throw𝑘:𝖢𝗈𝗇𝗍𝐵⊢𝗅𝖾𝗍𝖼𝖼𝑞:𝖢𝗈𝗇𝗍𝐴𝗂𝗇𝗍𝗁𝗋𝗈𝗐(𝗂𝗇𝗋𝑞)𝗍𝗈𝑘:𝐴T−Letcc𝑘:𝖢𝗈𝗇𝗍𝐵⊢𝗂𝗇𝗅(𝗅𝖾𝗍𝖼𝖼𝑞:𝖢𝗈𝗇𝗍𝐴𝗂𝗇𝗍𝗁𝗋𝗈𝗐(𝗂𝗇𝗋𝑞)𝗍𝗈𝑘):𝐵Inl⊢𝗅𝖾𝗆𝐴:𝐵T−Letcc. The two variable premises are instances of Var; the last premise of T-Throw records its arbitrary result type 𝐴.
Operationally, the outer capture substitutes 𝖼𝗈𝗇𝗍(𝐾). The injection frame is pushed before the inner capture, so that capture yields 𝑞=𝖼𝗈𝗇𝗍(𝐾′). Throwing its right injection to 𝖼𝗈𝗇𝗍(𝐾) discards 𝐾′ and gives the first displayed return. The second calculation is the throw rule followed by the injection-frame return. Both traces include the same caller stack 𝐾; this is why the caller repeats its case analysis and can take the other branch. ◻
Proof of Corollary 35.17 — Double-negation elimination and excluded middle
Proof. For double-negation elimination use 𝜆𝑛:(𝐴→𝟎)→𝟎.𝗅𝖾𝗍𝖼𝖼𝑘:𝖢𝗈𝗇𝗍𝐴𝗂𝗇𝖺𝖻𝗈𝗋𝗍𝐴(𝑛(𝜆𝑎:𝐴.𝗍𝗁𝗋𝗈𝗐𝑎𝗍𝗈𝑘)). The inner lambda has type 𝐴→𝟎 because its throw is assigned result type 𝟎; hence 𝑛 produces 𝟎, which 𝖺𝖻𝗈𝗋𝗍𝐴 eliminates. For excluded middle, case-analyze 𝗅𝖾𝗆𝐴. Map 𝗂𝗇𝗅𝑎 to 𝗂𝗇𝗅𝑎, and map 𝗂𝗇𝗋𝑞, where 𝑞:𝖢𝗈𝗇𝗍𝐴, to 𝗂𝗇𝗋(𝜆𝑎:𝐴.𝗍𝗁𝗋𝗈𝗐𝑎𝗍𝗈𝑞). The latter lambda again has type 𝐴→𝟎. ◻
These propositions prove classical formulas by typed programs. They do not make double-negation elimination or excluded middle a definitional equality of the pure calculus. Their machine reductions capture and reinstate stacks; their CPS images are pure functions with additional beta reductions. Consistency follows from that translation, not from erasing the operational difference between classical and intuitionistic proof terms.
★★☆ Translate 𝗅𝖾𝗆𝐴 to CPS and beta-reduce it to a term of type ((𝐴𝗏+(𝐴𝗏→𝟎))→𝟎)→𝟎. Identify the target subterm which first calls the supplied continuation with a right injection and the occurrence which later calls it with a left injection.
Delimiters, dependent projection, and let-polymorphism
The preceding continuations contain the whole stack. A reset instead marks a boundary, and 𝗌𝗁𝗂𝖿𝗍0 or 𝖼𝗈𝗇𝗍𝗋𝗈𝗅0 captures only the evaluation context inside the nearest boundary. The four translations below are typable only under a specific polymorphic type-and-effect signature, and that signature is the content of this section. No theorem below identifies these calculi with 𝜆𝖪.
The section revisits deep resumption, handler clauses, and effect annotations in a deliberately different, unlabeled ordered-row signature. No theorem identifies its 𝖽𝗈 form with the labeled operations of chapter 22, chapter 25. In the first of those chapters, an operation is 𝗈𝗉ℓ𝑉(𝑥.𝑀), a handler is 𝗁𝖺𝗇𝖽𝗅𝖾𝑀𝗐𝗂𝗍𝗁𝐻, and the annotation 𝐸out is a finite set. In chapter 25, the corresponding forms are 𝗉𝖾𝗋𝖿𝗈𝗋𝗆ℓ𝑣, 𝗁𝖺𝗇𝖽𝗅𝖾𝑒𝗐𝗂𝗍𝗁𝐻, and the unordered row 𝜖. Here they are 𝖽𝗈𝑣, 𝗁𝖺𝗇𝖽𝗅𝖾𝑒{𝑥,𝑟.𝑒ℎ;𝑦.𝑒𝑟}, and the ordered row 𝜌. This last vocabulary is not inherited from either earlier chapter; it is the common core in which the four correspondences below are stated.
A delimited correspondence must record two facts that the whole-stack machine does not: which row entry marks the nearest delimiter, and whether invoking a captured continuation reinstalls that delimiter. The common core makes the row explicit before either control operator is introduced.
Kinds, types, and rows are 𝜅::=𝖳∣𝖤∣𝖱,𝜏::=𝛼𝖳∣𝜏𝜌→𝜏∣∀𝛼::𝜅.𝜏,𝜀::=𝛼𝖤∣𝜀ext,𝜌::=𝛼𝖱∣𝜄∣𝜀⋅𝜌. The rule-name prefix K- denotes kind formation in this comparison signature; it is unrelated to the machine-stack metavariable 𝐾 used in the first part of the chapter. The subscripts on variables are metanotational kind tags, not source punctuation. A metavariable 𝜎 below ranges over any expression of the kind demanded by its judgment. Thus the raw grammar never identifies a function type with a row; 𝜀ext stands for one of the extension-specific effects declared below. Here 𝜄 is the empty row. A row is ordered: the head effect is the one encountered by the nearest matching delimiter. The common terms and evaluation contexts are 𝑣::=𝑥∣𝜆𝑥.𝑒,𝑒::=𝑣∣𝑒𝑒∣[𝑒],𝐸::=[]∣𝐸𝑒∣𝑣𝐸∣[𝐸]. Three similar brackets have different syntactic jobs. The term [𝑒] is a lift, [] is a one-hole evaluation context, and 𝐸[𝑒] denotes plugging 𝑒 into that hole. We retain these source-calculus brackets so the correspondence clauses can be compared directly with their published forms. The following rules generate every well-formed type, effect, and row:
𝛼::𝜅∈Δ
Δ⊢𝛼::𝜅
K-Var
Δ⊢𝜏1::𝖳Δ⊢𝜌::𝖱Δ⊢𝜏2::𝖳
Δ⊢𝜏1𝜌→𝜏2::𝖳
K-Arr
Δ,𝛼::𝜅⊢𝜏::𝖳
Δ⊢∀𝛼::𝜅.𝜏::𝖳
K-All
Rule K-Var handles all three sorted variable cases uniformly. Rows are formed by
Δ⊢𝜄::𝖱
K-Nil
Δ⊢𝜀::𝖤Δ⊢𝜌::𝖱
Δ⊢𝜀⋅𝜌::𝖱
K-Cons
Each extension below contributes its own single-effect formation rule. When a judgment writes a single effect 𝜀 to the right of /, it abbreviates the row 𝜀⋅𝜄. This is the omitted trailing-𝜄 convention used whenever a rule displays a single effect after /.
The judgment Δ;Γ⊢𝑒:𝜏/𝜌 records both result type and evaluation effect. Variables and lambdas are pure; application aligns the latent and evaluation rows:
𝑥:𝜏∈Γ
Δ;Γ⊢𝑥:𝜏/𝜄
P-Var
Δ;Γ,𝑥:𝜏1⊢𝑒:𝜏2/𝜌
Δ;Γ⊢𝜆𝑥.𝑒:𝜏1𝜌→𝜏2/𝜄
P-Lam
Δ;Γ⊢𝑒1:𝜏1𝜌→𝜏2/𝜌Δ;Γ⊢𝑒2:𝜏1/𝜌
Δ;Γ⊢𝑒1𝑒2:𝜏2/𝜌
P-App
Write <: for the kind-indexed preorder, read as subtyping at 𝖳 and subrowing at 𝖱. It is the PPS kinded preorder, not the earlier term-type subtyping judgment that shares its printed glyph. It is generated exactly by
Δ⊢𝜎<:𝜎
Sub-Refl
Δ⊢𝜏21<:𝜏11Δ⊢𝜌1<:𝜌2Δ⊢𝜏12<:𝜏22
Δ⊢(𝜏11𝜌1⟶𝜏12)<:(𝜏21𝜌2⟶𝜏22)
Sub-Arr
Δ,𝛼::𝜅⊢𝜏1<:𝜏2
Δ⊢∀𝛼::𝜅.𝜏1<:∀𝛼::𝜅.𝜏2
Sub-All
The row clauses are
Δ⊢𝜌::𝖱
Δ⊢𝜄<:𝜌
Sub-Nil
Δ⊢𝜌1<:𝜌2
Δ⊢𝜀⋅𝜌1<:𝜀⋅𝜌2
Sub-Cons
Generalization, instantiation, subtyping, and lift are explicit derivation steps:
Δ,𝛼::𝜅;Γ⊢𝑒:𝜏/𝜄𝛼∉𝖥𝖵(Γ)
Δ;Γ⊢𝑒:∀𝛼::𝜅.𝜏/𝜄
P-Gen
Δ⊢𝜎::𝜅Δ;Γ⊢𝑒:∀𝛼::𝜅.𝜏/𝜌
Δ;Γ⊢𝑒:𝜏[𝜎/𝛼]/𝜌
P-Inst
Δ⊢𝜏1<:𝜏2Δ⊢𝜌1<:𝜌2Δ;Γ⊢𝑒:𝜏1/𝜌1
Δ;Γ⊢𝑒:𝜏2/𝜌2
P-Sub
Transitivity is not a primitive rule. Every use of P-Sub invokes one displayed subtype derivation; no hidden transitive chain is needed. The remaining core rule is
Δ⊢𝜀::𝖤Δ;Γ⊢𝑒:𝜏/𝜌
Δ;Γ⊢[𝑒]:𝜏/𝜀⋅𝜌
P-Lift
The lift is a type-and-control mask. If 𝑒:𝜏/𝜌, it inserts an arbitrary well-kinded head effect, yielding [𝑒]:𝜏/𝜀⋅𝜌, while [𝑣]⇝0𝑣 erases an inert mask. Operationally, a context [𝐸] has freeness index one rather than zero, so a capture beneath that lift cannot jump directly to the surrounding delimiter. The translations below preserve each explicit lift; delimiter clauses discharge the effect head introduced by the corresponding source delimiter rule. In this core 𝑒⇝0𝑒′ again denotes a root contraction. The complete core dynamics and contextual closure are (𝜆𝑥.𝑒)𝑣⇝0𝑒[𝑣/𝑥],[𝑣]⇝0𝑣,𝑒⇝0𝑒′𝐸[𝑒]⟶𝐸[𝑒′]. Freeness is generated by 0-𝖿𝗋𝖾𝖾([]),𝑛-𝖿𝗋𝖾𝖾(𝐸)𝑛-𝖿𝗋𝖾𝖾(𝐸𝑒),𝑛-𝖿𝗋𝖾𝖾(𝐸)𝑛-𝖿𝗋𝖾𝖾(𝑣𝐸),𝑛-𝖿𝗋𝖾𝖾(𝐸)(𝑛+1)-𝖿𝗋𝖾𝖾([𝐸]). Capture rules below require a 0-free context, so they cannot cross an unmasked nearer delimiter. Read the index as the balance of unmatched lifts on the path from the hole to the root: application frames preserve it, a lift raises it, and each delimiter rule below discharges one. Hence a 0-free context is exactly balanced for the delimiter whose root rule is being applied.
The notation changes at this boundary. In chapter 4, rows are unique-label and unordered. In chapter 25, 𝜀 is a duplicate-label unordered row and 𝜇 a row variable. Here 𝜀 is one effect, 𝜌 is a duplicate-permitting ordered row, and 𝜇 below binds a recursive effect. The kind context Δ and term context Γ are not the store typing Σ used in the state fragments of earlier chapters. These distinctions, rather than a shared row notation, are used by the translations. The arrow ⇒ in Δ0.𝜏1⇒𝜏2 is the single-effect signature separator, corresponding to the earlier 𝑃⇝𝑅. It is unrelated to the stack/frame typing judgment 𝐹:𝐴⇒𝐵 used in the first two sections.
A deep single effect has form Δ0.𝜏1⇒𝜏2. It may bind variables of all three kinds. Terms add 𝖽𝗈𝑣,𝗁𝖺𝗇𝖽𝗅𝖾𝑒{𝑥,𝑟.𝑒ℎ;𝑦.𝑒𝑟}. Its formation rule, evaluation-context extension, and freeness rule are
Δ,Δ0⊢𝜏1::𝖳Δ,Δ0⊢𝜏2::𝖳
Δ⊢Δ0.𝜏1⇒𝜏2::𝖤
K-DH
(𝑛+1)-𝖿𝗋𝖾𝖾(𝐸)
𝑛-𝖿𝗋𝖾𝖾(𝗁𝖺𝗇𝖽𝗅𝖾𝐸{𝑥,𝑟.𝑒ℎ;𝑦.𝑒𝑟})
Free-DH
Evaluation contexts add 𝐸::=⋯∣𝗁𝖺𝗇𝖽𝗅𝖾𝐸{𝑥,𝑟.𝑒ℎ;𝑦.𝑒𝑟}. Here Δ⊢𝛿::Δ0 means that 𝛿 has exactly the domain of Δ0 and Δ⊢𝛿(𝛼)::Δ0(𝛼) for every bound 𝛼. For a well-kinded substitution 𝛿::Δ0, operation invocation and handling are typed by
If 𝐸 is 0-free, its operation contraction is 𝗁𝖺𝗇𝖽𝗅𝖾𝐸[𝖽𝗈𝑣]{𝑥,𝑟.𝑒ℎ;𝑦.𝑒𝑟}⇝0𝑒ℎ[𝑣/𝑥,(𝜆𝑧.𝗁𝖺𝗇𝖽𝗅𝖾𝐸[𝑧]{𝑥,𝑟.𝑒ℎ;𝑦.𝑒𝑟})/𝑟]. The handler is placed around the captured continuation again; this is the meaning of deep. On a returned value, 𝗁𝖺𝗇𝖽𝗅𝖾𝑣{𝑥,𝑟.𝑒ℎ;𝑦.𝑒𝑟}⇝0𝑒𝑟[𝑣/𝑦].
For 0-free 𝐸, ⟨𝐸[𝗌𝗁𝗂𝖿𝗍0𝑘.𝑒]∣𝑥.𝑒𝑟⟩⇝0𝑒[(𝜆𝑧.⟨𝐸[𝑧]∣𝑥.𝑒𝑟⟩)/𝑘], and ⟨𝑣∣𝑥.𝑒𝑟⟩⇝0𝑒𝑟[𝑣/𝑥]. The captured continuation contains the reset; invoking it is therefore deep-like.
For a concrete root calculation, temporarily add the pure arithmetic constants used in the stack machine and put 𝐸=𝖺𝖽𝖽2[],𝑒=𝑘40,𝑒𝑟=𝑥. The S0-Shift contraction gives 𝖺𝖽𝖽1⟨𝖺𝖽𝖽2(𝗌𝗁𝗂𝖿𝗍0𝑘.𝑘40)∣𝑥.𝑥⟩⇝0𝖺𝖽𝖽1((𝜆𝑧.⟨𝖺𝖽𝖽2𝑧∣𝑥.𝑥⟩)40)⟶∗43. The substituted continuation reinstalls the reset around precisely the inner 𝖺𝖽𝖽2[] context; the outer 𝖺𝖽𝖽1[] frame is not captured.
There are translations 𝖣𝖧(−) from 𝗌𝗁𝗂𝖿𝗍0 to deep handlers and 𝖣𝖣(−) in the reverse direction. They are homomorphic on every construct except the following displayed term clauses: 𝖣𝖧(𝗌𝗁𝗂𝖿𝗍0𝑘.𝑒)=𝖽𝗈(𝜆𝑘.𝖣𝖧(𝑒)),𝖣𝖧(⟨𝑒∣𝑥.𝑒𝑟⟩)=𝗁𝖺𝗇𝖽𝗅𝖾𝖣𝖧(𝑒){𝑓,𝑟.𝑓𝑟;𝑥.𝖣𝖧(𝑒𝑟)},𝖣𝖣(𝖽𝗈𝑣)=𝗌𝗁𝗂𝖿𝗍0𝑘.𝜆ℎ.ℎ𝖣𝖣(𝑣)(𝜆𝑥.𝑘𝑥ℎ),𝖣𝖣(𝗁𝖺𝗇𝖽𝗅𝖾𝑒{𝑥,𝑟.𝑒ℎ;𝑦.𝑒𝑟})=⟨𝖣𝖣(𝑒)∣𝑦.𝜆ℎ.𝖣𝖣(𝑒𝑟)⟩(𝜆𝑥.𝜆𝑟.𝖣𝖣(𝑒ℎ)). For the first single-effect clause, name 𝑇=𝖣𝖧(𝜏),𝑅=𝖣𝖧(𝜌),𝐶𝑎=𝑎𝑅⟶𝑇,𝑄𝑎=∀Δ0.(𝐶𝑎𝑅⟶𝑇). Thus the prefix 𝑎::𝖳. binds the target operation’s answer type, whereas ∀Δ0 re-quantifies the source effect’s parameters inside its argument type. On single effects the clauses are exactly 𝖣𝖧(Δ0.𝜏/𝜌)=𝑎::𝖳.∀Δ0.((𝑎𝖣𝖧(𝜌)←←←←←←←←←←←→𝖣𝖧(𝜏))𝖣𝖧(𝜌)←←←←←←←←←←←→𝖣𝖧(𝜏))⇒𝑎,𝖣𝖣(Δ0.𝜏1⇒𝜏2)=𝛼::𝖳,𝛽::𝖱.(∀Δ0.𝖣𝖣(𝜏1)→(𝖣𝖣(𝜏2)𝛽→𝛼)𝛽→𝛼)𝛽→𝛼/𝛽. Equivalently, the first right-hand side is 𝑎::𝖳.𝑄𝑎⇒𝑎. Here and below an undecorated arrow is pure. The four named operators are syntax-directed translations.
Proof of Lemma 35.22 — Deep-bridge type substitution
Proof. Induct on types and rows. At a quantified effect, alpha-rename every binder away from 𝛼 and ftv(𝜎) before applying the induction hypothesis beneath the binder. ◻
Proof of Lemma 35.23 — Deep-bridge value substitution
Proof. Induct on terms. Alpha-rename lambda, shift, handler, and return-clause binders away from 𝑥 and 𝖥𝖵(𝑣) before descent. For example, choose 𝑘,ℎ,𝑦∉𝖥𝖵(𝖣𝖣(𝑣))∪{𝑥}. The reverse operation clause gives 𝖣𝖣((𝖽𝗈𝑤)[𝑣/𝑥])=𝗌𝗁𝗂𝖿𝗍0𝑘.𝜆ℎ.ℎ𝖣𝖣(𝑤[𝑣/𝑥])(𝜆𝑦.𝑘𝑦ℎ), which is the induction hypothesis substituted into 𝖣𝖣(𝖽𝗈𝑤). The remaining nonhomomorphic clauses commute with capture-avoiding substitution in the same way. ◻
Proof of Lemma 35.24 — Deep-bridge context compatibility
Proof. Use simultaneous induction on the evaluation-context and freeness derivations. Application and lift use the common context constructors. A source delimiter becomes exactly one target delimiter, so its premise raises freeness from 𝑛 to 𝑛+1 in both calculi. ◻
The tempting reverse operation clause 𝖣𝖣bad(𝖽𝗈𝑣)=𝗌𝗁𝗂𝖿𝗍0𝑘.𝜆ℎ.ℎ𝖣𝖣(𝑣)𝑘 does not type-check. The captured 𝑘 returns the handler-code function still expected outside the capture, whereas ℎ requires a resumption that returns the handler result after receiving that code. Closing over ℎ in 𝜆𝑥.𝑘𝑥ℎ rethreads the deep handler on every resumption.
Proof. Translate kinding and subtyping simultaneously; this fixes the scope of ∀Δ0 in the handler-code type. Use lemma 35.25 at every type-substitution boundary.
Formation. Induct simultaneously on kinding and subtyping. Variable, arrow, universal, empty-row, and row-cons formation are homomorphic, as are reflexive, arrow, universal, empty-row, and row-cons subtyping. The homomorphic cases follow their corresponding source constructors. For the translated-effect instance of Sub-Cons, the induction hypothesis on 𝜌1<:𝜌2 gives 𝖣𝖧(𝜌1)<:𝖣𝖧(𝜌2); one target Sub-Cons step then derives 𝖣𝖧(𝜀)⋅𝖣𝖧(𝜌1)<:𝖣𝖧(𝜀)⋅𝖣𝖧(𝜌2). The 𝖣𝖣 direction is identical after replacing each translation symbol.
The two nonhomomorphic effect-formation cases do not follow from those homomorphic clauses. For the forward K-S0-to-K-DH case put 𝑇=𝖣𝖧(𝜏),𝑅=𝖣𝖧(𝜌),𝐶𝑎=𝑎𝑅⟶𝑇,𝑄𝑎=∀Δ0.(𝐶𝑎𝑅⟶𝑇),𝜂=𝑎::𝖳.𝑄𝑎⇒𝑎. The source premises translate to Δ,Δ0⊢𝑇::𝖳 and Δ,Δ0⊢𝑅::𝖱. Under fresh 𝑎::𝖳, write Ξ=Δ,𝑎::𝖳,Δ0. Weakening derives Ξ⊢𝑇::𝖳 and Ξ⊢𝑅::𝖱. The complete target derivation is Ξ⊢𝑎::𝖳Ξ⊢𝑅::𝖱Ξ⊢𝑇::𝖳Ξ⊢𝐶𝑎::𝖳𝐾−𝐴𝑟𝑟.Ξ⊢𝐶𝑎::𝖳Ξ⊢𝑅::𝖱Ξ⊢𝑇::𝖳Ξ⊢𝐶𝑎𝑅⟶𝑇::𝖳𝐾−𝐴𝑟𝑟.Δ,𝑎::𝖳,Δ0⊢𝐶𝑎𝑅⟶𝑇::𝖳Δ,𝑎::𝖳⊢𝑄𝑎::𝖳𝐾−𝐴𝑙𝑙Δ0,Δ,𝑎::𝖳⊢𝑄𝑎::𝖳Δ,𝑎::𝖳⊢𝑎::𝖳Δ⊢𝜂::𝖤𝐾−𝐷𝐻.Δ⊢𝜂::𝖤Δ⊢𝜄::𝖱Δ⊢𝜂⋅𝜄::𝖱𝐾−𝐶𝑜𝑛𝑠. Here 𝐾−𝐴𝑙𝑙Δ0 denotes one displayed K-All step for every binder of Δ0, retaining each declared kind 𝖳, 𝖤, or 𝖱. Thus the universal really scopes the entire function 𝐶𝑎𝑅⟶𝑇, and the result is both a deep effect and, after row cons, a row.
For K-DH-to-K-S0, put 𝐷𝑎,𝑏=𝜏2𝑏→𝑎,𝐺𝑎,𝑏=𝐷𝑎,𝑏𝑏→𝑎,𝐻𝑎,𝑏=∀Δ0.(𝜏1𝜄→𝐺𝑎,𝑏),𝜃=𝑎::𝖳,𝑏::𝖱.(𝐻𝑎,𝑏𝑏→𝑎)/𝑏. The translated K-DH premises give Δ,Δ0⊢𝜏𝑖::𝖳 for 𝑖=1,2. Under fresh 𝑎::𝖳,𝑏::𝖱, write Ω0=Δ,𝑎::𝖳,𝑏::𝖱 and Ω=Ω0,Δ0. Weakening derives Ω⊢𝜏𝑖::𝖳 for 𝑖=1,2. The three K-Arr derivations are Ω⊢𝜏2::𝖳Ω⊢𝑏::𝖱Ω⊢𝑎::𝖳Ω⊢𝐷𝑎,𝑏::𝖳𝐾−𝐴𝑟𝑟.Ω⊢𝐷𝑎,𝑏::𝖳Ω⊢𝑏::𝖱Ω⊢𝑎::𝖳Ω⊢𝐺𝑎,𝑏::𝖳𝐾−𝐴𝑟𝑟.Ω⊢𝜏1::𝖳Ω⊢𝜄::𝖱Ω⊢𝐺𝑎,𝑏::𝖳Ω⊢𝜏1𝜄→𝐺𝑎,𝑏::𝖳𝐾−𝐴𝑟𝑟. The remaining formation tree is Ω⊢𝜏1𝜄→𝐺𝑎,𝑏::𝖳Ω0⊢𝐻𝑎,𝑏::𝖳𝐾−𝐴𝑙𝑙Δ0,Ω0⊢𝐻𝑎,𝑏::𝖳Ω0⊢𝑏::𝖱Ω0⊢𝑎::𝖳Ω0⊢𝐻𝑎,𝑏𝑏→𝑎::𝖳𝐾−𝐴𝑟𝑟,Ω0⊢𝐻𝑎,𝑏𝑏→𝑎::𝖳Ω0⊢𝑏::𝖱Δ⊢𝜃::𝖤𝐾−𝑆0. Consequently K-Cons also gives Δ⊢𝜃⋅𝜄::𝖱. This displays both the polymorphic handler-code kind and the effect/row result required by the reverse translation. ◻
Proof of Lemma 35.27 — shift_0-to-deep type preservation
Proof. Induct on the source typing derivation. Rule P-Gen retains the empty row; P-Inst uses type substitution from lemma 35.25; and P-Sub uses lemma 35.26. Variable, lambda, application, and lift rebuild their corresponding target rules. It remains to check the two nonhomomorphic cases.
For a source effect Δ0.𝜏/𝜌, put 𝑇=𝖣𝖧(𝜏),𝑅=𝖣𝖧(𝜌),𝐶𝑋=𝑋𝑅⟶𝑇,𝑄𝑋=∀Δ0.(𝐶𝑋𝑅⟶𝑇),𝜂=𝑎::𝖳.𝑄𝑎⇒𝑎.
For the control-to-handler implication, the two nonhomomorphic typing cases use 𝑈=𝖣𝖧(𝜏′),𝑅0=𝖣𝖧(𝜌0). For S0-Shift, the induction hypothesis and the translated premise give Δ,Δ0;Γ,𝑘:𝐶𝑈⊢𝖣𝖧(𝑒):𝑇/𝑅0,𝑅0<:𝑅. Alpha-rename every binder of Δ0 away from 𝖥𝖵(Γ); hence each use of P-Gen satisfies its freshness side condition. Apply P-Sub to the evaluation row, then P-Lam, then P-Gen once for each binder in Δ0: Γ,𝑘:𝐶𝑈⊢𝖣𝖧(𝑒):𝑇/𝑅,𝑃−𝑆𝑢𝑏Γ⊢𝜆𝑘.𝖣𝖧(𝑒):𝐶𝑈𝑅⟶𝑇/𝜄,𝑃−𝐿𝑎𝑚Γ⊢𝜆𝑘.𝖣𝖧(𝑒):𝑄𝑈/𝜄.𝑃−𝐺𝑒𝑛 Rule DH-Do uses the explicit instantiation 𝑎↦𝑈 and derives 𝖽𝗈(𝜆𝑘.𝖣𝖧(𝑒)):𝑈/𝜂. Finally, Sub-Cons lifts 𝜄<:𝑅0 beneath 𝜂. Rule P-Sub then gives 𝑈/𝜂⋅𝑅0, the translated conclusion.
For S0-Reset, let 𝛿::Δ0 be its displayed instantiation. The induction hypotheses type the handled computation at 𝑈/𝜂⋅𝛿𝑅 and the return clause at 𝛿𝑇/𝛿𝑅. In the operation clause of DH-Handle, 𝑓:𝑄𝑎,𝑟:𝑎𝛿𝑅⟶𝛿𝑇. Rule P-Inst specializes every binder of 𝑓 by 𝛿, so 𝑓:(𝑎𝛿𝑅⟶𝛿𝑇)𝛿𝑅⟶𝛿𝑇. Lift the two pure variable judgments to 𝛿𝑅 by P-Sub and apply P-App; this derives 𝑓𝑟:𝛿𝑇/𝛿𝑅. Thus DH-Handle has all three premises and returns 𝛿𝑇/𝛿𝑅.
Proof of Lemma 35.28 — Deep-to- shift_0 type preservation
Proof. Induct on the source typing derivation. Common rules are homomorphic, with substitution and subtyping supplied by lemma 35.25, lemma 35.26. It remains to check the operation and handler rules.
For a source effect Δ0.𝜏1⇒𝜏2 with trailing row 𝜄, put 𝐷𝑎,𝑏=𝜏2𝑏→𝑎,𝐺𝑎,𝑏=𝐷𝑎,𝑏𝑏→𝑎,𝐻𝑎,𝑏=∀Δ0.(𝜏1𝜄→𝐺𝑎,𝑏),𝜃=𝑎::𝖳,𝑏::𝖱.(𝐻𝑎,𝑏𝑏→𝑎)/𝑏. The undecorated arrow in definition 35.21 is the 𝜄-arrow in this definition of 𝐻𝑎,𝑏. In a translated DH-Do premise, let 𝛿0::Δ0 be the operation’s instantiation. Under fresh 𝑎,𝑏, P-Inst gives ℎ:𝛿0𝜏1→(𝛿0𝜏2𝑏→𝑎)𝑏→𝑎,𝑘:𝛿0𝜏2𝑏→(𝐻𝑎,𝑏𝑏→𝑎). After lifting pure variables to row 𝑏, repeated P-App and P-Lam derive, in order, 𝑘𝑥ℎ:𝑎/𝑏,𝜆𝑥.𝑘𝑥ℎ:𝛿0𝜏2𝑏→𝑎/𝜄,ℎ𝖣𝖣(𝑣)(𝜆𝑥.𝑘𝑥ℎ):𝑎/𝑏, and hence 𝜆ℎ.ℎ𝖣𝖣(𝑣)(𝜆𝑥.𝑘𝑥ℎ):𝐻𝑎,𝑏𝑏→𝑎/𝜄. Rule S0-Shift, with its binders instantiated by the fresh 𝑎,𝑏, uses 𝜄<:𝑏 and concludes 𝛿0𝜏2/𝜃⋅𝜄.
Finally suppose the source DH-Handle has result 𝜏𝑟/𝜌, and put 𝐻𝑟=∀Δ0.𝜏1→(𝜏2𝜌→𝜏𝑟)𝜌→𝜏𝑟. The translated reset uses the explicit substitution [𝑎↦𝜏𝑟,𝑏↦𝜌]. Its computation premise has type 𝜏/𝜃⋅𝜌, while 𝑥.𝜆ℎ.𝖣𝖣(𝑒𝑟) has type 𝐻𝑟𝜌→𝜏𝑟/𝜌; hence S0-Reset yields 𝐻𝑟𝜌→𝜏𝑟/𝜌. Generalizing the translated operation clause over Δ0 gives 𝜆𝑥.𝜆𝑟.𝖣𝖣(𝑒ℎ):𝐻𝑟/𝜄. One final P-App, after pure-row widening, gives 𝜏𝑟/𝜌. This also explains why the captured resumption is 𝜆𝑥.𝑘𝑥ℎ, not merely 𝑘. ◻
Proof of Lemma 35.29 — Deep-bridge root simulation
Proof. For semantic preservation, write 𝐷={𝑓,𝑟.𝑓𝑟;𝑦.𝖣𝖧(𝑒𝑟)},𝑣𝑐=𝜆𝑧.𝗁𝖺𝗇𝖽𝗅𝖾𝖣𝖧(𝐸)[𝑧]𝐷. The translated 𝗌𝗁𝗂𝖿𝗍0 root is the complete calculation 𝖣𝖧(⟨𝐸[𝗌𝗁𝗂𝖿𝗍0𝑘.𝑒]∣𝑦.𝑒𝑟⟩)=𝗁𝖺𝗇𝖽𝗅𝖾𝖣𝖧(𝐸)[𝖽𝗈(𝜆𝑘.𝖣𝖧(𝑒))]𝐷⟶(𝜆𝑘.𝖣𝖧(𝑒))𝑣𝑐⟶𝛽𝖣𝖧(𝑒)[𝑣𝑐/𝑘]=𝖣𝖧(𝑒[(𝜆𝑧.⟨𝐸[𝑧]∣𝑦.𝑒𝑟⟩)/𝑘]), where the last equality is lemma 35.25. Every step is an ordinary evaluation step.
In the reverse direction put 𝐻=𝜆𝑥.𝜆𝑟.𝖣𝖣(𝑒ℎ),𝑅𝑦=𝜆ℎ.𝖣𝖣(𝑒𝑟),𝑄[𝑧]=⟨𝖣𝖣(𝐸)[𝑧]∣𝑦.𝑅𝑦⟩. Then the translated deep-operation root is 𝖣𝖣(𝗁𝖺𝗇𝖽𝗅𝖾𝐸[𝖽𝗈𝑣]{𝑥,𝑟.𝑒ℎ;𝑦.𝑒𝑟})=⟨𝖣𝖣(𝐸)[𝗌𝗁𝗂𝖿𝗍0𝑘.𝜆ℎ.ℎ𝖣𝖣(𝑣)(𝜆𝑧.𝑘𝑧ℎ)]∣𝑦.𝑅𝑦⟩𝐻⟶(𝜆ℎ.ℎ𝖣𝖣(𝑣)(𝜆𝑧.(𝜆𝑤.𝑄[𝑤])𝑧ℎ))𝐻⟶+𝑖𝐻𝖣𝖣(𝑣)(𝜆𝑧.𝑄[𝑧]𝐻)⟶+𝑖𝖣𝖣(𝑒ℎ)[𝖣𝖣(𝑣)/𝑥,(𝜆𝑧.𝑄[𝑧]𝐻)/𝑟]=𝖣𝖣(𝑒ℎ[𝑣/𝑥,(𝜆𝑧.𝗁𝖺𝗇𝖽𝗅𝖾𝐸[𝑧]{𝑥,𝑟.𝑒ℎ;𝑦.𝑒𝑟})/𝑟]). The contraction of (𝜆𝑤.𝑄[𝑤])𝑧 occurs beneath 𝜆𝑧, so ordinary evaluation-context reduction is insufficient; this is exactly the use of arbitrary-context ⟶𝑖. Handler/reset return roots contract directly and then use substitution. The context and freeness parts of lemma 35.25 lift these root calculations, proving the two stated positive closures. ◻
For every well-formed Δ,Γ,𝜏,𝜌, type-and-effect preservation has two directions. From 𝗌𝗁𝗂𝖿𝗍0, Δ;Γ⊢𝑒:𝜏/𝜌⟹Δ;𝖣𝖧(Γ)⊢𝖣𝖧(𝑒):𝖣𝖧(𝜏)/𝖣𝖧(𝜌). From deep handlers, Δ;Γ⊢𝑒:𝜏/𝜌⟹Δ;𝖣𝖣(Γ)⊢𝖣𝖣(𝑒):𝖣𝖣(𝜏)/𝖣𝖣(𝜌). The two directions differ in the reduction relation the simulation needs:
if 𝑒⟶𝑒′ in 𝗌𝗁𝗂𝖿𝗍0, then 𝖣𝖧(𝑒)⟶+𝖣𝖧(𝑒′) by ordinary evaluation-context reduction;
if 𝑒⟶𝑒′ with deep handlers, then 𝖣𝖣(𝑒)⟶+𝑖𝖣𝖣(𝑒′) under arbitrary compatible closure.
Fix 𝐴::𝖳 and 𝑎:𝐴, and put 𝜀𝑠=∅.𝐴/𝜄,𝜀ℎ=𝑏::𝖳.((𝑏𝜄→𝐴)𝜄→𝐴)⇒𝑏. The empty quantifier in 𝜀𝑠 disappears in 𝜀ℎ. The complete source derivation for 𝑑𝐴=⟨𝗌𝗁𝗂𝖿𝗍0𝑘.𝑘𝑎∣𝑥.𝑥⟩ is
𝐴::𝖳⊢∅::∅
𝑎:𝐴,𝑘:𝐴𝜄→𝐴⊢𝑘:(𝐴𝜄→𝐴)/𝜄
P-Var
𝑎:𝐴,𝑘:𝐴𝜄→𝐴⊢𝑎:𝐴/𝜄
P-Var
𝑎:𝐴,𝑘:𝐴𝜄→𝐴⊢𝑘𝑎:𝐴/𝜄
P-App
𝜄<:𝜄𝐴::𝖳𝜀𝑠⋅𝜄::𝖱
𝑎:𝐴⊢𝗌𝗁𝗂𝖿𝗍0𝑘.𝑘𝑎:𝐴/𝜀𝑠⋅𝜄
S0-Shift
𝑎:𝐴,𝑥:𝐴⊢𝑥:𝐴/𝜄
P-Var
𝑎:𝐴⊢𝑑𝐴:𝐴/𝜄
S0-Reset
Put 𝐷𝗁={𝑓,𝑟.𝑓𝑟;𝑥.𝑥}. Its translation is ℎ𝐴=𝗁𝖺𝗇𝖽𝗅𝖾𝖽𝗈(𝜆𝑘.𝑘𝑎)𝐷𝗁. No typing premise is implicit: P-App followed by P-Lam gives 𝑎:𝐴⊢𝜆𝑘.𝑘𝑎:(𝐴𝜄→𝐴)𝜄→𝐴/𝜄, and DH-Do, instantiated by 𝑏↦𝐴, gives 𝑎:𝐴⊢𝖽𝗈(𝜆𝑘.𝑘𝑎):𝐴/𝜀ℎ. For the handler’s operation premise, under fresh 𝑏::𝖳, 𝑓:(𝑏𝜄→𝐴)𝜄→𝐴,𝑟:𝑏𝜄→𝐴⊢𝑓𝑟:𝐴/𝜄 by two P-Var leaves and P-App; its return premise is 𝑥:𝐴⊢𝑥:𝐴/𝜄 by P-Var. Rule DH-Handle therefore derives 𝑎:𝐴⊢ℎ𝐴:𝐴/𝜄. The typed source root and its target simulation are 𝑑𝐴𝑐𝑎𝑝𝑡𝑢𝑟𝑒⟶(𝜆𝑧.⟨𝑧∣𝑥.𝑥⟩)𝑎𝛽⟶⟨𝑎∣𝑥.𝑥⟩𝑟𝑒𝑠𝑒𝑡𝑟𝑒𝑡𝑢𝑟𝑛⟶𝑎,ℎ𝐴ℎ𝑎𝑛𝑑𝑙𝑒𝑟𝑐𝑎𝑝𝑡𝑢𝑟𝑒⟶(𝜆𝑘.𝑘𝑎)(𝜆𝑧.𝗁𝖺𝗇𝖽𝗅𝖾𝑧𝐷𝗁)𝛽⟶(𝜆𝑧.𝗁𝖺𝗇𝖽𝗅𝖾𝑧𝐷𝗁)𝑎𝛽⟶𝗁𝖺𝗇𝖽𝗅𝖾𝑎𝐷𝗁ℎ𝑎𝑛𝑑𝑙𝑒𝑟𝑟𝑒𝑡𝑢𝑟𝑛⟶𝑎.
★☆☆ In the first effect translation of theorem 35.30, instantiate Δ0 by one type variable 𝛾::𝖳 and take 𝜏=𝛾, 𝜌=𝜄. Write the complete deep operation type. Explain why ∀𝛾 must scope the whole function from captured continuations to answers, rather than only the captured-continuation type.
Deep resumption reinstalls its delimiter. Shallow resumption does not. The type of a resumed computation can therefore expose the same leading effect again, which forces recursion at the level of effect specifications.
A shallow-handler effect is 𝜀=𝜇𝛼.Δ0.𝜏1⇒𝜏2, with 𝛼::𝖤 available in 𝜏1,𝜏2. Its formation rule is
Δ,𝛼::𝖤,Δ0⊢𝜏1::𝖳Δ,𝛼::𝖤,Δ0⊢𝜏2::𝖳
Δ⊢𝜇𝛼.Δ0.𝜏1⇒𝜏2::𝖤
K-SH
Terms and handler evaluation contexts are those of the deep calculus, with the same freeness rule (𝑛+1)-𝖿𝗋𝖾𝖾(𝐸)𝑛-𝖿𝗋𝖾𝖾(𝗁𝖺𝗇𝖽𝗅𝖾𝐸{𝑥,𝑟.𝑒ℎ;𝑦.𝑒𝑟}). The shallow contractions are 𝗁𝖺𝗇𝖽𝗅𝖾𝐸[𝖽𝗈𝑣]{𝑥,𝑟.𝑒ℎ;𝑦.𝑒𝑟}⇝0𝑒ℎ[𝑣/𝑥,(𝜆𝑧.𝐸[𝑧])/𝑟](0-𝖿𝗋𝖾𝖾(𝐸)),𝗁𝖺𝗇𝖽𝗅𝖾𝑣{𝑥,𝑟.𝑒ℎ;𝑦.𝑒𝑟}⇝0𝑒𝑟[𝑣/𝑦]. Put 𝛿𝜀=𝛿[𝛼↦𝜀]. The complete operation and handler rules are
The resumption omits the handler and may perform 𝜀 again.
A 𝖼𝗈𝗇𝗍𝗋𝗈𝗅0 effect is 𝜀=𝜇𝛼.Δ0.𝜏1⇒𝜏2/𝜌. Its formation rule is
Δ,𝛼::𝖤,Δ0⊢𝜏1::𝖳Δ,𝛼::𝖤,Δ0⊢𝜏2::𝖳Δ,𝛼::𝖤,Δ0⊢𝜌::𝖱
Δ⊢𝜇𝛼.Δ0.𝜏1⇒𝜏2/𝜌::𝖤
K-C0
Terms add 𝖼𝗈𝗇𝗍𝗋𝗈𝗅0𝑘.𝑒 and ⟨𝑒∣𝑥.𝑒𝑟⟩; evaluation contexts add ⟨𝐸∣𝑥.𝑒𝑟⟩, with (𝑛+1)-𝖿𝗋𝖾𝖾(𝐸)𝑛-𝖿𝗋𝖾𝖾(⟨𝐸∣𝑥.𝑒𝑟⟩). The capture and return roots are ⟨𝐸[𝖼𝗈𝗇𝗍𝗋𝗈𝗅0𝑘.𝑒]∣𝑥.𝑒𝑟⟩⇝0𝑒[(𝜆𝑧.𝐸[𝑧])/𝑘](0-𝖿𝗋𝖾𝖾(𝐸)),⟨𝑣∣𝑥.𝑒𝑟⟩⇝0𝑒𝑟[𝑣/𝑥]. Unlike 𝗌𝗁𝗂𝖿𝗍0, this continuation omits the reset and return clause. The full control rule is
The prefix SH- on a rule name denotes a shallow-handler rule, whereas 𝖲𝖧(−) below denotes the forward syntax translation; the parentheses and rule-name typography keep the two roles distinct. The named translations 𝖲𝖧(−) from 𝖼𝗈𝗇𝗍𝗋𝗈𝗅0 to shallow handlers and 𝖲𝖣(−) back have term clauses 𝖲𝖧(𝖼𝗈𝗇𝗍𝗋𝗈𝗅0𝑘.𝑒)=𝖽𝗈(𝜆𝑘.𝖲𝖧(𝑒)),𝖲𝖧(⟨𝑒∣𝑥.𝑒𝑟⟩)=𝗁𝖺𝗇𝖽𝗅𝖾𝖲𝖧(𝑒){𝑓,𝑟.𝑓𝑟;𝑥.𝖲𝖧(𝑒𝑟)},𝖲𝖣(𝖽𝗈𝑣)=𝖼𝗈𝗇𝗍𝗋𝗈𝗅0𝑘.𝜆ℎ.ℎ𝖲𝖣(𝑣)𝑘,𝖲𝖣(𝗁𝖺𝗇𝖽𝗅𝖾𝑒{𝑥,𝑟.𝑒ℎ;𝑦.𝑒𝑟})=⟨𝖲𝖣(𝑒)∣𝑦.𝜆ℎ.𝖲𝖣(𝑒𝑟)⟩(𝜆𝑥.𝜆𝑟.𝖲𝖣(𝑒ℎ)). The forward single-effect clause is 𝖲𝖧(𝜇𝛼.Δ0.𝜏1⇒𝜏2/𝜌)=𝜇𝛼.𝛽::𝖳.(∀Δ0.(𝛽𝛼⋅𝖲𝖧(𝜌)←←←←←←←←←←←←←→𝖲𝖧(𝜏1))𝖲𝖧(𝜌)←←←←←←←←←←→𝖲𝖧(𝜏2))⇒𝛽, For the reverse direction, define the four-argument metanotation 𝐻(𝑎,𝑏1,𝑏2,𝑔):=∀Δ0.𝖲𝖣(𝜏1)→(𝖲𝖣(𝜏2)𝑎⋅𝑔⟶𝑏1)𝑔→𝑏2. Its four arguments make explicit the variables that vary across uses; Δ0,𝜏1,𝜏2 are fixed by the ambient effect being translated. The reverse single-effect clause is 𝖲𝖣(𝜇𝛼.Δ0.𝜏1⇒𝜏2)=𝜇𝛼.𝛽1::𝖳,𝛽2::𝖳,𝛾::𝖱.𝛽1⇒(𝐻(𝛼,𝛽1,𝛽2,𝛾)𝛾→𝛽2)/𝛾.
Proof of Lemma 35.33 — Shallow-bridge type substitution
Proof. Induct on types and rows. The recursive-effect case uses capture-avoiding unfolding 𝛿𝜀=𝛿[𝛼↦𝜀], after renaming every bound variable away from the substitution support. ◻
Proof of Lemma 35.34 — Shallow-bridge value substitution
Proof. Induct on terms, alpha-renaming each term binder away from 𝑥 and 𝖥𝖵(𝑣) before descent. Choose 𝑘,ℎ∉𝖥𝖵(𝖲𝖣(𝑣))∪{𝑥}. Then 𝖲𝖣((𝖽𝗈𝑤)[𝑣/𝑥])=𝖼𝗈𝗇𝗍𝗋𝗈𝗅0𝑘.𝜆ℎ.ℎ𝖲𝖣(𝑤[𝑣/𝑥])𝑘, and the induction hypothesis makes this exactly 𝖲𝖣(𝖽𝗈𝑤)[𝖲𝖣(𝑣)/𝑥]. The shallow handler clause follows by the same capture-avoiding calculation. ◻
Proof of Lemma 35.35 — Shallow-bridge context compatibility
Proof. Use simultaneous induction on the evaluation-context and freeness derivations. A shallow handler and a 𝖼𝗈𝗇𝗍𝗋𝗈𝗅0 reset each add exactly one delimiter; their translated contexts therefore have the same freeness index. ◻
For a shallow handler, the corresponding plain-𝑘 clause is well typed: 𝖲𝖣(𝖽𝗈𝑣)=𝖼𝗈𝗇𝗍𝗋𝗈𝗅0𝑘.𝜆ℎ.ℎ𝖲𝖣(𝑣)𝑘. Unlike the failed deep attempt above, invoking 𝑘 must not reinstall the handler. This contrast determines the operation clause rather than merely confirming it after translation.
The forward translation in definition 35.32 preserves kinding and subtyping. For every well-formed Δ,Γ,𝜏,𝜌, a 𝖼𝗈𝗇𝗍𝗋𝗈𝗅0 source judgment implies Δ;Γ⊢𝑒:𝜏/𝜌⟹Δ;𝖲𝖧(Γ)⊢𝖲𝖧(𝑒):𝖲𝖧(𝜏)/𝖲𝖧(𝜌),
Proof of Lemma 35.37 — Forward shallow-bridge preservation
Proof. Induct simultaneously on forward formation, subtyping, and typing, using the substitution component of lemma 35.36 at each binding boundary.
Formation. Variable, arrow, universal, empty-row, and row-cons formation and all common-core typing cases are homomorphic. We derive the recursive effect-formation cases and the novel typing cases here, without appealing to another correspondence theorem. In the control-to-handler direction use 𝖲𝖧(−). For the source types 𝜏1,𝜏2 and answer row 𝜌, put 𝑇1:=𝖲𝖧(𝜏1),𝑇2:=𝖲𝖧(𝜏2),𝑅:=𝖲𝖧(𝜌). Then write 𝜂=𝜇𝑎.𝑏::𝖳.𝑄𝑏⇒𝑏,𝑄𝑏=∀Δ0.((𝑏𝑎⋅𝑅←←←←←←→𝑇1)𝑅⟶𝑇2). For the K-C0-to-K-SH formation case, write Ξ=Δ,𝑎::𝖤,𝑏::𝖳,Δ0. The translated source premises provide Ξ⊢𝑇𝑖::𝖳 and Ξ⊢𝑅::𝖱. The target derivation begins Ξ⊢𝑎::𝖤Ξ⊢𝑅::𝖱Ξ⊢𝑎⋅𝑅::𝖱𝐾−𝐶𝑜𝑛𝑠,Ξ⊢𝑏::𝖳Ξ⊢𝑎⋅𝑅::𝖱Ξ⊢𝑇1::𝖳Ξ⊢𝑏𝑎⋅𝑅←←←←←←→𝑇1::𝖳𝐾−𝐴𝑟𝑟,Ξ⊢𝑏𝑎⋅𝑅←←←←←←→𝑇1::𝖳Ξ⊢𝑅::𝖱Ξ⊢𝑇2::𝖳Ξ⊢(𝑏𝑎⋅𝑅←←←←←←→𝑇1)𝑅⟶𝑇2::𝖳𝐾−𝐴𝑟𝑟. Iterated universal formation and recursive shallow-effect formation are Ξ⊢(𝑏𝑎⋅𝑅←←←←←←→𝑇1)𝑅⟶𝑇2::𝖳Δ,𝑎::𝖤,𝑏::𝖳⊢𝑄𝑏::𝖳𝐾−𝐴𝑙𝑙Δ0,Δ,𝑎::𝖤,𝑏::𝖳⊢𝑄𝑏::𝖳Δ,𝑎::𝖤,𝑏::𝖳⊢𝑏::𝖳Δ⊢𝜂::𝖤𝐾−𝑆𝐻. Thus K-Cons gives Δ⊢𝜂⋅𝜄::𝖱.
Control-to-handler typing.
Unfolding the recursive effect is kind-preserving substitution. Put ――𝑇𝑖=𝑇𝑖[𝜂/𝑎],――𝑅=𝑅[𝜂/𝑎],――𝑄𝑏=𝑄𝑏[𝜂/𝑎]. The preceding derivation therefore gives Δ,𝑏::𝖳⊢――𝑄𝑏::𝖳,Δ,Δ0⊢𝜂⋅――𝑅::𝖱, where, in full, ――𝑄𝑏=∀Δ0.((𝑏𝜂⋅――𝑅←←←←←←←→――𝑇1)――𝑅⟶――𝑇2). In the typing derivation below we drop overlines on 𝑇𝑖,𝑅, but retain ――𝑄. Thus a translated C0-Control premise, for result type 𝑈, is Δ,Δ0;Γ,𝑘:𝑈𝜂⋅𝑅←←←←←←←→𝑇1⊢𝖲𝖧(𝑒):𝑇2/𝑅0,𝑅0<:𝑅. Rule P-Sub widens the body’s row to 𝑅; P-Lam and one P-Gen for each member of Δ0 then give the following judgment. Alpha-rename the binders of Δ0 away from 𝖥𝖵(Γ) first, so every generalization satisfies the freshness premise: Γ⊢𝜆𝑘.𝖲𝖧(𝑒):――𝑄𝑈/𝜄. Rule SH-Do unfolds 𝜂, instantiates 𝑏↦𝑈, and gives result 𝑈/𝜂. The final P-Sub uses 𝜄<:𝑅0 under Sub-Cons, yielding 𝑈/𝜂⋅𝑅0.
At a translated C0-Reset, let 𝛿::Δ0 be the source instantiation. The operation arm of SH-Handle has 𝑓:――𝑄𝑏,𝑟:𝑏𝜂⋅𝛿𝑅←←←←←←←←→𝛿𝑇1. Instantiating 𝑓 by 𝛿 gives 𝑓:(𝑏𝜂⋅𝛿𝑅←←←←←←←←→𝛿𝑇1)𝛿𝑅⟶𝛿𝑇2. After pure-row widening, P-App derives 𝑓𝑟:𝛿𝑇2/𝛿𝑅. Together with the two induction hypotheses this is exactly SH-Handle; no deep handler is silently used. ◻
The reverse translation in definition 35.32 preserves kinding and subtyping. For every well-formed Δ,Γ,𝜏,𝜌, a shallow-handler source judgment implies Δ;Γ⊢𝑒:𝜏/𝜌⟹Δ;𝖲𝖣(Γ)⊢𝖲𝖣(𝑒):𝖲𝖣(𝜏)/𝖲𝖣(𝜌).
Proof of Lemma 35.38 — Reverse shallow-bridge preservation
Proof. Induct simultaneously on reverse formation, subtyping, and typing. Common constructors are homomorphic, and substitution uses lemma 35.36. The recursive effect, operation, and handler cases are the following explicit derivations.
For K-SH-to-K-C0, first keep the recursive variable abstract: 𝐹𝑎𝑏1,𝑔=𝖲𝖣(𝜏2)𝑎⋅𝑔⟶𝑏1,𝐺𝑎𝑏1,𝑏2,𝑔=𝐹𝑎𝑏1,𝑔𝑔→𝑏2,𝐻(𝑎,𝑏1,𝑏2,𝑔)=∀Δ0.(𝖲𝖣(𝜏1)𝜄→𝐺𝑎𝑏1,𝑏2,𝑔),𝜃=𝜇𝑎.𝑏1::𝖳,𝑏2::𝖳,𝑔::𝖱.𝑏1⇒(𝐻(𝑎,𝑏1,𝑏2,𝑔)𝑔→𝑏2)/𝑔. Put Ω=Δ,𝑎::𝖤,𝑏1::𝖳,𝑏2::𝖳,𝑔::𝖱,Δ0. The translated K-SH premises and the fresh-variable rules yield the following derivation: Ω⊢𝑎::𝖤Ω⊢𝑔::𝖱Ω⊢𝑎⋅𝑔::𝖱𝐾−𝐶𝑜𝑛𝑠.Ω⊢𝜏2::𝖳Ω⊢𝑎⋅𝑔::𝖱Ω⊢𝑏1::𝖳Ω⊢𝐹𝑎𝑏1,𝑔::𝖳𝐾−𝐴𝑟𝑟.Ω⊢𝐹𝑎𝑏1,𝑔::𝖳Ω⊢𝑔::𝖱Ω⊢𝑏2::𝖳Ω⊢𝐺𝑎𝑏1,𝑏2,𝑔::𝖳𝐾−𝐴𝑟𝑟,Ω⊢𝜏1::𝖳Ω⊢𝜄::𝖱Ω⊢𝐺𝑎𝑏1,𝑏2,𝑔::𝖳Ω⊢𝜏1𝜄→𝐺𝑎𝑏1,𝑏2,𝑔::𝖳𝐾−𝐴𝑟𝑟. Writing Ω0=Δ,𝑎::𝖤,𝑏1::𝖳,𝑏2::𝖳,𝑔::𝖱, the final formation steps are Ω⊢𝜏1𝜄→𝐺𝑎𝑏1,𝑏2,𝑔::𝖳Ω0⊢𝐻(𝑎,𝑏1,𝑏2,𝑔)::𝖳𝐾−𝐴𝑙𝑙Δ0,Ω0⊢𝐻(𝑎,𝑏1,𝑏2,𝑔)::𝖳Ω0⊢𝑔::𝖱Ω0⊢𝑏2::𝖳Ω0⊢𝐻(𝑎,𝑏1,𝑏2,𝑔)𝑔→𝑏2::𝖳𝐾−𝐴𝑟𝑟,Ω0⊢𝑏1::𝖳Ω0⊢𝐻(𝑎,𝑏1,𝑏2,𝑔)𝑔→𝑏2::𝖳Ω0⊢𝑔::𝖱Δ⊢𝜃::𝖤𝐾−𝐶0. In particular, K-Cons gives Δ⊢𝜃⋅𝜄::𝖱. Unfolding is again kind-preserving substitution. With ――𝜏𝑖=𝜏𝑖[𝜃/𝑎],――𝐻𝑏1,𝑏2,𝑔=𝐻(𝑎,𝑏1,𝑏2,𝑔)[𝜃/𝑎], we have ――𝐻𝑏1,𝑏2,𝑔=∀Δ0.(――𝜏1𝜄→((――𝜏2𝜃⋅𝑔⟶𝑏1)𝑔→𝑏2)),Δ,𝑏1::𝖳,𝑏2::𝖳,𝑔::𝖱⊢――𝐻𝑏1,𝑏2,𝑔::𝖳.Δ,𝑏1::𝖳,𝑏2::𝖳,𝑔::𝖱⊢𝜃⋅𝑔::𝖱. Below we omit overlines on 𝜏𝑖, but retain ――𝐻. For a translated SH-Do, specialize ℎ:――𝐻𝑏1,𝑏2,𝑔 by the source substitution 𝛿0::Δ0. With 𝑘:𝛿0𝜏2𝜃⋅𝑔⟶𝑏1 the two applications derive ℎ𝖲𝖣(𝑣)𝑘:𝑏2/𝑔,𝜆ℎ.ℎ𝖲𝖣(𝑣)𝑘:――𝐻𝑏1,𝑏2,𝑔𝑔→𝑏2/𝜄. Rule C0-Control now instantiates its recursive effect by fresh 𝑏1,𝑏2,𝑔, uses 𝜄<:𝑔, and concludes 𝛿0𝜏2/𝜃⋅𝜄.
For a source shallow handler with result 𝜏𝑟/𝜌, set 𝐻𝑟=∀Δ0.𝜏1→(𝜏2𝜃⋅𝜌⟶𝜏)𝜌→𝜏𝑟. The translated reset explicitly instantiates [𝑏1↦𝜏,𝑏2↦𝜏𝑟,𝑔↦𝜌]. Its body has type 𝜏/𝜃⋅𝜌, and its return clause 𝑥.𝜆ℎ.𝖲𝖣(𝑒𝑟) has type 𝐻𝑟𝜌→𝜏𝑟/𝜌. Rule C0-Reset therefore returns a function of that type. Generalizing 𝜆𝑥.𝜆𝑟.𝖲𝖣(𝑒ℎ) over Δ0 gives 𝐻𝑟/𝜄; one row-widened P-App gives 𝜏𝑟/𝜌. Thus 𝑏1 is the type expected by the captured continuation, 𝑏2 is the handler result, and 𝑔 is the residual row, with every instantiation exposed. ◻
Both shallow translations preserve one source step by a nonempty target evaluation sequence: 𝑒⟶𝑒′⟹𝖲𝖧(𝑒)⟶+𝖲𝖧(𝑒′),𝑒⟶𝑒′⟹𝖲𝖣(𝑒)⟶+𝖲𝖣(𝑒′). No arbitrary-context relation is needed in either direction.
Proof of Lemma 35.39 — Shallow-bridge root simulation
Proof. For the control-to-handler root, put 𝐷={𝑓,𝑟.𝑓𝑟;𝑦.𝖲𝖧(𝑒𝑟)},𝑣𝑐=𝜆𝑧.𝖲𝖧(𝐸)[𝑧]. Then 𝖲𝖧(⟨𝐸[𝖼𝗈𝗇𝗍𝗋𝗈𝗅0𝑘.𝑒]∣𝑦.𝑒𝑟⟩)=𝗁𝖺𝗇𝖽𝗅𝖾𝖲𝖧(𝐸)[𝖽𝗈(𝜆𝑘.𝖲𝖧(𝑒))]𝐷⟶(𝜆𝑘.𝖲𝖧(𝑒))𝑣𝑐⟶𝛽𝖲𝖧(𝑒)[𝑣𝑐/𝑘]=𝖲𝖧(𝑒[(𝜆𝑧.𝐸[𝑧])/𝑘]). The shallow resumption contains neither handler nor reset.
For the reverse root, put 𝐻=𝜆𝑥.𝜆𝑟.𝖲𝖣(𝑒ℎ),𝑅𝑦=𝜆ℎ.𝖲𝖣(𝑒𝑟). The whole calculation is 𝖲𝖣(𝗁𝖺𝗇𝖽𝗅𝖾𝐸[𝖽𝗈𝑣]{𝑥,𝑟.𝑒ℎ;𝑦.𝑒𝑟})=⟨𝖲𝖣(𝐸)[𝖼𝗈𝗇𝗍𝗋𝗈𝗅0𝑘.𝜆ℎ.ℎ𝖲𝖣(𝑣)𝑘]∣𝑦.𝑅𝑦⟩𝐻⟶(𝜆ℎ.ℎ𝖲𝖣(𝑣)(𝜆𝑧.𝖲𝖣(𝐸)[𝑧]))𝐻⟶+𝛽𝐻𝖲𝖣(𝑣)(𝜆𝑧.𝖲𝖣(𝐸)[𝑧])⟶+𝛽𝖲𝖣(𝑒ℎ)[𝖲𝖣(𝑣)/𝑥,(𝜆𝑧.𝖲𝖣(𝐸)[𝑧])/𝑟]=𝖲𝖣(𝑒ℎ[𝑣/𝑥,(𝜆𝑧.𝐸[𝑧])/𝑟]). Every administrative contraction is in evaluation position. Return roots translate to return roots, while common beta and lift roots are homomorphic. The substitution, context, and freeness parts of lemma 35.36 lift the calculations and complete both positive simulations. For a source context step 𝐸[𝑒]⟶𝐸[𝑒′], the plugging equation rewrites 𝑋(𝐸[𝑒]) to 𝑋(𝐸)[𝑋(𝑒)]; target compatible closure lifts the root calculation, and plugging rewrites its endpoint to 𝑋(𝐸[𝑒′]). ◻
For every well-formed Δ,Γ,𝜏,𝜌, a 𝖼𝗈𝗇𝗍𝗋𝗈𝗅0 source judgment implies Δ;Γ⊢𝑒:𝜏/𝜌⟹Δ;𝖲𝖧(Γ)⊢𝖲𝖧(𝑒):𝖲𝖧(𝜏)/𝖲𝖧(𝜌), while a shallow-handler source judgment implies Δ;Γ⊢𝑒:𝜏/𝜌⟹Δ;𝖲𝖣(Γ)⊢𝖲𝖣(𝑒):𝖲𝖣(𝜏)/𝖲𝖣(𝜌). Both also preserve one source evaluation step by a nonempty sequence of ordinary evaluation-context steps in the target: 𝑒⟶𝑒′⟹𝖲𝖧(𝑒)⟶+𝖲𝖧(𝑒′),𝑒⟶𝑒′⟹𝖲𝖣(𝑒)⟶+𝖲𝖣(𝑒′). No arbitrary-context relation is needed in either shallow direction.
Again fix 𝐴::𝖳 and 𝑎:𝐴, and define 𝜀𝑐=𝜇𝑞.∅.𝐴⇒𝐴/𝜄,𝜀ℎ=𝜇𝑞.𝑏::𝖳.((𝑏𝑞⋅𝜄⟶𝐴)𝜄→𝐴)⇒𝑏. For 𝑐𝐴=⟨𝖼𝗈𝗇𝗍𝗋𝗈𝗅0𝑘.𝑎∣𝑥.𝑥⟩, the capture premise and conclusion are
𝜀𝑐=𝜇𝑞.∅.𝐴⇒𝐴/𝜄
𝑎:𝐴,𝑘:𝐴𝜀𝑐⋅𝜄←←←←←←→𝐴⊢𝑎:𝐴/𝜄
P-Var
𝜄<:𝜄𝐴::𝖳𝜀𝑐⋅𝜄::𝖱
𝑎:𝐴⊢𝖼𝗈𝗇𝗍𝗋𝗈𝗅0𝑘.𝑎:𝐴/𝜀𝑐⋅𝜄
C0-Control
Together with 𝐴::𝖳⊢∅::∅ and 𝑎:𝐴,𝑥:𝐴⊢𝑥:𝐴/𝜄, rule C0-Reset gives 𝑎:𝐴⊢𝑐𝐴:𝐴/𝜄. This lists every premise of the two novel source rules.
The translated operation is 𝖽𝗈(𝜆𝑘.𝑎). Rule P-Var types the body 𝑎, and P-Lam abstracts 𝑘, giving 𝑎:𝐴⊢𝜆𝑘.𝑎:(𝐴𝜀ℎ⋅𝜄←←←←←←→𝐴)𝜄→𝐴/𝜄. Rule SH-Do unfolds 𝜀ℎ and instantiates 𝑏↦𝐴, deriving 𝑎:𝐴⊢𝖽𝗈(𝜆𝑘.𝑎):𝐴/𝜀ℎ. In the operation arm, under fresh 𝑏::𝖳, 𝑓:(𝑏𝜀ℎ⋅𝜄←←←←←←→𝐴)𝜄→𝐴,𝑟:𝑏𝜀ℎ⋅𝜄←←←←←←→𝐴⊢𝑓𝑟:𝐴/𝜄 by two P-Var leaves and P-App; the return arm is 𝑥:𝐴⊢𝑥:𝐴/𝜄. Hence SH-Handle completely derives 𝑎:𝐴⊢𝗁𝖺𝗇𝖽𝗅𝖾𝖽𝗈(𝜆𝑘.𝑎){𝑓,𝑟.𝑓𝑟;𝑥.𝑥}:𝐴/𝜄. The source and target roots expose the shallow resumption: 𝑐𝐴𝑐𝑎𝑝𝑡𝑢𝑟𝑒⟶𝑎,𝗁𝖺𝗇𝖽𝗅𝖾𝖽𝗈(𝜆𝑘.𝑎){𝑓,𝑟.𝑓𝑟;𝑥.𝑥}ℎ𝑎𝑛𝑑𝑙𝑒𝑟𝑐𝑎𝑝𝑡𝑢𝑟𝑒⟶(𝜆𝑘.𝑎)(𝜆𝑧.𝑧)𝛽⟶𝑎. The target resumption is 𝜆𝑧.𝑧, with no reinstalled handler.
The difference becomes observable when resumption itself reaches another operation. For this calculation only, conservatively extend the shared PPS core with the pure unit type 𝖴𝗇𝗂𝗍, its value (), and typed lambda annotations; none of the correspondence theorems depends on this example. At the respective deep and shallow signatures for the unit operation 𝖴𝗇𝗂𝗍⇒𝖴𝗇𝗂𝗍, let 𝑃:=𝗁𝖺𝗇𝖽𝗅𝖾((𝜆𝑢:𝖴𝗇𝗂𝗍.𝖽𝗈())(𝖽𝗈())){𝑥,𝑟.𝑟();𝑦.𝑦}. For a deep handler, handling the first operation passes the reinstalled continuation 𝜆𝑧.𝗁𝖺𝗇𝖽𝗅𝖾((𝜆𝑢:𝖴𝗇𝗂𝗍.𝖽𝗈())𝑧){𝑥,𝑟.𝑟();𝑦.𝑦} to the clause. Invoking it puts the second 𝖽𝗈 beneath the same handler, so the second operation is handled and the computation returns (). For a shallow handler, the corresponding resumption is only 𝜆𝑧.(𝜆𝑢:𝖴𝗇𝗂𝗍.𝖽𝗈())𝑧. Invoking it reduces to the unhandled operation 𝖽𝗈(). Thus reinstalling the delimiter is not administrative syntax: it determines whether a later operation is caught.
The result is signature-specific. It requires four things at once: ordered rows; quantification over all three kinds; polymorphic deep effects; and recursive shallow effects. The four proved translations are 𝗌𝗁𝗂𝖿𝗍0𝖣𝖧←←←←←←←←→deep,deep𝖣𝖣←←←←←←←←→𝗌𝗁𝗂𝖿𝗍0,𝖼𝗈𝗇𝗍𝗋𝗈𝗅0𝖲𝖧←←←←←←←←→shallow,shallow𝖲𝖣←←←←←←←←→𝖼𝗈𝗇𝗍𝗋𝗈𝗅0. Each arrow states type preservation and the simulation relation printed in the corresponding theorem. No inverse, retraction, reflection, or full- abstraction theorem is proved. Swapping either pairing, or erasing the quantifiers, changes the resumption type and invalidates the typing proof.
★☆☆ Make a four-row table for the translations in theorem 35.30, theorem 35.40. For each row, record whether the captured continuation reinstalls its delimiter, whether the target effect is recursive, and whether semantic preservation uses ordinary evaluation reduction or arbitrary-context reduction. Justify every entry from one displayed term or effect clause.
Dependent elimination: a separate failure boundary
This boundary calculation uses a strong existential, which packages a witness together with a certificate whose type may mention that very witness: from 𝑝:∃𝑥:𝖭𝖺𝗍.𝐴, its projections have types 𝗐𝗂𝗍𝑝:𝖭𝖺𝗍 and 𝗉𝗋𝖿𝑝:𝐴[𝗐𝗂𝗍𝑝/𝑥].
Simple types do not let a result type inspect the particular value returned by a computation. A dependent pair does. Control can make the same proof return different witnesses in different continuations, so unrestricted dependent elimination can confuse a witness with its certificate.
The counterexample in this section is not a run of the call-by-value machine 𝜆𝖪. It belongs to a separate hypothetical call-by-name calculus with a commuting rule for first projection. Its purpose is to isolate exactly which extra dependent rule is dangerous.
Separate number terms from proof terms, and distinguish proof values: 𝐴,𝐵::=⊥∣𝑡=𝑢∣∃𝑥:𝖭𝖺𝗍.𝐴,𝑡,𝑢::=𝑥∣𝑛∣𝗐𝗂𝗍𝑝∣𝖼𝖺𝗅𝗅𝖼𝖼𝑘𝑡,𝑝,𝑞::=𝑎∣𝗋𝖾𝖿𝗅∣(𝑡,𝑝)∣𝗉𝗋𝖿𝑝∣𝗌𝗎𝖻𝗌𝗍𝑝𝑞∣𝖼𝖺𝗅𝗅𝖼𝖼𝑘𝑝∣𝗍𝗁𝗋𝗈𝗐𝑘𝑝∣𝗍𝗁𝗋𝗈𝗐𝑘𝑡,𝑉::=𝑎∣𝗋𝖾𝖿𝗅∣(𝑡,𝑉). Contexts are sorted: Γ::=∅∣Γ,𝑥:𝖭𝖺𝗍∣Γ,𝑎:𝐴∣Γ,𝑘÷𝐴∣Γ,𝑘÷𝖭𝖺𝗍. The entry 𝑘÷𝐴 names a proof continuation accepting an 𝐴-proof, and 𝑘÷𝖭𝖺𝗍 names a number continuation. The typing rules print these as 𝑘:¬𝐴 and 𝑘:𝖭𝖺𝗍→⊥ for readability; they are continuation names, not formulas in the formula grammar and not ordinary function assumptions. Formula formation and the variable leaves are
Γ⊢⊥𝗉𝗋𝗈𝗉
Dep-Bot-F
Γ⊢𝑡:𝖭𝖺𝗍Γ⊢𝑢:𝖭𝖺𝗍
Γ⊢𝑡=𝑢𝗉𝗋𝗈𝗉
Dep-Eq-F
Γ,𝑥:𝖭𝖺𝗍⊢𝐴𝗉𝗋𝗈𝗉
Γ⊢∃𝑥:𝖭𝖺𝗍.𝐴𝗉𝗋𝗈𝗉
Dep-Ex-F
𝑛∈ℕ
Γ⊢𝑛:𝖭𝖺𝗍
Dep-Nat
𝑥:𝖭𝖺𝗍∈Γ
Γ⊢𝑥:𝖭𝖺𝗍
Dep-Var-N
𝑎:𝐴∈Γ
Γ⊢𝑎:𝐴
Dep-Var-P
The strong-existential and equality rules are
Γ⊢𝑡:𝖭𝖺𝗍Γ⊢𝑝:𝐴[𝑡/𝑥]
Γ⊢(𝑡,𝑝):∃𝑥:𝖭𝖺𝗍.𝐴
Dep-Pair
Γ⊢𝑝:∃𝑥:𝖭𝖺𝗍.𝐴
Γ⊢𝗐𝗂𝗍𝑝:𝖭𝖺𝗍
Dep-Wit
Γ⊢𝑝:∃𝑥:𝖭𝖺𝗍.𝐴
Γ⊢𝗉𝗋𝖿𝑝:𝐴[𝗐𝗂𝗍𝑝/𝑥]
Dep-Prf
Let ⟼ be the least compatible one-step relation generated by the projection, substitution, commuting, and vacuity clauses displayed in this definition. Number-term convertibility 𝑡≡𝑢 is the least reflexive, symmetric, transitive relation containing ⟼; formula convertibility is its congruential extension. Equality has
𝑡≡𝑢
Γ⊢𝗋𝖾𝖿𝗅:𝑡=𝑢
Dep-Refl
Γ⊢𝑝:𝑡=𝑢Γ⊢𝑞:𝐵[𝑡/𝑥]
Γ⊢𝗌𝗎𝖻𝗌𝗍𝑝𝑞:𝐵[𝑢/𝑥]
Dep-Subst
There is also the conversion rule Γ⊢𝑝:𝐴𝐴≡𝐵Γ⊢𝑝:𝐵𝐷𝑒𝑝−𝐶𝑜𝑛𝑣. The ordinary projection contractions are 𝗐𝗂𝗍(𝑡,𝑝)⟼𝑡,𝗉𝗋𝖿(𝑡,𝑝)⟼𝑝,𝗌𝗎𝖻𝗌𝗍𝗋𝖾𝖿𝗅𝑝⟼𝑝. At proof type, the naive classical rules, including their arbitrary throw result, are
Γ,𝑘:¬𝐴⊢𝑝:𝐴
Γ⊢𝖼𝖺𝗅𝗅𝖼𝖼𝑘𝑝:𝐴
Dep-Callcc-P
Γ,𝑘:¬𝐴⊢𝑝:𝐴Γ⊢𝐵𝗉𝗋𝗈𝗉
Γ,𝑘:¬𝐴⊢𝗍𝗁𝗋𝗈𝗐𝑘𝑝:𝐵
Dep-Throw-P
The assumption 𝑘:¬𝐴 names a captured proof evaluation context whose hole accepts a proof of 𝐴 and whose command has answer sort ⊥; it can be invoked only by 𝗍𝗁𝗋𝗈𝗐𝑘. The mixed forms 𝖼𝖺𝗅𝗅𝖼𝖼𝑘𝑡 and 𝗍𝗁𝗋𝗈𝗐𝑘𝑡 exist only so that first projection can commute with control: the former captures a number context, and the latter is a proof-level escape to it with an arbitrary local proof type. Writing ⊥ for the answer sort, their schematic rules are
Γ,𝑘:𝖭𝖺𝗍→⊥⊢𝑡:𝖭𝖺𝗍
Γ⊢𝖼𝖺𝗅𝗅𝖼𝖼𝑘𝑡:𝖭𝖺𝗍
Dep-Callcc-N
Γ,𝑘:𝖭𝖺𝗍→⊥⊢𝑡:𝖭𝖺𝗍Γ⊢𝐵𝗉𝗋𝗈𝗉
Γ,𝑘:𝖭𝖺𝗍→⊥⊢𝗍𝗁𝗋𝗈𝗐𝑘𝑡:𝐵
Dep-Throw-N
Here 𝑘:𝖭𝖺𝗍→⊥ likewise names a captured number context whose hole accepts a natural number; it is not a lambda-bound function. Define 𝑝[𝑘∘𝗐𝗂𝗍/𝑘] to replace each 𝗍𝗁𝗋𝗈𝗐𝑘𝑞 in 𝑝 by 𝗍𝗁𝗋𝗈𝗐𝑘(𝗐𝗂𝗍𝑞). This operation changes the sort of the named continuation: before commuting, 𝑘÷𝐴 accepts an existential proof; afterward, 𝑘÷𝖭𝖺𝗍 accepts its witness. Both control rules are present precisely to type that change. The commuting and vacuity clauses used here are 𝗐𝗂𝗍(𝖼𝖺𝗅𝗅𝖼𝖼𝑘𝑝)⟼𝖼𝖺𝗅𝗅𝖼𝖼𝑘(𝗐𝗂𝗍(𝑝[𝑘∘𝗐𝗂𝗍/𝑘])),𝖼𝖺𝗅𝗅𝖼𝖼𝑘𝑡⟼𝑡(𝑘∉𝖥𝖵(𝑡)),𝖼𝖺𝗅𝗅𝖼𝖼𝑘𝑝⟼𝑝(𝑘∉𝖥𝖵(𝑝)). The vacuity, or eta, clause is load-bearing: Herbelin’s Corollary 5 shows that the explicitly typed control system without this clause avoids the Sigma-type collapse, whereas adding the clause to that same typed system admits the calculation below (Proposition 6, §2.6); untyped control names collapse already without it (§2.3) [Her05]. Reduction is closed under either sort of 𝖼𝖺𝗅𝗅𝖼𝖼: 𝑡⟼𝑡′𝖼𝖺𝗅𝗅𝖼𝖼𝑘𝑡⟼𝖼𝖺𝗅𝗅𝖼𝖼𝑘𝑡′,𝑝⟼𝑝′𝖼𝖺𝗅𝗅𝖼𝖼𝑘𝑝⟼𝖼𝖺𝗅𝗅𝖼𝖼𝑘𝑝′. These compatibility clauses are part of the hypothetical call-by-name fragment; in particular, they authorize the projection step beneath the number-level 𝖼𝖺𝗅𝗅𝖼𝖼 below. Reduction does not inspect the certificate in (𝑡,𝑝) before a projection demands it. No confluence theorem is claimed for ⟼, and the counterexample uses only the two forward reductions displayed below. Thus its conclusion does not rest on choosing normal forms for the convertibility relation. The first rule moves the context 𝗐𝗂𝗍([]) through the captured continuation. It has no counterpart in the call-by-value stack machine.
The unrestricted projection grammar is unsafe. Its value-restricted variant replaces 𝗐𝗂𝗍𝑝 and 𝗉𝗋𝖿𝑝 by 𝗐𝗂𝗍𝑉 and 𝗉𝗋𝖿𝑉, using the proof-value grammar above. Equivalently, rules Dep-Wit and Dep-Prf require their existential premise to be a syntactic 𝑉. Neither a 𝖼𝖺𝗅𝗅𝖼𝖼 term nor 𝑝0 below is a proof value.
Consider 𝑝0:=𝖼𝖺𝗅𝗅𝖼𝖼𝑘(0,𝗍𝗁𝗋𝗈𝗐𝑘(1,𝗋𝖾𝖿𝗅)):∃𝑥:𝖭𝖺𝗍.𝑥=1. Every node of its typing is visible in the following chain. Under 𝑘:¬(∃𝑥:𝖭𝖺𝗍.𝑥=1), 1:𝖭𝖺𝗍,𝗋𝖾𝖿𝗅:1=1,(1,𝗋𝖾𝖿𝗅):∃𝑥:𝖭𝖺𝗍.𝑥=1. Rule Dep-Throw-P assigns 𝗍𝗁𝗋𝗈𝗐𝑘(1,𝗋𝖾𝖿𝗅) the arbitrary local result type 0=1. With 0:𝖭𝖺𝗍, rule Dep-Pair therefore gives (0,𝗍𝗁𝗋𝗈𝗐𝑘(1,𝗋𝖾𝖿𝗅)):∃𝑥:𝖭𝖺𝗍.𝑥=1, and Dep-Callcc-P discharges 𝑘, yielding the type in (35.2). The commuting rule and the explicit compatibility clause under 𝖼𝖺𝗅𝗅𝖼𝖼 give the call-by-name reduction 𝗐𝗂𝗍𝑝0⟼𝖼𝖺𝗅𝗅𝖼𝖼𝑘(𝗐𝗂𝗍(0,𝗍𝗁𝗋𝗈𝗐𝑘(𝗐𝗂𝗍(1,𝗋𝖾𝖿𝗅))))⟼𝖼𝖺𝗅𝗅𝖼𝖼𝑘0⟼0. The projection typing and this conversion yield 𝗉𝗋𝖿𝑝0:𝗐𝗂𝗍𝑝0=1,𝗋𝖾𝖿𝗅:𝗐𝗂𝗍𝑝0=0. Substitution of equals produces a proof of 1=0. The same occurrence 𝑝0 supplied the witness in one continuation and the certificate in another. This collapses two distinct closed numerals. The fragment has no rule from 1=0 to ⊥, so the precise conclusion is failure of numerical canonicity, not a separately derived proof of falsehood.
The hypothetical calculus of definition 35.41 derives 1=0. This result does not run (35.2) in 𝜆𝖪 and does not refute a call-by-value, value-restricted dependent control calculus. The failure is the derivation in which 𝗐𝗂𝗍𝑝0 reduces to 0 while 𝗉𝗋𝖿𝑝0 retains the certificate obtained with witness 1. Requiring Dep-Wit and Dep-Prf to eliminate only a proof value rejects 𝑝0, which contains 𝖼𝖺𝗅𝗅𝖼𝖼.
Proof of Proposition 35.42 — The dependent-elimination boundary
Proof. Let 𝐵(𝑥):=𝑥=0. The first displayed proof has type 𝗐𝗂𝗍𝑝0=1, while conversion along 𝗐𝗂𝗍𝑝0⟼∗0 types 𝗋𝖾𝖿𝗅:𝗐𝗂𝗍𝑝0=0=𝐵(𝗐𝗂𝗍𝑝0). Rule Dep-Subst therefore derives 𝗌𝗎𝖻𝗌𝗍(𝗉𝗋𝖿𝑝0)𝗋𝖾𝖿𝗅:𝐵(1),thatis,1=0. The simple CPS theorem cannot be applied: its source has neither dependent projection nor the commuting call-by-name rule. A value restriction blocks both 𝗐𝗂𝗍𝑝0 and 𝗉𝗋𝖿𝑝0, because 𝑝0 is not a proof value. Soundness of any broader purity class requires a separate preservation argument. ◻
★★★ Derive the type of every subterm of 𝑝0, including the arbitrary result type assigned to its throw. Write all three reduction steps—the commuting step, the pair projection under 𝖼𝖺𝗅𝗅𝖼𝖼, and the vacuous-𝖼𝖺𝗅𝗅𝖼𝖼 step—including the transformed throw payload. Then derive, from the two displayed projection typings, 𝗌𝗎𝖻𝗌𝗍(𝗉𝗋𝖿𝑝0)𝗋𝖾𝖿𝗅:1=0. Identify the rule absent from the call-by-value 𝜆𝖪 machine.
The ML comparison concerns neither the stack machine nor dependent elimination. It asks whether Damas–Milner let-generalization remains sound after continuations become first-class ML values. The answer depends on the operational treatment of 𝗅𝖾𝗍, so the syntax, type assignment, and evaluator are fixed together.
The definitions, counterexample, soundness theorem, and exercise in this subsection belong to the closed, purely functional ML core of Harper, Duba, and MacQueen, extended only by 𝖼𝖺𝗅𝗅𝖼𝖼 and 𝗍𝗁𝗋𝗈𝗐. Its continuation type 𝜏𝖼𝗈𝗇𝗍, its evaluator continuations, and its fixed answer monotype are local to this subsection. They are not the 𝖢𝗈𝗇𝗍𝐴 values of 𝜆𝖪, the ordered effects of section 35.4, or the proof continuations of subsection 35.4.4. The result concerns neither state, exceptions, handlers, dependent types, answer-type polymorphism, or a modern value restriction for a larger language.
Write 𝑒1;𝑒2:=(𝜆𝑧.𝑒2)𝑒1, where 𝑧∉fv(𝑒2). The unsoundness result and this closed ML expression are due jointly to Lillibridge and Harper [HDM93]: 𝗅𝖾𝗍𝑓𝖻𝖾𝖼𝖺𝗅𝗅𝖼𝖼(𝜆𝑘.𝜆𝑥.𝗍𝗁𝗋𝗈𝗐𝑘(𝜆𝑦.𝑥))𝗂𝗇𝑓1;𝑓𝗍𝗋𝗎𝖾. Unrestricted assignment gives this expression type 𝖻𝗈𝗈𝗅, while the continuation evaluator returns 1. The local rules and the complete derivations make both statements precise.
The local expressions, monotypes, and polytypes are 𝑒::=𝑥∣𝑐∣𝜆𝑥.𝑒∣𝑒1𝑒2∣𝗅𝖾𝗍𝑥𝖻𝖾𝑒1𝗂𝗇𝑒2,𝜏::=𝑏∣𝑡∣𝜏1→𝜏2∣𝜏𝖼𝗈𝗇𝗍,𝜎::=𝜏∣∀𝑡.𝜎. Here 𝑏 ranges over base types, including 𝗂𝗇𝗍 and 𝖻𝗈𝗈𝗅, while 𝑡 ranges over type variables. Apart from 𝖼𝖺𝗅𝗅𝖼𝖼 and 𝗍𝗁𝗋𝗈𝗐, constants have base monotypes. Write 𝜎≽𝜏 when 𝜏 is a monotype instance of 𝜎, and write 𝜎1⊒𝜎2 when every monotype instance of 𝜎2 is an instance of 𝜎1. This relation is reflexive and transitive by inclusion of instance sets. Define 𝖢𝗅𝗈𝗌𝖾Γ(𝜏):=∀𝑡1…∀𝑡𝑛.𝜏,{𝑡1,…,𝑡𝑛}=ftv(𝜏)∖ftv(Γ). Let Σ be the fixed constant signature. The complete type-assignment card is
Γ(𝑥)≽𝜏
Γ⊢𝑥:𝜏
ML-Var
Σ(𝑐)≽𝜏
Γ⊢𝑐:𝜏
ML-Const
Γ,𝑥:𝜏1⊢𝑒:𝜏2𝑥∉dom(Γ)
Γ⊢𝜆𝑥.𝑒:𝜏1→𝜏2
ML-Abs
Γ⊢𝑒1:𝜏2→𝜏Γ⊢𝑒2:𝜏2
Γ⊢𝑒1𝑒2:𝜏
ML-App
Γ⊢𝑒1:𝜏1Γ,𝑥:𝖢𝗅𝗈𝗌𝖾Γ(𝜏1)⊢𝑒2:𝜏2𝑥∉dom(Γ)
Γ⊢𝗅𝖾𝗍𝑥𝖻𝖾𝑒1𝗂𝗇𝑒2:𝜏2
ML-Let
The continuation primitives have exactly the polytypes 𝖼𝖺𝗅𝗅𝖼𝖼:∀𝑡.(𝑡𝖼𝗈𝗇𝗍→𝑡)→𝑡,𝗍𝗁𝗋𝗈𝗐:∀𝑠.∀𝑡.𝑠𝖼𝗈𝗇𝗍→𝑠→𝑡. Thus the result type of a throw is arbitrary, but the value sent to the saved continuation has the saved continuation’s argument type.
The operational judgment is 𝐾⊢𝑒⇓𝑎. Its closed values, evaluation continuations, and answers are 𝑣::=𝑐∣𝜆𝑥.𝑒∣𝗍𝗁𝗋𝗈𝗐𝑣∣𝐾,𝐾::=[]∣𝐾𝑒∣𝑣𝐾∣𝗅𝖾𝗍𝑥𝖻𝖾𝐾𝗂𝗇𝑒,𝑎::=𝑣∣𝗐𝗋𝗈𝗇𝗀. The partial application 𝗍𝗁𝗋𝗈𝗐𝑣 is a value because 𝗍𝗁𝗋𝗈𝗐 is curried; an evaluator continuation 𝐾 is a reified run-time value, not a surface expression. Fix one answer monotype 𝛼. Its free type variables are rigid parameters of the operational metatheory: admissible type substitutions fix them. Quantified prefixes are alpha-renamed away from those parameters. For that theorem instance, 𝖢𝗅𝗈𝗌𝖾 quantifies only flexible variables, while ≽ and ⊒ range over admissible substitutions fixing the rigid answer parameters. Outside the fixed-answer operational argument, the surface assignment rules retain their ordinary meaning. Reified-continuation typing is defined only at that boundary: 𝐾:𝜏𝖼𝗈𝗇𝗍⟺𝑥:𝜏⊢𝐾[𝑥]:𝛼. The surface type 𝜏𝖼𝗈𝗇𝗍 does not quantify over 𝛼. If 𝐾 has one hole, write 𝐾[𝑒] for plugging 𝑒 into that hole and 𝐾[𝐾′] for continuation composition. The complete evaluator card is
[]⊢𝑣⇓𝑣
val0
[]⊢𝐾[𝑣]⇓𝑎𝐾≠[]
𝐾⊢𝑣⇓𝑎
val1
𝐾[[]𝑒2]⊢𝑒1⇓𝑎𝑒1isnotavalue
𝐾⊢𝑒1𝑒2⇓𝑎
fn
𝐾[𝑣1[]]⊢𝑒2⇓𝑎𝑒2isnotavalue
𝐾⊢𝑣1𝑒2⇓𝑎
arg
𝐾⊢𝑒1[𝑣2/𝑥]⇓𝑎
𝐾⊢(𝜆𝑥.𝑒1)𝑣2⇓𝑎
beta
𝑣1isheadedbyneither𝜆nor𝖼𝖺𝗅𝗅𝖼𝖼nor𝗍𝗁𝗋𝗈𝗐
𝐾⊢𝑣1𝑣2⇓𝗐𝗋𝗈𝗇𝗀
wrong
𝐾[𝗅𝖾𝗍𝑥𝖻𝖾[]𝗂𝗇𝑒2]⊢𝑒1⇓𝑎𝑒1isnotavalue
𝐾⊢𝗅𝖾𝗍𝑥𝖻𝖾𝑒1𝗂𝗇𝑒2⇓𝑎
bind
𝐾⊢𝑒2[𝑣1/𝑥]⇓𝑎
𝐾⊢𝗅𝖾𝗍𝑥𝖻𝖾𝑣1𝗂𝗇𝑒2⇓𝑎
sub
𝐾⊢𝑣𝐾⇓𝑎
𝐾⊢𝖼𝖺𝗅𝗅𝖼𝖼𝑣⇓𝑎
seize
𝐾′⊢𝑣⇓𝑎
𝐾⊢𝗍𝗁𝗋𝗈𝗐𝐾′𝑣⇓𝑎
jump
Rule seize passes the current 𝐾 to its operand. Rule jump discards the current 𝐾 and resumes 𝐾′. In wrong, “headed by 𝗍𝗁𝗋𝗈𝗐” includes the partial application 𝗍𝗁𝗋𝗈𝗐𝐾′, whose next argument is governed by jump. These clauses are the continuation semantics at the fixed answer boundary, not a reduction semantics for 𝜆𝖪.
For the counterexample, Σ(1)=𝗂𝗇𝗍 and Σ(𝗍𝗋𝗎𝖾)=𝖻𝗈𝗈𝗅. Unrestricted ML-Let is unsound with this evaluator. Let 𝑞:=𝜆𝑘.𝜆𝑥.𝗍𝗁𝗋𝗈𝗐𝑘(𝜆𝑦.𝑥),𝐸:=𝖼𝖺𝗅𝗅𝖼𝖼𝑞,𝐶[𝑓]:=𝑓1;𝑓𝗍𝗋𝗎𝖾,𝑃:=𝗅𝖾𝗍𝑓𝖻𝖾𝐸𝗂𝗇𝐶[𝑓], where the sequencing abbreviation uses application, not let. Thus 𝑃 is the expression in (35.3). Every type in the critical derivation can be read off from one type variable 𝑢. Under 𝑘:(𝑢→𝑢)𝖼𝗈𝗇𝗍 and 𝑥:𝑢, choose 𝑦:𝑢. Then 𝜆𝑦.𝑥:𝑢→𝑢,𝗍𝗁𝗋𝗈𝗐𝑘(𝜆𝑦.𝑥):𝑢,𝜆𝑥.𝗍𝗁𝗋𝗈𝗐𝑘(𝜆𝑦.𝑥):𝑢→𝑢. The throw uses (35.4) at 𝑠=𝑢→𝑢 and local result 𝑡=𝑢. Instantiating 𝖼𝖺𝗅𝗅𝖼𝖼 at 𝑡=𝑢→𝑢 gives 𝑞:(𝑢→𝑢)𝖼𝗈𝗇𝗍→(𝑢→𝑢),𝐸:𝑢→𝑢. Because the outer context is empty, ML-Let assigns 𝑓:∀𝑢.𝑢→𝑢. Its first occurrence is instantiated at 𝗂𝗇𝗍, its second at 𝖻𝗈𝗈𝗅; hence 𝑓1:𝗂𝗇𝗍,𝑓𝗍𝗋𝗎𝖾:𝖻𝗈𝗈𝗅,𝜆𝑧.𝑓𝗍𝗋𝗎𝖾:𝗂𝗇𝗍→𝖻𝗈𝗈𝗅,𝐶[𝑓]:𝖻𝗈𝗈𝗅,𝑃:𝖻𝗈𝗈𝗅.
The evaluator nevertheless returns 1. Put 𝐾0:=𝗅𝖾𝗍𝑓𝖻𝖾[]𝗂𝗇𝐶[𝑓],𝑔:=𝜆𝑥.𝗍𝗁𝗋𝗈𝗐𝐾0(𝜆𝑦.𝑥),ℎ:=𝜆𝑦.1, and let 𝐾1:=(𝜆𝑧.𝑔𝗍𝗋𝗎𝖾)[] be the sequence continuation waiting for the result of the first call 𝑔1, and put 𝐾2:=(𝜆𝑧.ℎ𝗍𝗋𝗎𝖾)[]. The following is the spine of the evaluation derivation; each double arrow replaces a conclusion by the premise selected by the named operational rule, rather than introducing a new reduction relation: []⊢𝑃⇓1𝑏𝑖𝑛𝑑⟺𝐾0⊢𝐸⇓1𝑠𝑒𝑖𝑧𝑒⟺𝐾0⊢𝑞𝐾0⇓1𝑏𝑒𝑡𝑎⟺𝐾0⊢𝑔⇓1𝑣𝑎𝑙1⟺[]⊢𝗅𝖾𝗍𝑓𝖻𝖾𝑔𝗂𝗇𝐶[𝑓]⇓1𝑠𝑢𝑏⟺[]⊢𝐶[𝑔]⇓1𝑎𝑟𝑔⟺𝐾1⊢𝑔1⇓1𝑏𝑒𝑡𝑎⟺𝐾1⊢𝗍𝗁𝗋𝗈𝗐𝐾0ℎ⇓1𝑗𝑢𝑚𝑝⟺𝐾0⊢ℎ⇓1𝑣𝑎𝑙1⟺[]⊢𝗅𝖾𝗍𝑓𝖻𝖾ℎ𝗂𝗇𝐶[𝑓]⇓1𝑠𝑢𝑏⟺[]⊢𝐶[ℎ]⇓1𝑎𝑟𝑔⟺𝐾2⊢ℎ1⇓1𝑏𝑒𝑡𝑎⟺𝐾2⊢1⇓1𝑣𝑎𝑙1⟺[]⊢(𝜆𝑧.ℎ𝗍𝗋𝗎𝖾)1⇓1𝑏𝑒𝑡𝑎⟺[]⊢ℎ𝗍𝗋𝗎𝖾⇓1𝑏𝑒𝑡𝑎⟺[]⊢1⇓1. The final judgment is the axiom val0. The first call of 𝑔 throws ℎ=𝜆𝑦.1 back to the let frame 𝐾0. Rule sub substitutes ℎ for 𝑓 and executes both calls. The original static derivation licensed the two occurrences of 𝑓 at distinct instances, but dynamically ℎ is only the integer constant function, so ℎ𝗍𝗋𝗎𝖾 also returns 1. For the diagnostic only, extend the signature with 𝗇𝗈𝗍:𝖻𝗈𝗈𝗅→𝖻𝗈𝗈𝗅, its two Boolean evaluation clauses, and a clause returning 𝗐𝗋𝗈𝗇𝗀 on a non-Boolean argument. Then 𝗇𝗈𝗍𝑃:𝖻𝗈𝗈𝗅, but evaluation first obtains 1 from 𝑃 and the new primitive clause returns 𝗐𝗋𝗈𝗇𝗀. This temporary observation primitive is not part of the fixed signature used in theorem 35.46.
The failed invariant is now explicit. The saved frame 𝐾0 needs its hole at the polytype ∀𝑢.𝑢→𝑢, because the body uses 𝑓 at two instances. Equation (35.5), however, can give a reified continuation only a monotype argument. Fixing the answer monotype 𝛼=𝖻𝗈𝗈𝗅 for this counterexample, the unrestricted proof has 𝑥:𝖢𝗅𝗈𝗌𝖾∅(𝑢→𝑢)⊢𝐾0[𝑥]:𝖻𝗈𝗈𝗅, not the premise 𝑥:𝑢→𝑢⊢𝐾0[𝑥]:𝖻𝗈𝗈𝗅 required by seize.
One exact repair restricts the language itself to values-only let: every term 𝗅𝖾𝗍𝑥𝖻𝖾𝑒1𝗂𝗇𝑒2 must have a syntactic value 𝑒1. The bind rule and the continuation form 𝗅𝖾𝗍𝑥𝖻𝖾𝐾𝗂𝗇𝑒 then disappear; value-bound lets still use ML-Let and may still generalize. This is the paper’s values-only language, not the distinct policy which retains arbitrary let right-hand sides but generalizes only selected ones.
Proof of Lemma 35.44 — Admissible type substitution
Proof. Induct on typing. Variable and constant instances compose their witnessing instantiations with 𝑆. Abstraction and application use the induction hypotheses on their premises. In a let, alpha-rename the variables closed by 𝖢𝗅𝗈𝗌𝖾Γ away from the domain and range of 𝑆. Since 𝑆 fixes Γ, closing the substituted right-hand-side type binds the same renamed variables; apply the two induction hypotheses and rebuild ML-Let. Fixing the rigid answer parameters ensures that the reified-continuation boundary (35.5) is unchanged. ◻
Proof of Lemma 35.45 — Generalized Damas–Milner substitution
Proof. First, replacing a declaration by a more general scheme preserves typing. This follows by induction on the typing derivation: at the replaced variable, transitivity of ⊒ preserves every monotype instance selected by ML-Var; constants, lambdas, and applications rebuild immediately. In a let case, a more general context has no additional free type variables, so its closure of the right-hand-side monotype is at least as general as the old closure. Apply the same replacement fact to the let-bound declaration in the body, then rebuild with ML-Let.
For substitution, prove the stronger claim with an arbitrary additional context Δ, disjoint from 𝑧: from Γ,Δ,𝑧:𝜎⊢𝑒:𝜏, derive Γ,Δ⊢𝑒[𝑤/𝑧]:𝜏. Induct on the displayed typing derivation. If the last rule is ML-Var at 𝑧, then 𝜎≽𝜏. Since 𝖢𝗅𝗈𝗌𝖾Γ(𝜌)⊒𝜎, also 𝖢𝗅𝗈𝗌𝖾Γ(𝜌)≽𝜏. Hence a substitution 𝑆 of monotypes for the variables closed in 𝜌, fixing ftv(Γ)∪ftv(𝛼), satisfies 𝜌[𝑆]=𝜏. Lemma 35.44 applied to Γ⊢𝑤:𝜌 gives Γ⊢𝑤:𝜏, and weakening adds Δ. A different variable and a constant are unchanged.
Rules ML-Abs and ML-App follow by extending Δ with the lambda binder, using exchange for the finite-map contexts, and applying the induction hypotheses. In the ML-Let case, apply the first induction hypothesis to its right-hand side. For its body, the induction hypothesis initially retains the scheme 𝖢𝗅𝗈𝗌𝖾Γ,Δ,𝑧:𝜎(𝜌1) of the let variable. Removing 𝑧:𝜎 can only quantify more variables, so 𝖢𝗅𝗈𝗌𝖾Γ,Δ(𝜌1)⊒𝖢𝗅𝗈𝗌𝖾Γ,Δ,𝑧:𝜎(𝜌1). The scheme-strengthening fact from the first paragraph replaces the retained declaration by 𝖢𝗅𝗈𝗌𝖾Γ,Δ(𝜌1); ML-Let then rebuilds the substituted term.
The run-time value clause for a reified continuation is stable under the same infrastructure. From 𝐾:𝜌𝖼𝗈𝗇𝗍 obtain 𝑥:𝜌⊢𝐾[𝑥]:𝛼. A type substitution 𝑆 gives 𝑥:𝜌[𝑆]⊢𝐾[𝑥]:𝛼, because 𝑆 fixes the rigid variables of the answer monotype; hence 𝐾:𝜌[𝑆]𝖼𝗈𝗇𝗍. In the term-substitution induction a reified 𝐾 is unchanged because run-time continuations are term-closed. The partial-𝗍𝗁𝗋𝗈𝗐 value uses ML-App. Thus the five surface rules and both additional run-time value forms are covered. Taking Δ=∅ proves the statement. ◻
Fix an answer monotype 𝛼, whose free variables are rigid as specified at (35.5). In the ML-continuation language with values-only let, suppose ∅⊢𝑒:𝜏, 𝑥:𝜏⊢𝐾[𝑥]:𝛼, and 𝐾⊢𝑒⇓𝑎. Then 𝑎 is a value and 𝑎:𝛼. Consequently a closed well-typed program evaluated in the empty continuation cannot produce 𝗐𝗋𝗈𝗇𝗀.
Proof. Use induction on the evaluation derivation. Each substitution step uses lemma 35.45. Its monomorphic specialization takes 𝜎=𝜌; its value-bound-let specialization takes 𝜎=𝖢𝗅𝗈𝗌𝖾∅(𝜌).
For val0, the premise 𝑥:𝜏⊢[][𝑥]:𝛼 forces the hole to be used at 𝛼, so substitution gives 𝑣:𝛼. For val1, monomorphic plugging gives 𝐾[𝑣]:𝛼; apply the induction hypothesis to []⊢𝐾[𝑣]⇓𝑎. In the fn case, inversion gives 𝑒1:𝜌→𝜏 and 𝑒2:𝜌. The pushed continuation has 𝑦:𝜌→𝜏⊢𝐾[𝑦𝑒2]:𝛼, so the induction hypothesis applies to evaluation of 𝑒1. The arg case is the same calculation with 𝑦:𝜌⊢𝐾[𝑣1𝑦]:𝛼. The beta case follows from monomorphic substitution. The sub case follows from value substitution at 𝖢𝗅𝗈𝗌𝖾∅(𝜌). There is no bind case.
For seize, inversion of 𝖼𝖺𝗅𝗅𝖼𝖼𝑣:𝜏 gives 𝑣:𝜏𝖼𝗈𝗇𝗍→𝜏. The theorem’s continuation premise and (35.5) give 𝐾:𝜏𝖼𝗈𝗇𝗍; hence 𝑣𝐾:𝜏, and the induction hypothesis applies to the rule premise. For jump, inversion of 𝗍𝗁𝗋𝗈𝗐𝐾′𝑣:𝜏 gives a monotype 𝜌 with 𝐾′:𝜌𝖼𝗈𝗇𝗍 and 𝑣:𝜌. By the definition of reified continuation typing, 𝑧:𝜌⊢𝐾′[𝑧]:𝛼, so the induction hypothesis applies to 𝐾′⊢𝑣⇓𝑎. Finally, a well-typed operator value of arrow type is a lambda, 𝖼𝖺𝗅𝗅𝖼𝖼, or a partial application of 𝗍𝗁𝗋𝗈𝗐; a reified continuation has a 𝖼𝗈𝗇𝗍 type. Thus the wrong rule cannot close a typed derivation. These cases exhaust the evaluator and establish 𝑎:𝛼, which excludes the only nonvalue answer 𝗐𝗋𝗈𝗇𝗀. For the corollary take 𝐾=[] and 𝛼=𝜏, after standardizing the typing derivation’s quantified and flexible variables apart from ftv(𝜏); then regard ftv(𝜏) as the rigid parameters of that theorem instance. ◻
The repair blocks 𝑃 before evaluation: its let right-hand side 𝐸 is an application of 𝖼𝖺𝗅𝗅𝖼𝖼, not a syntactic value. The theorem does not identify every safe nonvalue and does not license a purity or effect generalization beyond the language fixed in convention 35.43. It is the values-only half of the soundness result proved by Harper, Duba, and MacQueen [HDM93].
★★☆ In the values-only language, derive 𝗅𝖾𝗍𝑖𝖻𝖾𝜆𝑧.𝑧𝗂𝗇𝑖1;𝑖𝗍𝗋𝗎𝖾:𝖻𝗈𝗈𝗅. Then, for the unrestricted evaluator’s saved continuation 𝐾0, show that no monotype 𝜏 makes 𝑥:𝜏⊢𝐾0[𝑥]:𝖻𝗈𝗈𝗅 derivable, even though assigning 𝑥:∀𝑢.𝑢→𝑢 does. This is a metatheoretic check of the unrestricted continuation, not a term admitted by values-only let. Identify the rejected syntactic node in 𝑃, and explain why replacing its body by 𝑓1;𝑓1 does not make 𝑃 admissible under the selected repair.
Begin with exercise 35.9, exercise 35.10. Continue with exercise 35.11 and exercise 35.8; then use the remaining problems to test the normalization, dependent, and ML-polymorphism boundaries. No problem in this seminar is a prerequisite for a later chapter.
★★☆ Let 𝐾=∙;𝖺𝖽𝖽3[];[]7, where the top frame expects a function 𝖭𝖺𝗍→𝖭𝖺𝗍. Derive 𝐾:(𝖭𝖺𝗍→𝖭𝖺𝗍)⇒𝖭𝖺𝗍, compute 𝐾𝗄ℎ, and verify its target type binder by binder. Then run the source and target states for the returned value 𝜆𝑥:𝖭𝖺𝗍.𝑥.
★★★ Fix 𝐾:𝐴⇒𝑅 and a closed value 𝑎:𝐴. Let 𝐶 be the case frame 𝖼𝖺𝗌𝖾[]𝗈𝖿{𝗂𝗇𝗅𝑥↦𝑥;𝗂𝗇𝗋𝑞↦𝗍𝗁𝗋𝗈𝗐𝑎𝗍𝗈𝑞}, so 𝐶:(𝐴+𝖢𝗈𝗇𝗍𝐴)⇒𝐴. Derive 𝐾;𝐶:(𝐴+𝖢𝗈𝗇𝗍𝐴)⇒𝑅, start 𝗅𝖾𝗆𝐴 under that stack, and splice both traces of proposition 35.16 into one uninterrupted run ending at 𝐾◃𝑎. Translate the case frame and the restored injection frame to CPS and check the corresponding target reductions.
★★☆ For a 0-free context 𝐸, expand the translation of ⟨𝐸[𝗌𝗁𝗂𝖿𝗍0𝑘.𝑒]∣𝑥.𝑒𝑟⟩. Perform the deep-handler contraction and every beta step until the translation of 𝑒[(𝜆𝑧.⟨𝐸[𝑧]∣𝑥.𝑒𝑟⟩)/𝑘] appears. State where 0-freeness is used.
★☆☆ Construct a fictitious translation which maps every source state to one fixed target value. It satisfies a zero-or-more-step simulation. Explain formally why it cannot prove theorem 35.13, and identify the exact use of positivity in that proof.
★☆☆ Impose the syntactic rule that 𝗐𝗂𝗍𝑝 and 𝗉𝗋𝖿𝑝 are formed only when 𝑝 is a value. Show precisely which two expressions in the numeral-collapse derivation become ill formed. Explain why this observation blocks that derivation but is not, by itself, a preservation proof for a complete dependent control calculus.
★★★Practical project.control-machine-cps Implement the finite stack machine and its represented CPS spine in Kappa. Maintain the invariant that a captured stack is the complete ordered frame list and that applying a saved continuation replaces, rather than extends, the current list; the saved list remains available to later callers. The four discriminatory cases must check complete-spine capture, stack replacement on throw, persistence across two callers, and the two-stage LEM change-of-mind trace. Direct/CPS equality, the declared positive costs, and the DNE constructor equality are smoke outputs only, not independent evidence. Mutate capture to retain only the top frame; the mutant must still type-check and audit cleanly but fail the complete-spine and LEM cases. Appendix E records the four acceptance commands, and appendix F gives the construction stages.
Griffin identified typed control with classical proofs [Gri90]. Harper develops the stack-machine and CPS background [Har16]. Kameyama and Hasegawa give the equational boundary for delimited control [KH03]. The four typed correspondences between handlers and control follow Piróg, Polesiuk, and Sieczkowski [PPS19]. Herbelin gives the Sigma-type counterexample and identifies the vacuity rule as the load-bearing boundary [Her05]; Miquey supplies the sequent-calculus presentation and value-restriction repair used here [Miq19].
Harper, Duba, and MacQueen supply the ML evaluator and exact values-only theorem; their footnote 3 credits the polymorphic counterexample jointly to Lillibridge and Harper [HDM93].