Lectures onType Theory
ch:effect-capabilities: ch:effect-capabilities
appendix sectionsolutions

ch:effect-capabilities: ch:effect-capabilities

Exercise 32.1.

Each request body has effect {ask}. Before the surrounding outer handler is applied, the first displayed phrase still has residual effect {ask}, while the inner handler in each of the latter two phrases discharges that request and leaves empty residual effect. After the active Hout is wrapped around each whole phrase, all three complete programs have empty residual effect.

For the first phrase, nearest-name lookup finds the surrounding Hout. For the second, the dynamically nearer Hin handles the request before the outer handler is reached. For the third, underInner invokes the closure under Hin, so nearest-name lookup again selects Hin.

After replacing each request by ask@Hout(), all three requests carry the same explicit authority and therefore select Hout; the inner same-operation handler is skipped in the latter two programs. Thus identical request effects and identical empty residual effects coexist with different handler identities under nearest-name lookup. Effect presence does not encode the authority used to resolve the request.

exercise 32.2.

The exercise assumption gives Ω(F)=1Int, fixing the operation types required by X-Handle. Let the answer type of the whole handler be Int. In the handled body, F:1Int, hence F:1IntF(()):Int. Rule X-Var gives x:Intx:Int; X-Expr lifts that expression to a statement judgment, and X-Val therefore gives F:1Intval x=F(());x:Int. In the clause, the operation argument has type 1, and the continuation block has type k:IntInt. The numeral 7 has type Int, so X-Call gives u:1k:IntIntk(7):Int. Rule X-Handle now concludes the displayed handler has type Int. The four answer-type occurrences are: the handled body, the handler clause, the continuation codomain, and the entire handler; all are Int.

exercise 32.3.

The restriction occurs at three mutually reinforcing points.

  1. Value types τ do not contain block types σ.

  2. The value context Γ stores only bindings x:τ; block bindings occur only in Δ.

  3. The expression grammar contains values and value variables, but not block variables or block abstractions.

Consequently val x=F;x has no derivation: X-Val requires a statement premise, while X-BVar concludes only a block judgment. The block abstraction {()F} fails for the same reason in the body premise of X-Block. Finally, in G(F,F), the first occurrence of F is in a value-argument position and would require an expression judgment; only the second occurrence is a legal block argument. By contrast, G((),F) is derivable by X-Call: ():1 is the value argument and F:1Int is the block argument. The call is well typed while the matching delimiter is active, but no rule turns its block argument into a returnable value.

exercise 32.4.

Write Ξ0 for the label context outside the matching delimiter. Inversion of the redex typing gives (Ξ0,:τ)()=τ,Γv:τ1,Γ,x:τ1Δ,k:τ0τΞ0sh:τ. For the displayed context and fresh y:τ0, typed context replacement gives Γ,y:τ0ΔΞ0#{val z=y;z}:τ. Rule X-Block therefore gives the reified continuation type ΓΔΞ0{(y:τ0)#{val z=y;z}}:τ0τ. Value substitution replaces x by v, and block substitution replaces k by this continuation, so the handler body remains typed at τ in the outer context Ξ0. If H contained an inner delimiter #, it would not belong to the no--binding grammar H; the context-replacement premise used to reconstruct the unique matching delimiter would therefore be unavailable. By contrast, a crossed # with belongs to the later suffix Ξ+. It is retained inside the reified continuation and is absent from the handler body’s birth-prefix derivation under Ξ0.

exercise 32.5.

Under nearest-name lookup the call stack is Hin::Hout::. Invoking q produces a request named ask. The first matching stack entry is Hin, so the execution returns 9, even though the closure was created under Hout.

In capability-passing style write q(F):=F(()),underInner(q,Fout):=handleHin(q(Fout)). The block argument Fout names the outer capability. The inner handler does not replace the argument, so the request reaches Hout and returns 7.

exercise 32.6.

Assume Σ(Ask)=1Int and Σ(Log)=Int1. The block parameter translates as CapTy(1Int/{Ask})=(1,1Int)Int. Since q’s declared effect is {Log}, its translated type is ((1,1Int)Int,Int1)Int. Under the fixed canonical ordering, the second parameter position of the inner type corresponds to Ask, and the final parameter position of the outer type corresponds to Log. The direct Ask requirement in the body is not in the declared set of q; rule E-Def therefore makes it a definition-site capability. Writing that closed-over capability as Askdef, the target has the shape def q={(g,Log)val a=g((),Askdef);val b=Askdef(());val u=Log(a);b};s. A call is q(g,Logcall). Thus the capability passed to g is Askdef, while the capability required by q’s own declared effect is Logcall.

exercise 32.7.

Rule T-HVar gives ΔPΓ,x:1Ξh:Fh.lbl. Then T-Up gives the pure operation value h:[1[Int]h.lbl]. Rule T-Sub widens its immediate effect from to h.lbl, using reflexive type subtyping. A second use of T-Sub widens x:[1] to x:[1]h.lbl. The two premises and the latent result effect now have the one common effect required by T-App, which yields ΔPΓ,x:1Ξhx:[Int]h.lbl. Rule WF-Unit supplies ΔPΞ1 type. Finally T-Lam gives ΔPΓΞλx:1.hx:[1[Int]h.lbl]. If h is instantiated by H, the latent effect becomes . A delimiter cannot discharge it while returning that function, because T-Down requires fl(T,e¯); here occurs inside the returned function type.

exercise 32.8.

After the two handler-beta steps, the inner delimiter contains Ki=let z:Int=[] in Hoo(),Ki[Hii()]. The context Ki does not bind i. Rule Tun-Down-up therefore substitutes x(),kλy:Int.i(let z:Int=y in Hoo()) into the body of Hi. Since that body is k(9), the inner step gives (λy:Int.i(let z:Int=y in Hoo()))9, which reduces to i(Hoo()). The outer delimiter now sees Ko=i[],Ko[Hoo()]. Because io, Ko does not bind o. The second Tun-Down-up step substitutes x(),kλy:Int.o(iy) into the body of Ho. Since that body is k(7), the immediate reduct is (λy:Int.o(iy))7, which reduces to 7 after both value delimiters disappear. If i=o, then Ko itself binds the target label, so it is not an admissible no-binding context for the outer rule. The dynamically inner matching delimiter must handle the request instead.

exercise 32.9.

The function clause requires related unit arguments u1,u2: V[[1]](u1,u2), which unfolds to u1=()u2=(). It must then establish T[[[1]h.lbl]]δρ(v1u1,v2u2). When the bodies invoke h, the relevant semantic effect clause is UA. The handler environment has the form ρ(h)=H11,H22,η. It supplies both concrete handlers and the relation η. The label interpretation W[[h.lbl]] is exactly η, and UA requires the two requests to use H11 and H22, to have related unit arguments, and to produce outcomes related at unit. Thus the handler identity enters through ρ, not through operation-name equality.

Exercise 32.10.

  1. Both Hout and Hin handle ask, so the row {ask} is present in either case, but the handlers return 7 and 9. Missing premise: a handler identity or authority relation.

  2. A capability block for ask grants authority to invoke that handler. It says nothing about a separately allocated mutable cell and does not prevent aliases to that cell. Missing premise: an ownership or separation invariant for memory.

  3. The two closed handlers that always return 7 and 9 are both effect-safe, yet a context observes different integers. Missing premise: a relational abstraction or noninterference condition.

  4. Let source terms t1,t2 be logically related. A faulty compiler may translate t1 correctly and translate t2 to divergence. Source logical relatedness says nothing about that arbitrary mapping. Missing premise: a compiler simulation or semantic-preservation theorem.

Exercise 32.11.

The handler step chooses fresh and gives #{val x=cap{(x,k)k(x)}(3);x}. The capability redex reifies kc={(y:Int)#{val x=y;x}}:IntInt and substitutes 3 for the clause parameter, yielding kc(3). Block beta gives #{val x=3;x}; value beta gives #{3}; delimiter return gives 3. Every term is typed at Int under the label context appropriate to its delimiter.

Replacing the body by the block variable F fails before runtime. The handled-body premise of X-Handle requires a statement judgment; X-BVar derives only a block judgment. Thus no inversion case gives F a value type that could escape the handler.

Exercise 32.12.

For E-Def, let the annotated effect set be ε0={F1,,Fn} in its canonical order. The induction hypothesis types the translated body under CapTy(Δ,g¯:σ¯),CapEff(ε0). Keep the definition-site capabilities CapEff(ε0ε0) in the surrounding block context and add every annotated capability in ε0 as an ordered formal parameter. Their union contains all capabilities in ε0; if an annotation is unused, target weakening supplies its formal binding. Abstracting over the ordinary parameters, translated block parameters, and F1,,Fn therefore gives exactly CapTy((τ¯,σ¯)τ0/ε0). The continuation induction hypothesis is weakened to the union of its own capabilities and the retained definition-site capabilities, after which X-Def applies.

For E-Try, put Θ=CapTy(Δ),CapEff((ε{F})εh). The translated handled statement weakens to Θ,F:τ1τ0, where the terminal binding is the capability introduced by the handler and shadows any outer F required by the clause. The translated clause weakens to Θ,resume:τ0τ. Rule X-Handle then produces the target statement at τ under exactly Θ. Thus source set subtraction corresponds to binding the handled occurrence of F, while an occurrence of F in εh remains available to the clause as an outer capability.

If one occurrence ordered {F,G} as (F,G) and another as (G,F), the same source block type would translate to two different positional block types and calls could swap capabilities. A fixed canonical order is therefore part of the syntax translation and of the induction invariant.

Exercise 32.13.

In the T-Down case, its first premise gives dom(Ξ). Choose one label fresh for Ξ and for both closing environments, alpha-rename the bound label before either closing substitution, and use the common extension Ξ+=Ξ,:[T]e¯. The induction hypothesis relates the guarded bodies at effects containing that common label. For a pair of related evaluation contexts in K, the semantic stuck relation S requires ⇝̸Ki. Its concrete-effect decomposition has ¯1=¯2=(). A matching pair of tunneled requests is classified by UB; Tun-Down-up contracts both sides, and the outcome relation is fed back through the reified continuations λy.Ki[y]. The leading later modality lowers the numerical index before the recursive T-use.

Because the type and effect well-formedness premises are checked under Ξ, which lacks , rule WF-Label derives fl(T,e¯). Neither the result type nor the residual effect interpretation can therefore mention the fresh label. Restricting Ξ+ back to Ξ yields the delimiter conclusion. No separate Kripke future-world restriction is involved.

Exercise 32.14.

For the first pair, use two closed programs that each handle their only ask request, one with Hout and one with Hin. Both are effect-safe: neither can become stuck on an unhandled request. They return 7 and 9, so an integer-observing context distinguishes them. This refutes “effect safety implies contextual equivalence”.

For the second pair, take two beta-equivalent pure terms, for example (λx.x)() and (). They are contextually equivalent in the source. Define a deliberately faulty compiler that maps the first to the target value () and the second to divergence. The target programs differ, so source equivalence alone does not imply compiler correctness. The missing premise is a semantic-preservation or simulation theorem for the compiler.

Exercise 32.15.

  1. Ownership: a file token needs exclusive state-transition authority, not merely an effect name.

  2. Tunnelling: the request must cross an unrelated same-operation handler and match its lexical handler identity.

  3. Effect rows: the requirement concerns the set of possible exception operations, not one handler instance.

  4. Control-flow linearity: the invariant counts resumption uses.

  5. Capture sets: the property is which capability value the closure may mention.

  6. Explicit capabilities with a label scope: the call must be rejected once the matching delimiter has left the dynamic context.

Effect rows are insufficient for item 2 because they do not distinguish the inner and outer handler instances. A capture set is insufficient for item 4: knowing that a closure mentions a resumption does not limit the number of calls to it.

Search the book

Type to search the local edition.