Lectures onType Theory
Chapter 32
Chapter 32Optional

Effect Capabilities and Tunnelling

Prerequisites. Direct starred prerequisites: none. No later core chapter depends on this route.

Let ask:1Int be an operation. Two handlers implement it: Hout(())=7,Hin(())=9. A library function receives a suspended computation q and evaluates it under the inner handler: underInner(q):=handleHin(q()). Suppose q is defined while the outer handler is in scope: handleHout(let q=λ().ask() in underInner(q)). In the minimal nearest-name model, beta reduction exposes the request under the inner handler; nearest-name dispatch makes the inner clause answer it. Thus the initial program and its result are handleHout(let q=λ().ask() in handleHin(q()))and9. The lexical definition of q is irrelevant to nearest-name lookup. In an authority-indexed model, write ask@H for a request authorized to invoke the particular handler H, and abbreviate pauth:=handleHout(let q=λ().ask@Hout() in handleHin(q())). Identity-matched dispatch instead skips the wrong-identity inner handler, so the initial program and its result are pauthand7. Both programs have the same effect presence information, namely that ask may occur. They differ in handler identity.

The distinction becomes sharper when the closure escapes. Give the outer handler a fresh runtime label out, and let the returned closure retain the corresponding authority: let q=handleHout(return (λ().ask@out())) in q(). Handler return removes the delimiter and beta reduction exposes an authority use with no matching active label. The initial program therefore evaluates to the displayed stuck term: let q=handleHout(return (λ().ask@out())) in q()andask@out(). The final term is stuck because out is no longer active. A sound static system must either prevent this escape or retain enough latent scope information to reject the later call.

Finally, operation equality does not imply handler equality: op(Hout)=ask=op(Hin),HoutHin. An effect row can state {ask}, but that statement alone authorizes neither handler. An effect capability is an explicit token naming authority at the call site; lexical tunnelling resolves authority instead from the lexical identity of a request.

Exercise 32.1 — Presence is not authority

★★☆ Assume an active outer handler Hout surrounds each of the following request-bearing phrases: ask(),handleHin(ask()),underInner(λ().ask()). For each phrase, distinguish three quantities: the effect of the request body, the residual effect of the whole displayed phrase before any surrounding outer handler is applied, and the residual effect of the complete outer-handled program. In particular, an inner handler discharges the whole phrase’s ask effect even though its request body has effect {ask}. Then state the handler selected by nearest-name lookup. Finally replace the request in each phrase by ask@Hout() and state the selected handler under explicit authority. Explain why a request effect alone cannot determine that identity.

System Xi: capabilities as second-class blocks

The first repair makes authority explicit. The selected core is System Xi from Effects as Capabilities [BSO20]. Its source syntax is fine-grain call by value: expressions are pure values, statements may control the continuation, and blocks are not expression values.

Definition 32.1 — System Xi card

The source and runtime syntax are τ::=IntBool1,σ::=(τ¯,σ¯)τ,e::=xv,v::=()ntruefalse,b::=fu,u::=wcap{(x,k)s},w::={(x¯:τ¯,f¯:σ¯)s},s::=val x=s;sedef f=b;sb(e¯,b¯)handle{Fs}with{(x,k)s}#{s}. Labels are fresh runtime names. Value contexts, block contexts, and label contexts are Γ::=Γ,x:τ,Δ::=Δ,f:σ,Ξ::=Ξ,:τ. Here F is an operation name and HX is an evaluation context. Fix an operation signature Ω mapping each operation name F to one block type τ1τ0, where τ1τ0 abbreviates the single-value, no-block-parameter type ((τ1),())τ0. All System Xi judgments below are relative to this fixed signature, which is suppressed from their notation. In particular, the parameter and result types of a handler are looked up rather than guessed. The expression fragment consists exactly of unit, integers, booleans, variables, and constants. The constant signature is C(())=1, C(n)=Int, and C(true)=C(false)=Bool. The label binding :τ records the answer type of the delimiter. The label context is an ordered allocation stack: in Ξ0,:τ,Ξ+, the prefix Ξ0 existed when was allocated and the suffix Ξ+ contains only delimiters allocated later. Context lookup is rightmost. Ordinary source binders are alpha-renamed fresh; one handler form intentionally shadows: its handled premise binds a fresh capability for the operation name F, while its clause is typed in the unextended outer block context. Thus an outer capability named F may remain available to the clause even though the handled body sees the newly bound one. The three typing judgments are Γe:τ,ΓΔΞb:σ,ΓΔΞs:τ. Source terms have Ξ=; labels, delimiters, and capabilities appear only during evaluation. In the metatheory, runtime terms are indexed by their typing derivations, as in the source mechanization. The notation above erases those indices: a capability node retains the outer derivation of its handler body, and a delimiter node binds its fresh label in the derivation of its body. Consequently the runtime rules below classify generated configurations; they are not constructors with which one may forge an arbitrary capability and an unrelated body.

The complete source rules are the following. Expression rules are ordinary:

x:τΓ
Γx:τ
X-Var
C(v)=τ
Γv:τ
X-Const

Blocks and statements use separate contexts:

Δ(f)=σ
ΓΔΞf:σ
X-BVar
Γ,x¯:τ¯Δ,f¯:σ¯Ξs:τ
ΓΔΞ{(x¯:τ¯,f¯:σ¯)s}:(τ¯,σ¯)τ
X-Block
Γe:τ
ΓΔΞe:τ
X-Expr
ΓΔΞs0:τ0Γ,x:τ0ΔΞs1:τ1
ΓΔΞval x=s0;s1:τ1
X-Val
ΓΔΞb:σΓΔ,f:σΞs:τ
ΓΔΞdef f=b;s:τ
X-Def
ΓΔΞb:(τ¯,σ¯)τ0(Γei:τi)i(ΓΔΞbj:σj)j
ΓΔΞb(e¯,b¯):τ0
X-Call
Ω(F)=τ1τ0ΓΔ,F:τ1τ0Ξs:τΓ,x:τ1Δ,k:τ0τΞsh:τ
ΓΔΞhandle{Fs}with{(x,k)sh}:τ
X-Handle

The derivation-indexed runtime interface and label invariant are:

Ξ=Ξ0,:τ,Ξ+Γ,x:τ1Δ,k:τ0τΞ0sh:τ
ΓΔΞcap{(x,k)sh}:τ1τ0
X-Cap
dom(Ξ)ΓΔΞ,:τs:τ
ΓΔΞ#{s}:τ
X-Delim

The decomposition in X-Cap records the capability’s allocation origin. Its stored body is typed in the birth prefix Ξ0, outside both its own delimiter and every later delimiter in Ξ+. The capability newly bound in the handled premise is therefore not in scope in its own clause; the only way to reenter that same delimiter is through continuation k, which reinstalls it. A same-spelled capability already present in the outer block context remains a distinct outer binding. Keeping the prefix, rather than merely deleting from an unordered set, is load bearing for preservation when a request crosses intervening delimiters. Rule X-BVar uses the rightmost lookup fixed above. Replacing it by unordered membership would admit the shadowed outer F in the handled premise and would invalidate the translation argument below.

For example, assume B=IntInt and Ω(F)=Δ(F)=B. Write s20=handle{FF(20)}with{(x,k)k(x)}. Then Ω(F)=B(Δ,F:B)(F)=BΓ20:IntΓΔ,F:BF(20):IntXCall(Δ,k:B)(k)=BΓ,x:Intx:IntΓ,x:IntΔ,k:Bk(x):IntXCallΓΔs20:IntXHandle. This derivation uses every source judgment: the arguments are expressions, F and k are blocks, and the whole program is a statement.

Exercise 32.2 — A System Xi derivation

★☆☆ Assume Ω(F)=Δ(F)=1Int. Derive the type of handle{Fval x=F(());x}with{(u,k)k(7)}. State the type of the continuation block and the common answer type at all four positions of X-Handle.

Why blocks are second class

The tempting escape term is handle{FF}with{(x,k)k(x)}. It has no typing derivation. The handled body must be a statement of a value type τ, but F is a block variable and there is no rule that embeds a block judgment into a statement or expression judgment. Likewise, value arguments cannot be blocks, and every block result is a value type. These are three faces of the same second-class restriction.

The restriction still permits higher-order blocks. A block may receive another block as an argument and invoke it while the defining delimiter is active: def use={(g:1Int)g(())};handle{Fdef q={()F(())};use(q)}with{(u,k)k(7)}. What is forbidden is returning q as an expression value after the handler scope ends.

Exercise 32.3 — Locate the restriction

★☆☆ Assume F:1Int and G:(1,1Int)Int in the block context. For each attempted System-Xi phrase below, identify the missing typing rule or violated syntactic category: val x=F;x,{()F},G(F,F). In the third phrase the first occurrence of F occupies a value-argument position and the second occupies a block-argument position. Finish by deriving the well-typed call G((),F), in which F is used before its delimiter is removed.

System Xi dynamics and safety

Runtime labels are generated by handlers. General evaluation contexts and contexts delimited with respect to are H::=[]val x=H;s#{H},H::=[]val x=H;s#{H}(). Thus H contains no delimiter labelled . The complete root contractions are val x=v;s0s[v/x]XValbeta,def f=u;s0s[u/f]XDefbeta,{(x¯,f¯)s}(v¯,u¯)0s[v¯/x¯][u¯/f¯]XBlockbeta,#{v}0vXDelimret. Handler allocation is the fresh-label contraction handle{Fs}with{(x,k)sh}0#{s[cap{(x,k)sh}/F]}XHandlebeta. Capability capture substitutes both the request argument and a reified continuation: #{H[cap{(x,k)sh}(v)]}0sh[v/x,{(y:τ0)#{H[y]}}/k]XCapbeta. The handler rule chooses fresh. Reduction is compatible closure under H. A capability call is not a redex by itself; it reduces only together with the matching delimiter.

The earlier example calculates as follows: handle{Fval x=F(20);x}with{(x,k)k(x)}0XHandlebeta#{val x=cap{(x,k)k(x)}(20);x}0XCapbeta{(y:Int)#{val x=y;x}}(20)0XBlockbeta#{val x=20;x}XValbeta#{20}0XDelimret20. The continuation reinstalls the delimiter, which is why the handler is deep.

The naive escaped capability H[cap{(x,k)s}(v)] is stuck when there is no surrounding #. The label context is introduced precisely to prove that a closed source program cannot reduce to such a term.

Definition 32.2 — Generated configuration

A generated configuration is a runtime statement reachable from source syntax. Formally, write GenΩ(s) when there is a (possibly open) source statement s0, containing neither cap nor #, such that s0s under the fixed signature Ω. Thus “generated” is a reachability predicate, not an extra runtime constructor or an informal promise about provenance.

Definition 32.3 — Undelimited capability

A statement contains an undelimited capability occurrence of when it has the form H[cap{(x,k)s}(v)] with no surrounding delimiter #.

The safety proof needs the usual structural lemmas. They are stated for all three judgments simultaneously. A block substitution on a derivation is scope respecting when, at every X-Cap subderivation labelled , its restriction to the free block variables of the stored handler body is typable under the birth prefix Ξ0 displayed by that rule. Source substitutions are scope respecting because source blocks contain no labels. The substitutions created by the reduction rules are scope respecting because X-Handle-beta chooses fresh and the capability binding introduced for the handled occurrences of F is absent from its own clause; any same-spelled F in that clause denotes an outer binding. This is the derivation-indexed form of the second-class scope invariant.

Lemma 32.4 — Generatedness closure

Generatedness is closed under one step: GenΩ(s)ssGenΩ(s).

Proof of Lemma 32.4 — Generatedness closure

Proof. Append one step to the witnessing reduction sequence. ◻

Lemma 32.5 — Ordered insertion weakening

If a generated System Xi runtime judgment is derivable, then extending Γ or Δ by fresh bindings preserves it. Extending an ordered label context ΞL,ΞR to ΞL,Ξs,ΞR, where the bindings of Ξs are fresh, also preserves it. If the original derivation is scope respecting, the weakened derivation is scope respecting. Right-suffix weakening is the case ΞR=.

Proof of Lemma 32.5 — Ordered insertion weakening

Proof. Induct on the derivation, retaining the cut ΞL|ΞR at which Ξs is inserted. Variable leaves use membership monotonicity, while block-variable leaves are unchanged because the insertion extends only Ξ, not Δ. Every ordinary rule applies the induction hypotheses to its premises. For X-Cap, compare the cut with the displayed decomposition Ξ0,:τ,Ξ+. If the cut lies in Ξ0, apply the induction hypothesis to the stored-body derivation; the enlarged prefix is the new birth prefix in the adjusted decomposition. If the cut lies at or to the right of , adjust only that decomposition and leave the stored body under its original prefix. Thus the capability-origin invariant is preserved. For X-Delim, alpha-rename its freshly bound away from Ξs, carry the same outer cut into its premise—that is, use the cut ΞL|(ΞR,:τ)—and reapply X-Delim. This proves arbitrary ordered insertion; iteration proves insertion of a finite fresh block. ◻

Lemma 32.6 — Block-context exchange and identical shadowing

Let block types be nondependent. Adjacent distinct bindings in Δ may be exchanged. Moreover, if a judgment is derivable under Δ0,f:σ,Δ1, appending a rightmost binding f:σ preserves it; the new binding shadows the old one at the same type. Both transformations preserve scope respect.

Proof of Lemma 32.6 — Block-context exchange and identical shadowing

Proof. Induct on the derivation. For a block-variable leaf, rightmost lookup either selects a name different from the exchanged pair, selects one member with its unchanged type, or changes an old f:σ lookup to the new f:σ lookup. Every case has the same conclusion type. All other rules rebuild from their induction hypotheses. At X-Cap, perform the same transformation only on the block context; its recorded label birth prefix Ξ0 is unchanged, so the scope-respecting premise is preserved. ◻

Lemma 32.7 — Value and scope-respecting block substitution

The following hold for generated runtime derivations.

  1. If Γ,x:τe:τ and Γv:τ, then Γe[v/x]:τ.

  2. If a block or statement is typed under Γ,x:τ and Γv:τ, substituting v for x preserves its type.

  3. If a block or statement is typed under Δ,f:σ, and a scope-respecting substitution assigns a runtime block value u:σ to f, applying that substitution preserves the judgment. At an ordinary occurrence of f, the value u is typed under the current label context. At an occurrence stored below X-Cap with birth prefix Ξ0, the restricted substitution must type u under Ξ0.

Simultaneous substitutions follow by iteration.

Proof of Lemma 32.7 — Value and scope-respecting block substitution

Proof. Induct on the typing derivation in each clause. The variable and block-variable cases split on whether the looked-up name is the substituted name. Binder cases alpha-rename bound names first. In X-Handle, apply value substitution to the operation parameter and block substitution to the continuation. In X-Cap, use the substitution restricted to the displayed birth prefix Ξ0; scope respect is exactly the premise needed for the stored handler-body derivation. In X-Delim, alpha-renaming keeps the fresh label outside the support of the substitution, so the freshness premise is unchanged. ◻

Lemma 32.8 — Canonical forms

Let the value and block-variable contexts be empty.

  1. If e:τ, then e is a primitive value.

  2. If Ξb:(τ¯,σ¯)τ, then b is a runtime block value u: either a block abstraction or a capability whose label lies in dom(Ξ).

Proof of Lemma 32.8 — Canonical forms

Proof. Invert the final typing rule. A variable conclusion is impossible in the corresponding empty variable context. The remaining expression rule produces a primitive value; the remaining block rules produce a block abstraction or a capability. In the capability case, inversion of X-Cap gives Ξ()=τ, hence dom(Ξ). ◻

Lemma 32.9 — Label-aware progress

If Ξs:τ, then at least one of the following operational alternatives is available:

  1. s is an expression value;

  2. there exists s with ss; or

  3. for some dom(Ξ), the statement has the undelimited form s=H[cap{(x,k)sh}(v)].

The third alternative need not be unique when several outer labels are available; the statement asserts only that every irreducible nonvalue exposes one such authorized outer request.

Proof of Lemma 32.9 — Label-aware progress

Proof. Induct on the statement-typing derivation. An expression is a value by lemma 32.8. For sequencing, apply the induction hypothesis to the bound statement: a value gives X-Val-beta, a step lifts through the context, and an undelimited request remains undelimited after prefixing the sequencing context. A definition has a runtime block value by lemma 32.8 and contracts by X-Def-beta. For a closed block call, the operator and block arguments are runtime block values. A block abstraction gives X-Block-beta; item 1 of lemma 32.8 makes every ordinary value argument a primitive value, so no argument step is missing. A capability labelled gives the third alternative, and inversion of X-Cap puts in dom(Ξ). A handler gives X-Handle-beta.

For #{s0}, apply the induction hypothesis to the premise typed under Ξ,:τ. A value gives X-Delim-ret, and a step lifts under the delimiter. If the induction hypothesis gives a request labelled , the outer delimiter and its no--binding context form an X-Cap-beta redex. If the induction hypothesis gives a request labelled , then prefixing # preserves the no-binding condition for , so the whole statement has the third form for the outer label dom(Ξ). ◻

Theorem 32.10 — Progress for System Xi

If s:τ, then s is an expression value or there exists s with ss.

Proof of Theorem 32.10 — Progress for System Xi

Proof. Apply lemma 32.9. Its third alternative requires a label in dom() and is therefore impossible. ◻

Lemma 32.11 — Typed evaluation-context replacement

Let a typing derivation of ΓΔΞH[s]:τ contain a distinguished subderivation ΓΔΞs:τ0 at the unique hole of H. If ΓΔΞs:τ0, then ΓΔΞH[s]:τ. The same replacement property holds for a no--binding context H, and replacement does not change its no-binding property.

Proof of Lemma 32.11 — Typed evaluation-context replacement

Proof. Induct on the context. For the empty context the replacement premise is the conclusion. A sequencing context uses the replacement induction hypothesis in the first premise of X-Val; its second premise is unchanged. A delimiter context uses the induction hypothesis in the premise of X-Delim; the fresh-label premise is unchanged. The grammar of H is the same induction with the additional fact that every crossed delimiter has label different from . ◻

Theorem 32.12 — Preservation

For a generated, scope-respecting runtime derivation, if GenΩ(s),ΓΔΞs:τandss, then ΓΔΞs:τ, and the reconstructed derivation of s is scope respecting.

Proof of Theorem 32.12 — Preservation

Proof. The three ordinary beta roots use lemma 32.7.

Compatible closure follows from lemma 32.11.

For X-Handle-beta, write Ξ0 for the outer label context and choose fresh . Weakening types the handled computation under Ξ0,:τ. Its handler clause remains typed under its birth prefix Ξ0, so X-Cap, with empty later suffix, types the substituted capability at τ1τ0. The generated substitution is scope respecting: F is bound only in the handled computation and is absent from its own handler clause. Block substitution types the handled body. Rule X-Delim then closes the fresh label scope.

For X-Cap-beta, write Ξ0 for the label context outside the matching delimiter. At the request occurrence, the crossed delimiters in H form a later suffix Ξ+. Inversion of X-Delim and X-Cap recovers the allocation decomposition Ξ0,:τ,Ξ+ and gives (Ξ0,:τ)()=τ,Γv:τ1,Γ,x:τ1Δ,k:τ0τΞ0sh:τ. By lemma 32.11, the typed no-binding context gives, for fresh y:τ0, Γ,y:τ0ΔΞ0#{H[y]}:τ. Here lemma 32.5 first adds the fresh y:τ0 assumption to every inactive premise crossed while exposing the hole; context replacement then changes only the distinguished subderivation. Define the reified continuation K:={(y:τ0)#{H[y]}}. It has type τ0τ under Ξ0. Simultaneous value and block substitution therefore types the reduct sh[v/x,K/k] at τ in the outer context, exactly as required after the delimiter disappears. The no--binding grammar for H contains no inner #, so the reinstalled delimiter is the unique matching binder. The suffix labels occur only inside the reified continuation; they are absent from the stored handler body, exactly as its birth-prefix premise requires. The delimiter-return case follows by inversion.

It remains to verify the second conclusion. Inspect the contracted root and then induct outward through its evaluation context. Ordinary beta roots introduce no capability. At X-Handle-beta, freshness of and separation of the handled premise from the clause give the stored body exactly its outer birth prefix. At X-Cap-beta, the no--binding context reifies a continuation whose delimiter restores the same allocation point; the stored clause stays under its recorded prefix. Delimiter return removes a label and no stored body. Context replacement preserves these facts at each outer frame. ◻

Corollary 32.13 — System Xi label safety

No finite reduction prefix from a closed, well-typed source statement ends in a stuck statement containing an undelimited capability. Equivalently, every reachable nonvalue can step.

Proof of Corollary 32.13 — System Xi label safety

Proof. The closed source typing gives GenΩ at the initial state. Apply lemma 32.4 and preservation at each step, then progress. Thus no reachable nonvalue is stuck on an undelimited System-Xi capability. ◻

Exercise 32.4 — The capability case

★★☆ Consider rule X-Cap-beta. Reconstruct its preservation case using H=val z=[];z. Give the reified continuation’s type and the label context in which the handler body is typed. Identify the premise that would fail if H contained an inner delimiter #. Also explain why a crossed #, with , lies in the later suffix rather than the handler body’s birth prefix.

Effekt: requirements translated to capabilities

Effekt replaces explicit System-Xi capability blocks and runtime labels by a source effect judgment. A closed set of operation names records the capabilities required from the context; the translation later restores one explicit capability parameter for each required name.

Definition 32.14 — Effekt card

Let τ::=IntBool1,σ::=(τ¯,σ¯)τ/ε,ε::={F1,,Fn},v::=()ntruefalse,e::=xv,s::=val x=s;sedef f(x¯:τ¯,g¯:σ¯):τ/ε=s;sf(e¯,g¯)effect F(x:τ1):τ0;sdo F(e)try{s}with F{(x:τ1)sh}. The contexts are a value context Γ, block context Δ, and operation signature Σ, where Σ(F)=τ1τ0. Value variables, ordinary block variables, and operation names are pairwise disjoint syntactic classes, and binders are alpha-renamed before extension. In cross-card comparisons these are ΓE, ΔE, and ΣE; F ranges over operation names, and no runtime-label context occurs in this source calculus. The judgments are Γe:τ,ΓΔΣs:τε.

Expression typing is the same as in System Xi. The complete statement rules are

Γe:τ
ΓΔΣe:τ
E-Expr
ΓΔΣs0:τ0ε0Γ,x:τ0ΔΣs1:τ1ε1
ΓΔΣval x=s0;s1:τ1ε0ε1
E-Val
Γ,x¯:τ¯Δ,g¯:σ¯Σs0:τ0ε0ΓΔ,f:(τ¯,σ¯)τ0/ε0Σs:τε
ΓΔΣdef f(x¯:τ¯,g¯:σ¯):τ0/ε0=s0;s:τ(ε0ε0)ε
E-Def

where ε0 is the inferred effect set of the body. The block closes over ε0ε0 at its definition site, and its declared type requires callers to provide every capability in ε0. There is deliberately no premise requiring every member of ε0 to occur in ε0: an unused annotated effect becomes an unused capability parameter, admitted by weakening. An implementation may warn about it, but must not reject it in this calculus.

For example, with Σ(Ask)=1Int, the block def f():Int/{Ask}=7;f() is typable. Its body has inferred effect , so ε0ε0=, while the call uses the declared set {Ask}. Adding the tempting premise ε0ε0 would reject this harmless unused capability and would invalidate effect weakening for block annotations.

Δ(f)=(τ¯,σ¯)τ/ε(Γei:τi)i(Δ(gj)=σj)j
ΓΔΣf(e¯,g¯):τε
E-Call
ΓΔΣ,F:τ1τ0s:τεFε
ΓΔΣeffect F(x:τ1):τ0;s:τε
E-Effect
Σ(F)=τ1τ0Γe:τ1
ΓΔΣdo F(e):τ0{F}
E-Do
Σ(F)=τ1τ0ΓΔΣs:τεΓ,x:τ1Δ,resume:τ0τ/Σsh:τεh
ΓΔΣtry{s}with F{(x:τ1)sh}:τ(ε{F})εh
E-Try

The continuation is contextually pure: it is a second-class block whose own control effects are handled outside the operation clause.

Let sask abbreviate try{do Ask(())}with Ask{(u:1)resume(7)}. For the derivation only, abbreviate ΣA:=Σ,Ask:1Int,Δr:=Δ,resume:IntInt/. The complete source derivation is assembled from the request and resumption subderivations Ask:1IntΣAΓ():1ΓΔΣAdo Ask(()):Int{Ask}EDoDdo and Δr(resume)=IntInt/Γ,u:17:IntΓ,u:1ΔrΣAresume(7):IntECallDresume. Using Ddo and Dresume as the two E-Try premises gives Ask:1IntΣADdoDresumeΓΔΣAsask:IntETryAskΓΔΣeffect Ask(u:1):Int;sask:IntEEffect. Rule E-Do introduces the requirement, E-Try removes it and types its resumption block, and E-Effect discharges the operation name from the signature context.

Exercise 32.5 — Tracing accidental capture

★☆☆ Write the nearest-name execution of the opening underInner(q) example and identify the exact dynamic handler selected. Then rewrite the call in capability-passing style by adding one block parameter to q and show which handler is selected.

The named Effekt-to-System-Xi translation

Fix a canonical ordering of every effect set. Write CapTy, CapEff, and CapStmtΣ,Δ for the syntax translations; these are not semantic interpretations. Value types and expressions are unchanged. If Σ(F)=τ1τ0, put CapOpΣ(F):=τ1τ0. For F¯=F1,,Fn, let CapOpΣ(F¯):=CapOpΣ(F1),,CapOpΣ(Fn). Then CapEffΣ({F¯}):=F1:CapOpΣ(F1),,Fn:CapOpΣ(Fn),CapTyΣ((τ¯,σ¯)τ0/{F¯}):=(τ¯,CapTyΣ(σ¯),CapOpΣ(F¯))τ0. The statement translation is directed by the source typing derivation. Its subscript records the signature and the current source block context: the call clause reads the ordered effect list F¯ from Δ(f), while a recursive call under a binder uses the extended current context. The fixed canonical ordering makes this context-directed translation deterministic. Its homomorphic clauses are CapStmtΣ,Δ(val x=s0;s1):=val x=CapStmtΣ,Δ(s0);CapStmtΣ,Δ(s1),CapStmtΣ,Δ(e):=e,CapStmtΣ,Δ(f(e¯,g¯)):=f(e¯,g¯,F¯),CapStmtΣ,Δ(effect F(x:τ1):τ0;s):=CapStmtΣ,Δ(s),CapStmtΣ,Δ(do F(e)):=F(e). Block definitions receive the ordered capabilities explicitly: CapStmtΣ,Δ(def f(x¯,g¯):τ0/{F¯}=s0;s):=def f={(x¯,g¯,F¯)CapStmtΣ,Δ(s0)};CapStmtΣ,Δ(s). Handlers bind the operation capability and resumption: CapStmtΣ,Δ(try{s}with F{(x:τ1)sh}):=handle{FCapStmtΣ,Δ(s)}:=with{(x,resume)CapStmtΣ,Δ(sh)}. The block-call clause uses the ordered effects in the declared type of f.

The complete handler example uses the earlier abbreviation sask and translates to CapStmtΣ,Δ(effect Ask(u:1):Int;sask)=handle{AskAsk(())}with{(u,resume)resume(7)}. The operation declaration disappears, the request becomes an ordinary block call, and the handler explicitly binds the capability block. Set the System-Xi operation signature to Ω(op)=Σ(op) for every Effekt operation. Thus if Σ(op)=AB, the translated X-Handle premise uses parameter type A and resumption-result type B, exactly as E-Try requires.

Theorem 32.15 — Effekt-to-System- Xi type preservation

If ΓΔΣs:τε, then ΓCapTyΣ(Δ),CapEffΣ(ε)CapStmtΣ,Δ(s):τ.

Proof of Theorem 32.15 — Effekt-to-System- Xi type preservation

Proof. Induct on the Effekt typing derivation. The expression and sequencing cases are homomorphic, using target weakening to place both translated premises under the union of their capability contexts.

For E-Def, write the annotated effects as ε0 and the inferred body effects as ε0. The induction hypothesis types the translated body under CapTyΣ(Δ,g¯:σ¯),CapEffΣ(ε0). Decompose the last context into the definition-site capabilities CapEffΣ(ε0ε0) and the formal capability parameters CapEffΣ(ε0). Add a genuinely fresh formal by lemma 32.5; if its name already occurs at the same closed operation type, use identical shadowing from lemma 32.6. Exchange distinct adjacent bindings to restore the canonical order. Rule X-Block therefore gives exactly CapTyΣ((τ¯,σ¯)τ0/ε0). The continuation induction hypothesis is typed under CapEffΣ(ε). Weakening both premises to CapEffΣ((ε0ε0)ε) and applying X-Def proves the case. In E-Call, the declared source block type fixes the same canonical order of capability arguments, so X-Call applies.

Translation erases E-Effect. Its side condition Fε gives CapEffΣ(ε{F})=CapEffΣ(ε),F:CapTyΣ(F), so the premise already has the conclusion’s translated capability assumptions. Rule E-Do becomes X-Call on the capability block F. For E-Try, put Θ:=CapTyΣ(Δ),CapEffΣ((ε{F})εh). The handled-statement induction hypothesis is typed under CapTyΣ(Δ),CapEffΣ(ε). Since CapEffΣ(ε{F})Θ, weakening adds the fresh capabilities in Θ. Exchange moves distinct bindings into canonical order. Append F:τ1τ0 on the right; identical shadowing from lemma 32.6 preserves the derivation whether or not εh already contributed an outer F, and rightmost lookup selects the handled binding. The handler-clause induction hypothesis is typed under CapTyΣ(Δ),CapEffΣ(εh),resume:τ0τ, and weakens to Θ,resume:τ0τ. Rule X-Handle therefore yields the translated conclusion under exactly Θ. No translation clause creates a runtime label, so the target label context is empty. ◻

Corollary 32.16 — Effekt effect safety

If Σs:τ, then every finite System-Xi execution prefix of CapStmtΣ,Δ(s) ends in a value or a statement that can step. In particular, the translation cannot reach an unbound operation block or an undelimited escaped capability.

Proof of Corollary 32.16 — Effekt effect safety

Proof. By theorem 32.15, the translation is closed and typed under an empty label context. Put Ω(op)=Σ(op). No clause of CapStmtΣ,Δ introduces cap or #, so s0=CapStmtΣ,Δ(s) is the source witness in definition 32.2; the reflexive zero-step trace proves GenΩ(s0). Use lemma 32.4, theorem 32.12, theorem 32.10 at each execution step. This corollary belongs to the selected Effekt source language through its translation. It is not a theorem about every feature of the full Effekt implementation. ◻

Exercise 32.6 — Translate one block

★☆☆ Let def q(g:1Int/{Ask}):Int/{Log}=sq;s where q’s body calls g, Ask, and Log. Write the translated System-Xi block type, block definition, and one call to q. Distinguish the capability passed to g from the capability required by q itself.

Effect tunnelling: lexical handler identity

Capability passing forbids a capability from escaping by making blocks second class. Tunnelling takes another route: first-class functions may carry latent capability effects, and each request names the handler it intends to invoke. The calculus is independent of System Xi; no translation between them is assumed.

Definition 32.17 — Tunnelling calculus card

Write λtun for the selected core of Abstraction-Safe Effect Handlers via Tunneling [ZM19a]. Its syntax is e::=αh.lbl,T,S::=1IntS[T]e¯α.TΠh:F.[T]e¯,h,g::=hvH,H,G::=handlerF x k.t,t,s::=()nxλx:T.ttslet x:T=t in sΛα.tt[e¯]λh:F.tthh[T]e¯t. Here hv is an atomic handler variable and h,g range over handler terms; binders use h as a metavariable for such an atom. This repairs the self-referential h::=hH printed in the source. The source core has only unit as a base type. The present card adds Int, integer literals, their formation rules below, and no other construct; the running examples require a second observable base type. The form h, read “request upward through handler h,” invokes the authority carried by h. The form [T]e¯t, read “delimit downward at ,” binds that handler label while t runs. Within this separate calculus card, e denotes a capability effect rather than a System Xi expression, and H denotes a handler body rather than a System Xi evaluation context. Capability effects are effect variables, concrete labels, or the hidden label of a handler variable. Contexts are Δ::=Δ,α,P::=P,h:F,Γ::=Γ,x:T,Ξ::=Ξ,:[T]e¯. Fix a global interface signature O with entries O(F)=TS, and write op(F)=TS for that lookup. Here TS abbreviates T[S]. An interface name is well formed exactly when it occurs in dom(O). Thus the four contexts are ΔT, PT, ΓT, and ΞT in cross-card comparisons; F is an interface and H,G are handler values. Every context extension binds a fresh name unless a displayed substitution has first alpha-renamed the binder. In particular, Δ,α, P,h:F, Γ,x:T, and Ξ,:[T]e¯ require the new name to be absent from the domain of the preceding context. The judgments are ΔPΞT type,ΔPΞe¯ effects,ΔPΓΞt:[T]e¯,ΔPΓΞh:Fe. Effects are finite sequences modulo permutation, duplication, and flattening of substituted effect variables.

The selected signature makes its formation, ordinary typing, partial-order, and characteristic tunnelling rules explicit.

The well-formedness layer makes every effect constructor explicit:

ΔPΞ effects
WF-Emp
αΔ
ΔPΞα effects
WF-EVar
dom(Ξ)
ΔPΞ effects
WF-Label
P(h)=F
ΔPΞh.lbl effects
WF-HLabel
ΔPΞe¯1 effectsΔPΞe¯2 effects
ΔPΞe¯1,e¯2 effects
WF-ESeq
ΔPΞ1 type
WF-Unit
ΔPΞInt type
WF-Int
ΔPΞS typeΔPΞT typeΔPΞe¯ effects
ΔPΞS[T]e¯ type
WF-Fun
Δ,αPΞT type
ΔPΞα.T type
WF-EAll
ΔP,h:FΞT typeΔP,h:FΞe¯ effects
ΔPΞΠh:F.[T]e¯ type
WF-HAll

For example, if P(h)=F, then WF-HLabel, WF-Unit, and WF-Fun derive ΔPΞ1[1]h.lbl type.

The ordinary term rules are part of the selected local signature, not an implicit appeal to the simply typed lambda calculus:

ΔPΓΞ():[1]
T-Unit
ΔPΓΞn:[Int]
T-Int
x:TΓ
ΔPΓΞx:[T]
T-Var
ΔPΞS typeΔPΓ,x:SΞt:[T]e¯
ΔPΓΞλx:S.t:[S[T]e¯]
T-Lam
ΔPΓΞt1:[S[T]e¯]e¯ΔPΓΞt2:[S]e¯
ΔPΓΞt1t2:[T]e¯
T-App
ΔPΞS typeΔPΓΞt1:[S]e¯ΔPΓ,x:SΞt2:[T]e¯
ΔPΓΞlet x:S=t1 in t2:[T]e¯
T-Let
Δ,αPΓΞt:[T]
ΔPΓΞΛα.t:[α.T]
T-EAbs

The compatibility family covers the displayed formation, ordinary typing, partial-order, and characteristic tunnelling rules. For T-Let, its compatibility case uses evaluation-context composition. Application and let use one common premise effect; the union-effect forms used later are derived by subsuming premises to a common join. Two preorders are part of the calculus. Type subtyping is the least transitive relation generated by

ΔPΞ11
S-Unit
ΔPΞIntInt
S-Int
ΔPΞT2T1ΔPΞS1S2ΔPΞe¯1e¯2
ΔPΞT1[S1]e¯1T2[S2]e¯2
S-Fun
Δ,αPΞT1T2
ΔPΞα.T1α.T2
S-AllE
ΔP,h:FΞT1T2ΔP,h:FΞe¯1e¯2
ΔPΞΠh:F.[T1]e¯1Πh:F.[T2]e¯2
S-AllH
ΔPΞT1T2ΔPΞT2T3
ΔPΞT1T3
S-Trans

Effect inclusion is generated by (j)(i). e1j=e2i(ΔPΞe2i effects)iΔPΞe¯1e¯2EffSub, where each e2i is read as a singleton effect sequence in the declared effects judgment. This formulation makes function domains contravariant, results and latent effects covariant, and both quantified forms pointwise. The term rule is ΔPΓΞt:[T1]e¯1ΔPΞT1T2ΔPΞe¯1e¯2ΔPΓΞt:[T2]e¯2TSub. The judgments are declarative: Eff-Sub compares normalized finite sets, type subtyping is the reflexive–transitive closure of its structural generators, and T-Sub may occur at any typing boundary. These rules alone do not define a principal-type algorithm or a unique placement of subsumption. When the finite effect preorder has the join e¯=e¯e¯1e¯2, subsume a function computation, its latent result effect, and its argument to e¯, then apply T-App; the conclusion has effect e¯. For sequencing, subsume both T-Let premises to e¯e¯1e¯2 derives sequencing at that union effect. Neither union-effect rule is primitive. The purity premise of T-EAbs is the selected source calculus’s explicit restriction on computations directly quantified by an effect variable; a body with an immediate nonempty effect cannot be hidden by effect abstraction.

ΔPΓΞt:[α.T]e¯0ΔPΞe¯ effects
ΔPΓΞt[e¯]:[T[e¯/α]]e¯0
T-EApp
ΔP,h:FΓΞt:[T]e¯
ΔPΓΞλh:F.t:[Πh:F.[T]e¯]
T-HAbs
ΔPΓΞt:[Πh:F.[T]e¯]e¯0ΔPΓΞh:Fe
ΔPΓΞth:[T[h]]e¯[h],e¯0
T-HApp

Here T[h] and e¯[h] replace the bound handler variable by its argument and replace h.lbl by the argument label. The four characteristic handler and tunnelling rules are

P(h)=F
ΔPΓΞh:Fh.lbl
T-HVar
ΔPΓΞh:Feop(F)=TS
ΔPΓΞh:[T[S]e]
T-Up
Ξ()=[S]e¯op(F)=T1T2ΔPΓ,x:T1,k:T2[S]e¯Ξt:[S]e¯
ΔPΓΞ(handlerF x k.t):F
T-HDef
dom(Ξ)ΔPΓΞ,:[T]e¯t:[T]e¯,ΔPΞT typeΔPΞe¯ effects
ΔPΓΞ[T]e¯t:[T]e¯
T-Down

The first side condition on T-Down states dom(Ξ). The two well-formedness premises are checked under Ξ, which does not contain ; by WF-Label, they imply fl(T,e¯). This derived fact is the region-capability restriction: the fresh label may occur while the guarded term executes, but may not escape in the result type or residual effects.

For a complete use of both judgments, assume P(h)=F and op(F)=1Int. Then ΔPΞ1 typeP(h)=FΔPΓ,x:1Ξh:Fh.lblTHVarop(F)=1IntΔPΓ,x:1Ξh:[1[Int]h.lbl]TUp1[Int]h.lbl1[Int]h.lblh.lblΔPΓ,x:1Ξh:[1[Int]h.lbl]h.lblTSubx:1Γ,x:1ΔPΓ,x:1Ξx:[1]TVar11h.lblΔPΓ,x:1Ξx:[1]h.lblTSubΔPΓ,x:1Ξhx:[Int]h.lblTAppΔPΓΞλx:1.hx:[1[Int]h.lbl]TLam. The handler judgment derives the effect h.lbl, the term judgment records it as a latent function effect, and the enclosing lambda itself is a value with no immediate effect.

A handler definition is paired with the label introduced by the surrounding . Substituting H for a handler variable h also substitutes for h.lbl. Thus handler identity is lexical, not just an operation name.

Exercise 32.7 — The tunnelling judgment

★☆☆ Assume P(h)=F and op(F)=1Int. Derive the type of λx:1.hx. Then show how the well-formedness premises of T-Down prevent from remaining in the function’s result effect annotation.

A request crosses an intervening handler

In the operational displays below, the result annotation [T]e¯ on a delimiter is suppressed when it is determined by the typing derivation. Values and evaluation contexts are v::=()λx:T.tΛα.tλh:F.tH,K::=[]KtvKK[¯]KHlet x:T=K in tK. Define the no-binding judgment ⇝̸K by structural recursion on this grammar: ⇝̸[],⇝̸Kt, ⇝̸vK, ⇝̸K[¯], ⇝̸KHif ⇝̸K,⇝̸let x:T=K in tif ⇝̸K,⇝̸Kif  and ⇝̸K. These clauses are exhaustive: only the delimiter frame binds a runtime label. Reduction is compatible closure under K. Besides ordinary beta and let rules, the roots are (Λα.t)[¯]0t[¯/α],TunEffbeta,(λh:F.t)H0t[H/h],TunHandlerbeta,v0v,TunDownval,K[Hv]0t[v/x,(λy:T2.K[y])/k],TunDownup, where H=handlerFx k.t, op(F)=T1T2, and the context K does not bind . In particular, K may contain for .

Let Ho=handlerFx k.k(7),Hi=handlerFx k.k(9), and abbreviate the two-request body by B(ho,hi):=let z:Int=hi() in ho(). The tunneled program first invokes the inner handler and then uses the outer handler authority while the inner delimiter is still dynamically active: ttun:=[Int]o((λho:F.[Int]ho.lbli((λhi:F.B(ho,hi))Hii))Hoo). Under ho:F,hi:F, the two requests have effects hi.lbl and ho.lbl, respectively. Thus the let body has effects hi.lbl,ho.lbl. The inner handler application replaces hi.lbl by i, and its delimiter discharges i, leaving the still-abstract effect ho.lbl. The outer T-HApp then replaces ho.lbl by o, which the outer delimiter discharges. Hence ttun:[Int]. Two handler-beta steps expose o(i(let z:Int=Hii() in Hoo())). The inner request is handled first and resumes with 9. After the let binding is discharged, the remaining request is Hoo() inside the i-delimiter. Since that delimiter binds i, not o, it belongs to the permitted context K of Tun-Down-up. The outer request therefore crosses the intervening same-operation handler, reaches Hoo, and returns 7. Nearest-name lookup on the same dynamic stack handles the second request at Hii and returns 9. Tunnelling changes the matching key from the operation signature to lexical handler identity.

Exercise 32.8 — Two tunneled steps

★★☆ After the two handler-beta steps for ttun, perform first the Tun-Down-up step at i and then the step at o. Write both reified continuations explicitly. For the second step, explain why replacing io by i=o violates the context side condition.

The tunnelling logical relation

A naive logical relation would define the handler interpretation by structural recursion on the operation signature. Recursive signatures defeat that plan. Let op(F)=1Πh:F.[T]h.lbl,R:=λh:F.(h())h, and let H:=handlerFx k.k(R). Then the closed term (RH) repeats: (RH)((H())H)(λy:Πh:F.[T]h.lbl.(yH))R(RH). The naive clause for H[[F]] must interpret the handler-polymorphic result, whose V-clause asks for H[[F]] again. The recursive call is not on a smaller type. The source repairs exactly this failed definition with a step-indexed logic and the later modality . Recursive handler interpretations occur under one later; Löb induction allows P as the hypothesis when proving P, and monotonicity removes one later from assumptions and conclusion together.

Definition 32.18 — Step-indexed interpretation and closing environments

The source notation suppresses the numerical step index behind . Its explicit syntactic parameter is the well-formed label environment Ξ=1:[T1]e¯1,,n:[Tn]e¯n. There is no additional Kripke order of future label environments in this calculus. Rule T-Down temporarily extends Ξ by one fresh label; the later modality, not label-world extension, justifies recursive use of the step-indexed induction hypothesis at a smaller index.

The interpretation is parameterized by closing environments δ::=δ,α¯1,¯2,ϕ,ρ::=ρ,hH11,H22,η,γ::=γ,xv1,v2. If δ(α)=¯1,¯2,ϕ, substitution replaces α by ¯i on side i, and ϕ relates the two outcome families. If ρ(h)=H11,H22,η, the side-i closing environment maps h to Hii, and η relates the two handlers. Each binding γ(x)=v1,v2 consists of closed values satisfying the value relation at the type assigned to x. Every recursive occurrence of an effect signature lies under , so unfolding it decreases the step index and the defining operator is contractive. All relations in the remainder of this definition are additionally indexed by this fixed syntactic Ξ. The index is suppressed in T,K,S,V,H,U,W, and the environment relations only to keep the formulas readable; it never denotes an implicit future-world order.

The observation relation is asymmetric termination approximation: O(t1,t2):=(v1,v2. t1=v1t2v2)(t1. t1t1O(t1,t2)). For closed terms, evaluation contexts, and potentially effect-stuck terms, define the biorthogonal relations T[[[T]e¯]]δρ(t1,t2):=K1,K2.K[[[T]e¯]]δρ(K1,K2)O(K1[t1],K2[t2]),K[[[T]e¯]]δρ(K1,K2):=(v1,v2.V[[T]]δρ(v1,v2)O(K1[v1],K2[v2]))(t1,t2.S[[[T]e¯]]δρ(t1,t2)O(K1[t1],K2[t2])). The smaller relation S is not an informal exception to biorthogonality. It has the exact closure condition S[[[T]e¯]]δρ(u1,u2):=K1,K2,t1,t2,ψ,¯1,¯2.u1=K1[t1]u2=K2[t2]U[[e¯]]δρ(t1,t2,ψ,¯1,¯2)(¯1. ⇝̸K1)(¯2. ⇝̸K2)t1,t2.ψ(t1,t2)T[[[T]e¯]]δρ(K1[t1],K2[t2]). Thus a pair of isolated requests may be stuck, but every related outcome must become a related computation after plugging it back into contexts that do not bind the request labels.

The structural value relation is V[[1]]δρ(v1,v2):=v1=()v2=(),V[[Int]]δρ(v1,v2):=n. v1=nv2=n,V[[S[T]e¯]]δρ(v1,v2):=u1,u2.V[[S]]δρ(u1,u2)T[[[T]e¯]]δρ(v1u1,v2u2),V[[α.T]]δρ(v1,v2):=¯1,¯2,ϕ.T[[[T]]]δ[α¯1,¯2,ϕ]ρ(v1[¯1],v2[¯2]),V[[Πh:F.[T]e¯]]δρ(v1,v2):=H11,H22,η.H[[F]](H11,H22,η)T[[[T]e¯]]δρ[hH11,H22,η](v1H11,v2H22). The handler relation unfolds one clause under related arguments and related continuations: H[[F]](H11,H22,η):=t1,t2,T1,T2.Hi=handlerFx k.ti (i=1,2)op(F)=T1T2v1,v2.V[[T1]](v1,v2)u1,u2.(w1,w2.V[[T2]](w1,w2)η(u1w1,u2w2))η(t1[v1/x,u1/k],t2[v2/x,u2/k]).

Finally, the semantic effect relation records both request forms and their outcomes. For an effect variable, U[[α]]δρ(t1,t2,ψ,¯1,¯2):=¯1,¯2,ϕ.δ(α)=¯1,¯2,ϕϕ(t1,t2,ψ,¯1,¯2). The primed sequences are the syntactic substitutions carried by δ; the semantic component ϕ, rather than syntactic equality with those sequences, decides whether the currently exposed labels and outcomes are related. For a concrete capability effect e{,h.lbl}, let ρi(e) replace handler-label projections on side i and leave a concrete label unchanged. Then U[[e]]δρ(t1,t2,ψ,¯1,¯2):=1,2.¯1=(1)¯2=(2)ρ1(e)=1ρ2(e)=2(UA[[e]]δρ(t1,t2,ψ,1,2)UB[[e]](t1,t2,ψ,1,2)),U[[e1,,en]]δρ(t1,t2,ψ,¯1,¯2):=i.U[[ei]]δρ(t1,t2,ψ,¯1,¯2). The request clause is UA[[e]]δρ(t1,t2,ψ,1,2):=F,T,T,H1,H2,v1,v2.t1=H11v1t2=H22v2H[[F]](H11,H22,W[[e]]δρ)op(F)=TTV[[T]](v1,v2)ψ=V[[T]]. The context clause is deliberately asymmetric, matching the observation relation: UB[[e]](t1,t2,ψ,1,2):=t1,t2.(K. 1⇝̸K1K[t1]+1K[t1])(K. 2⇝̸K2K[t2]2K[t2])ψ={(t1,t2)}. Labels are interpreted by W[[h.lbl]]δρ(t1,t2):=ρ(h)=H11,H22,ηη(t1,t2),W[[]]δρ(t1,t2):=T,e¯.Ξ()=[T]e¯T[[[T]e¯]]δρ(t1,t2).

Interpret the environments by EΔ(,δ):=δ=,EΔ(Δ,α,δ):=δ,¯1,¯2,ϕ.δ=δ,α¯1,¯2,ϕEΔ(Δ,δ),EP(,ρ):=ρ=,EP(P,h:F,ρ):=ρ,H11,H22,η.ρ=ρ,hH11,H22,ηEP(P,ρ)H[[F]](H11,H22,η),EΓδ,ρ(,γ):=γ=,EΓδ,ρ(Γ,x:T,γ):=γ,v1,v2.γ=γ,xv1,v2EΓδ,ρ(Γ,γ)V[[T]]δρ(v1,v2). Open term refinement is the exact closing-substitution lifting ΔPΓΞt1logt2:[T]e¯:=δ,ρ,γ.EΔ(Δ,δ)EP(P,ρ)EΓδ,ρ(Γ,γ)T[[[T]e¯]]δρ(δ1ρ1γ1t1,δ2ρ2γ2t2). Handler refinement is ΔPΓΞh1logh2:Fe:=δ,ρ,γ.EΔ(Δ,δ)EP(P,ρ)EΓδ,ρ(Γ,γ)H[[F]](δ1ρ1γ1h1,δ2ρ2γ2h2,W[[e]]δρ).

Lemma 32.19 — Delimiter compatibility

The logical-refinement interpretation of every premise of T-Down implies the interpretation of its conclusion.

Proof of Lemma 32.19 — Delimiter compatibility

Proof. Choose one label fresh for Ξ and for the finite supports of both closing environments. Alpha-rename the bound label of T-Down to before applying either closing substitution. Both closed instances therefore use the same concrete name. Extend the one suppressed syntactic index on both sides by the one open entry Ξ+:=Ξ,:[T]e¯; do not form separate entries from the two closed instances of T and e¯. This common extension is required by the concrete-label clause W[[]], which performs one lookup in Ξ+.

Use the premise relation at Ξ+. Fresh-label index weakening embeds related test contexts K1,K2 from Ξ into Ξ+. Bound labels in those contexts may be alpha-renamed away from , so ⇝̸Ki for both i. If both guarded terms terminate normally, the premise relation supplies related values at T, and Tun-Down-val removes the two occurrences of the common delimiter. If a guarded term exposes , the two structural judgments above give the decomposition required by S. Its concrete-effect clause has ¯1=¯2=(); the UB-clause contracts Tun-Down-up on both sides. The arguments use the request’s V-premise, and the reified continuations are λy.Ki[y]. The universally quantified outcome premise relates those continuations, and the leading lowers the numerical index before the recursive T-use.

The freshness discharge used in both alternatives is simultaneous strengthening of T,K,S,V,U, and W: if fl(T,e¯), restricting Ξ+ back to Ξ preserves the interpretation at [T]e¯. Prove it by induction on the displayed relation clauses and the numerical index. The only clause that inspects Ξ+ is W[[]]. The result side condition excludes a literal , and freshness for the supports of δ and ρ excludes an effect-variable or handler-label instantiation to ; every lookup is therefore unchanged. Thus the normal-return and exposed-request alternatives establish exactly the two clauses of O at the original index Ξ. ◻

Lemma 32.20 — Handler-definition compatibility

The logical-refinement interpretation of every premise of T-HDef implies the interpretation of its conclusion.

Proof of Lemma 32.20 — Handler-definition compatibility

Proof. At numerical index n, define the candidate outcome relation ηn(u1,u2):=T[[[S]e¯]]δρ(u1,u2)at index n. Assume as Löb hypothesis that the two handler values satisfy H[[F]] at every smaller index. To establish its clause at n, take related operation arguments v1,v2 and continuations u1,u2 preserving ηn on related results. Extend the closing substitution by x(v1,v2) and k(u1,u2). The clause-body induction hypothesis yields ηn(t1[v1/x,u1/k],t2[v2/x,u2/k]). Any recursive request through the handler consults W[[]]; its H premise asks only for index n1, exactly the Löb hypothesis. This proves the displayed H-clause and closes the induction on n. ◻

Lemma 32.21 — Compatibility mechanism

Every rule displayed in the tunnelling card preserves logical refinement; subappendix A.29 collects the same exact signature. The compatibility proof includes the evaluation-context case T-Let.

Proof of Lemma 32.21 — Compatibility mechanism

Proof. Proceed by induction on the typing derivation. Rule T-Unit uses the unit singleton clause, and T-Int uses the integer-equality clause; T-Var uses the related closing environment. Rule T-Sub uses type covariance and effect monotonicity: increasing the result type or effect weakens the observation required by the relation. Rule T-Lam uses the function clause of V.

For the first nontrivial biorthogonal case, consider T-App. After fixing related closing substitutions, write fi and ui for the two closed instances of its function and argument premises. Their induction hypotheses give T[[[S[T]e¯]e¯]](f1,f2),T[[[S]e¯]](u1,u2). To prove the conclusion, fix K1,K2K[[[T]e¯]]. If related function values g1,g2 have already been obtained, the function clause of V says that g1a1,g2a2 are T[[[T]e¯]]-related for every V[[S]]-related pair a1,a2. Hence the contexts Ki[gi[]] satisfy both clauses of K[[[S]e¯]]: the value clause uses the function relation, and the exposed-request clause is inherited by compatible closure. Applying the argument induction hypothesis gives O(K1[g1u1],K2[g2u2]). Therefore the outer contexts Ki[[]ui] satisfy both clauses of K[[[S[T]e¯]e¯]]. Applying the function induction hypothesis yields O(K1[f1u1],K2[f2u2]), exactly the conclusion relation. This nested-context argument is the representative application pattern; it uses the common effect e¯ in both premises.

For T-Let, related t1 terms are placed in the evaluation context let x:S=[] in t2; the induction hypothesis for t2, under related substitutions extended at x, closes the context composition. Rules T-EAbs and T-EApp extend and instantiate δ, while T-HAbs and T-HApp extend and instantiate ρ using the quantified clauses of V. Rule T-HVar is immediate from EP and the W-clause for h.lbl.

For T-Up, related handlers and related arguments satisfy UA, hence S; the inclusion ST closes the case. Lemma 32.19 supplies the complete T-Down case, including both outcome alternatives and the fresh-label discharge. Lemma 32.20 supplies the T-HDef case with its explicit candidate ηn and numerical Löb descent. These cases cover every rule in the selected local signature. ◻

Definition 32.22 — Program contexts and contextual refinement

A program context is generated by C::=[]C[λx:T.[]]C[[]t]C[t[]]C[let x:T=[] in t]C[let x:T=t in []]C[Λα.[]]C[[][e¯]]C[λh:F.[]]C[[]h]C[t(handlerFx k.[])]C[(handlerFx k.[])]C[[T]e¯[]]. These are full program contexts, not the evaluation contexts K used by the operational semantics. A well-formed closing context has judgment C:ΔPΓΞ[T]e¯T. For well-typed terms at the displayed open boundary, write CtxRef(t1,t2) when, for every such closing program context and every result type T, termination of C[t1] implies termination of C[t2].

Lemma 32.23 — Adequacy

If t1logt2:[T], then O(t1,t2).

Proof of Lemma 32.23 — Adequacy

Source import. This is Lemma 6 (Adequacy), §5.4, article p. 5:22 (physical p. 22) of [ZM19a]. The identity contexts form a related pair in K[[[T]]]: their value clause is immediate, and their S-clause is vacuous because an empty effect sequence contains no effect that could witness an exposed related request. Unfolding T therefore gives the displayed observation. ◻

Corollary 32.24 — Tunnelling parametricity

Every well-typed term or handler in the selected card is logically related to itself at the same open typing boundary.

Proof of Corollary 32.24 — Tunnelling parametricity

Proof. Induction on the typing derivation applies lemma 32.21; the closing environments relate each bound variable to itself. The term and handler conclusions are the two displayed logical-refinement judgments. ◻

Theorem 32.25 — Tunnelling metatheorems

The calculus of definition 32.17 satisfies both conclusions below.

  1. if t:[T], and tt, then t is a value or can step;

  2. logical refinement implies contextual refinement at the same open typing boundary.

Proof of Theorem 32.25 — Tunnelling metatheorems

Source import. The two clauses map to Theorems 2–3, respectively, in §5.4, article p. 5:22 (physical p. 22) of [ZM19a]. The technical report states them in §5.4, pp. 21–22 and supplies the complete static signature and Compatibility Lemmas 7–19 in Appendix B [ZM19b].

  1. Source Theorem 2 specializes the environments of corollary 32.24 to empty ones and the closed computation type [T]. Its parametricity and lemma 32.23 give the stated value-or-step alternative for every reduct.

  2. Source Theorem 3 assumes the same open typing boundary on both sides. Its adequacy and congruence hypotheses quantify over the full program contexts of definition 32.22, yielding contextual refinement at that boundary and at arbitrary observation result type.

The step-indexed, biorthogonal relation and its environment interpretation are source imports; the preceding local compatibility proof establishes only that the displayed rules meet their premises. No later result in this book uses a stronger consequence. ◻

Exercise 32.9 — A logical-relation clause

★★☆ Assume P(h)=F and op(F)=11. Unfold the function clause of V for 1[1]h.lbl. State the assumptions on the two unit arguments, then identify the U component used when the function bodies invoke h. Explain where the handler environment ρ(h) enters.

Control-flow linearity: source card.

Effect presence does not bound resumption use. For the comparison predicate, write uses(r,M)q,q{1,ω}, when every execution path of M invokes resumption r at most once for q=1, with no finite bound asserted for q=ω. A handler that resumes twice separates this predicate from effect safety: both resumptions may target an active handler even though the linear-use judgment rejects the clause. Tang, Hillerström, Lindley, and Morris develop the source calculus and qualified-effect discipline in Soundly Handling Linearity [THLM24]. The display above is only the book’s distinguishing predicate. No syntax, rule, or theorem of that calculus is imported into System Xi, Effekt, or λtun.

Bidirectional control: Olaf source card.

A unidirectional operation sends an argument to its handler and receives a resumption result. Olaf permits the handler computation itself to raise statically tracked effects back toward the suspended requester. Its source judgment and configuration step are ΔΘΓΞt:[τ]c,L;tL;t. Lifetimes occur in effect sequences, and a continuation type records effects on both sides of control transfer. These are Olaf objects: Θ is a lifetime-variable context here, L is a lifetime store, and neither is the System-Xi translation abbreviation used above.

Theorem 32.26 — Olaf source metatheorems

At the exact Olaf signature, well-typed terms are logically related to themselves; closed well-typed programs are value-or-step safe at every reduct; and logical refinement implies contextual refinement.

Proof of Theorem 32.26 — Olaf source metatheorems

Source import. These are Theorems 1–3 in §6.2, article pp. 23–24 of [ZSM20]. Their hypotheses use Olaf’s worlds, fixpoint-handler relation, lifetime effects, and program contexts. They are not consequences of theorem 32.25, and they imply no translation between Olaf and System Xi, Effekt, or λtun. ◻

Locality: global-modality source card.

White’s locality calculus distinguishes ordinary bindings Γ;x:τ from global bindings Γ;x:τ. Context restriction keeps only the latter: /=,(Γ;x:τ)/=Γ/,(Γ;x:τ)/=(Γ/);x:τ. The characteristic introduction premise is Γ/Vv:τΓVbox v:τLocBox. Thus a boxed value cannot retain an ordinary local binding. This is the source card of §3.2, Fig. 2, pp. 12–13 of [Whi26]; it is not System Xi’s second-class block restriction and supplies no theorem about its runtime labels.

Effect reflection: source card.

For an algebraic signature Σ, the source has a free-algebra type WΣ(τ) and a handler-mapping type y(Σ). Reflection and reification have the characteristic rules ΓVv1:y(Σ)op:τ1τ2ΣΓVv2:τ1Γ;x:τ2Cc:τ3ΓCreflect(v1(op))(v2,x.c):τ3ReflUpΓ;x:y(Σ)Cc:τΓCreifyΣ(x.c):WΣ(τ)ReflDown. These are §3.4, Fig. 4, pp. 14–15 of [Whi26]. The semantic comparison in §4.8, p. 23 derives the reification isomorphism from Lemma 4.8 and Yoneda. Because this recent source is retained only as a source-gated comparison, the present block imports neither subject reduction nor normalization. In particular, reflection is not the authority-token discipline of System Xi and is not the tunnelling request constructor.

Mechanism invariants and distinguishing tests

The distinctions can now be stated without relying on shared vocabulary. In this comparison only, an ownership capability is a permission whose typing tracks transfer or disposal of one resource; a capture set is the finite set of names a closure may mention; locality/effect reflection is a boundary discipline recording which effects are available inside, reflected outward, or confined; and control-flow linearity bounds the number of uses of a continuation or resumption. These are comparison predicates, not additional term formers or judgments of the three calculi developed above. A “region capability” in the exercise below means one member of a closure’s capture set that names a lexical region; no separate region calculus is assumed.

mechanism static information distinguishing test
Effect row operation names that may occur two same-named handlers satisfy the same row
Handler capability authority for one runtime-labelled handler region return a closure carrying the handler and test escape
Ownership capability permission to access, transfer, or dispose of a resource transfer a cell without installing any handler
Capture set values or capabilities a closure may mention closures with equal captures can call different handlers
Lexical handler identity the particular handler variable or definition-label pair nest two definitions of the same interface
Effect tunnelling requests search for their named lexical handler through intervening frames place an unrelated handler between request and owner
Locality/effect reflection effects are available, reflected, or confined at a lexical boundary return a capability across its lexical boundary
Control-flow linearity how many times a continuation or resumption may be used invoke one captured resumption twice

The first two columns state the static invariant; the third supplies a program shape that separates it from a neighboring invariant.

Exercise 32.10 — Boundary counterexamples

★★☆ For each claim, give a pair of programs and the premise that makes their observations differ:

  1. nest two handlers for F. Compare equal rows paired with distinct handler identities;

  2. pass a handler block without any cell, then pass a cell capability without any handler;

  3. choose two effect-safe programs that write distinct public integers;

  4. choose related source terms and a backend that replaces one translation by the integer constant 0.

State the missing premise in each case.

Sources.

System Xi, Effekt, their translation, and source effect safety follow [BSO20]. The tunnelling calculus, logical relation, and fundamental, safety, and contextual-refinement theorems follow [ZM19a] and its technical report [ZM19b]. The final comparison cites Olaf’s bidirectional calculus [ZSM20].

Suggested first pass.

Exercise 32.11, Exercise 32.12, Exercise 32.15 form the suggested first pass.

Exercise 32.11

★★☆ Reconstruct progress for the closed System-Xi statement handle{Fval x=F(3);x}with{(x,k)k(x)}. Give every intermediate runtime term, fresh-label premise, and typing judgment. Then replace the handled body by the illegal block escape F and locate the first failed inversion.

Exercise 32.12

★★★ Prove the E-Def and E-Try cases of theorem 32.15 in full detail, including the translated block contexts and the canonical ordering of effect arguments. Explain why the proof would not be syntax directed if different occurrences of the same effect set used different orders.

Exercise 32.13

★★★ Rebuild the preceding compatibility proof for rule T-Down. State the freshness premise dom(Ξ), the extended label environment, the no-binding condition on evaluation contexts, the outcome relation after a matching request, and the point at which the derived fact fl(T,e¯) permits the fresh label binding to be discharged. Identify which two well-formedness premises imply that fact.

Exercise 32.14

★★☆ Construct two well-typed programs that are both effect-safe but are observationally distinguishable because they choose different handlers for the same operation. Then construct two contextually equivalent pure terms whose compiled code could nevertheless be incorrect under a deliberately faulty compiler. State which overstrong implication each pair refutes.

Exercise 32.15

★★☆ For each of the following requirements, choose effect rows, explicit capabilities, tunnelling, ownership, capture sets, or control-flow linearity, and justify the choice by a typing or operational invariant:

  1. prevent a file token from being used after close;

  2. ensure a request crosses an unrelated inner handler;

  3. infer that a function may throw either of two exceptions;

  4. guarantee a resumption is invoked at most once;

  5. record that a closure mentions a region capability;

  6. reject a capability call after its delimiter has returned, naming both the System-Xi second-class-block invariant and the tunnelling result-type/latent-effect non-escape invariant, and saying which calculus still admits a first-class closure;

For two items, explain why a plausible alternative is insufficient.

Exercise 32.16

★★★ Practical project.capability-model Implement the finite handler/capability model in appendix E. Distinguish operation names, handler identities, and runtime labels. Validate second-class escape and label scope. Translate one finite Effekt term to System Xi while preserving its validated identities and labels, and implement a tunnel that searches by handler identity through an intervening handler for the same operation.

Maintain this invariant: every successful authorized request is handled by the unique handler whose identity and active label match its authority, after the finite model rejects duplicate handler identities and labels. The observable result is an exact ten-case report. The decidable acceptance test must cover authorized dispatch, accidental nearest-name capture, distinct same-operation identities, closure authority across a boundary, second-class escape rejection, invalid-label rejection, translation-label preservation, and tunneled dispatch, plus duplicate active-stack and duplicate source-binding rejection. Five typechecking mutations—name-only authority, nearest-handler tunnelling, omitted second-class rejection, omitted stack validation, and omitted translation validation—must each make the frozen oracle fail while the accepted source satisfies the gate in appendix E.

Search the book

Type to search the local edition.