exercise 22.1.
Let the initial counter be 𝑛. Call by value evaluates the argument before beta reduction: (𝜆𝑥.())𝗍𝗂𝖼𝗄()⟶(𝜆𝑥.())()⟶(),𝑛⟼𝑛+1. Call by name substitutes the unevaluated argument: (𝜆𝑥.())𝗍𝗂𝖼𝗄()⟶(),𝑛⟼𝑛. The traces first differ at argument evaluation: the value strategy performs the tick before beta reduction, while the name strategy takes beta first and the absent occurrence of 𝑥 discards the argument.
exercise 22.2.
A raise request never receives a response, so its response-indexed family is the unique map 𝖺𝖻𝗌𝗎𝗋𝖽0 :0 →𝑇Σ𝐴. Giving it response type 1 would supply a branch 𝑘(()) :𝑇Σ𝐴. That branch falsely depicts an execution in which the outside world acknowledges the exception and the computation after the raise continues.
exercise 22.3.
Let 𝑘 :𝑅𝗈𝗉 →𝑇Σ𝐴, 𝑓 :𝐴 →𝑇Σ𝐵, and 𝑔 :𝐵 →𝑇Σ𝐶. Then (𝖮𝗉𝗈𝗉(𝑝,𝑘)≫=𝑓)≫=𝑔=𝖮𝗉𝗈𝗉(𝑝,𝜆𝑟:𝑅𝗈𝗉.(𝑘(𝑟)≫=𝑓)≫=𝑔)=𝖮𝗉𝗈𝗉(𝑝,𝜆𝑟:𝑅𝗈𝗉.𝑘(𝑟)≫=(𝜆𝑎.𝑓(𝑎)≫=𝑔))=𝖮𝗉𝗈𝗉(𝑝,𝑘)≫=(𝜆𝑎.𝑓(𝑎)≫=𝑔). The middle equality uses the induction hypothesis at every 𝑟 :𝑅𝗈𝗉. By the branch equality stipulated in definition 22.1, those pointwise equalities are exactly equality of the two continuation branches; no additional function-extensionality axiom is used.
exercise 22.4.
With result 𝑆, take 𝑡1=𝖮𝗉𝗀𝖾𝗍((),𝜆𝑠.𝖮𝗉𝗀𝖾𝗍((),𝜆_.𝖱𝖾𝗍(𝑠))) and 𝑡2 =𝖮𝗉𝗀𝖾𝗍((),𝜆𝑠.𝖱𝖾𝗍(𝑠)). A usual state theory identifies them: the second read, with no intervening write, returns the same store as the first. They are unequal raw trees. After constructor injectivity removes their common outer get node, the branch of 𝑡1 begins with another operation node whereas the corresponding branch of 𝑡2 is a return node; distinct constructors cannot be equal.
exercise 22.5.
Use carrier 𝖫𝗂𝗌𝗍(𝐴) and 𝑟(𝑎)=[𝑎],ℎ𝖼𝗁𝗈𝗈𝗌𝖾((),𝑘)=𝑘(𝖿𝖺𝗅𝗌𝖾)++𝑘(𝗍𝗋𝗎𝖾), where + + is list concatenation. The fold equation is therefore 𝖿𝗈𝗅𝖽(𝖮𝗉𝖼𝗁𝗈𝗈𝗌𝖾((),𝑘))=𝖿𝗈𝗅𝖽(𝑘(𝖿𝖺𝗅𝗌𝖾))++𝖿𝗈𝗅𝖽(𝑘(𝗍𝗋𝗎𝖾)), so every false subtree is explored before its corresponding true subtree. Well-foundedness makes each result list finite.
exercise 22.6.
If a closed terminal 𝑀 has type 𝐹𝐴, inversion of terminal canonical forms gives 𝑀 =𝗋𝖾𝗍𝗎𝗋𝗇 𝑉. If it has type 𝐴′ ⇒𝐶, the same lemma gives 𝑀 =𝜆𝑥.𝑁. Hence one fixed well-typed terminal cannot have both shapes. The decisive facts are inversion of C-Return and C-Lam: the former concludes only an 𝐹 type and the latter only a computation arrow.
exercise 22.7.
For 𝑐 :𝑏, the translation is 𝗋𝖾𝗍𝗎𝗋𝗇(𝗍𝗁𝗎𝗇𝗄(𝜆𝑓.(𝗋𝖾𝗍𝗎𝗋𝗇𝑓 𝗍𝗈 𝑔.𝗋𝖾𝗍𝗎𝗋𝗇𝑐 𝗍𝗈 𝑎.(𝖿𝗈𝗋𝖼𝖾𝑔)𝑎)))𝗍𝗈 𝑞.𝗋𝖾𝗍𝗎𝗋𝗇(𝗍𝗁𝗎𝗇𝗄(𝜆𝑥.𝗋𝖾𝗍𝗎𝗋𝗇𝑥)) 𝗍𝗈 𝑖.(𝖿𝗈𝗋𝖼𝖾𝑞)𝑖. The binders have types 𝑞:𝑈(𝑈(𝑏⇒𝐹𝑏)⇒𝐹𝑏),𝑖,𝑓,𝑔:𝑈(𝑏⇒𝐹𝑏),𝑎:𝑏. Two To steps, Force, and Beta expose the body. Its two To steps bind 𝑔 =𝑖 and 𝑎 =𝑐; two more Force/Beta steps reduce the identity call to 𝗋𝖾𝗍𝗎𝗋𝗇 𝑐.
exercise 22.8.
Rename binders fresh. For abstraction, ((𝜆𝑦.𝑒)[𝑢/𝑥])𝑛=𝜆𝑦.(𝑒[𝑢/𝑥])𝑛≡𝑎𝜆𝑦.𝑒𝑛[𝗍𝗁𝗎𝗇𝗄𝑢𝑛/𝑥]=(𝜆𝑦.𝑒)𝑛[𝗍𝗁𝗎𝗇𝗄𝑢𝑛/𝑥]. For application, congruence and the two induction hypotheses give ((𝑒1𝑒2)[𝑢/𝑥])𝑛=(𝑒1[𝑢/𝑥])𝑛(𝗍𝗁𝗎𝗇𝗄(𝑒2[𝑢/𝑥])𝑛)≡𝑎𝑒𝑛1[𝗍𝗁𝗎𝗇𝗄𝑢𝑛/𝑥](𝗍𝗁𝗎𝗇𝗄(𝑒𝑛2[𝗍𝗁𝗎𝗇𝗄𝑢𝑛/𝑥]))=(𝑒1𝑒2)𝑛[𝗍𝗁𝗎𝗇𝗄𝑢𝑛/𝑥]. Weak reduction does not enter the lambda in the first calculation. It therefore cannot contract a force–thunk redex created in its body; ≡𝑎, whose compatible closure includes lambda bodies, is necessary.
exercise 22.9.
The outer application translates to (𝜆𝑥.ℎ𝑛(𝗍𝗁𝗎𝗇𝗄(𝖿𝗈𝗋𝖼𝖾𝑥))(𝗍𝗁𝗎𝗇𝗄(𝖿𝗈𝗋𝖼𝖾𝑥)))(𝗍𝗁𝗎𝗇𝗄𝑢𝑛), where each displayed source application uses the call-by-name application clause and the curried type of ℎ determines the parentheses. Beta reduction yields ℎ𝑛(𝗍𝗁𝗎𝗇𝗄(𝖿𝗈𝗋𝖼𝖾(𝗍𝗁𝗎𝗇𝗄𝑢𝑛)))(𝗍𝗁𝗎𝗇𝗄(𝖿𝗈𝗋𝖼𝖾(𝗍𝗁𝗎𝗇𝗄𝑢𝑛))). Thus the substituted thunk for 𝑢 occurs twice, once below each thunk passed to ℎ; the two occurrences of 𝑥𝑛 =𝖿𝗈𝗋𝖼𝖾 𝑥 are the two forces. Each is run only if the corresponding argument of ℎ is demanded.
exercise 22.10.
In context 𝑥 :1, the operation rule gives 𝗋𝖺𝗂𝗌𝖾𝑒0(𝑧.𝗋𝖾𝗍𝗎𝗋𝗇𝑧):𝐹0!{𝗋𝖺𝗂𝗌𝖾}. Hence C-LamΣ gives 𝜆𝑥.𝗋𝖺𝗂𝗌𝖾𝑒0(𝑧.𝗋𝖾𝗍𝗎𝗋𝗇𝑧):1⇒{𝗋𝖺𝗂𝗌𝖾}𝐹0!∅, and V-ThunkΣ gives the value type 𝑈∅(1⇒{𝗋𝖺𝗂𝗌𝖾}𝐹0). The thunk has no immediate computation effect because it is a value; the lambda computation also has empty immediate effect; entering the arrow body has latent effect {𝗋𝖺𝗂𝗌𝖾}.
exercise 22.11.
Let Σ(𝖼𝗁𝗈𝗈𝗌𝖾) =1 ⇝𝖡𝗈𝗈𝗅 and let the handler preserve result type 𝐴. Put Ein ={𝖼𝗁𝗈𝗈𝗌𝖾} ∪D and choose D ⊆Eout. Its return premise is Γ,𝑥:𝐴⊢𝑐𝗋𝖾𝗍𝗎𝗋𝗇𝑥:𝐹𝐴!Eout, using C-Weaken. In the choose clause, 𝑝:1,𝑘:𝑈∅(𝖡𝗈𝗈𝗅⇒Eout𝐹𝐴). Both (𝖿𝗈𝗋𝖼𝖾 𝑘) 𝖿𝖺𝗅𝗌𝖾 and (𝖿𝗈𝗋𝖼𝖾 𝑘) 𝗍𝗋𝗎𝖾 have 𝐹𝐴!Eout by C-ForceΣ and C-AppΣ; their sequencing has the same annotation. The two occurrences on the continuation arrow are exactly the output effect that types the two resumptions. Finally Ein ∖{𝖼𝗁𝗈𝗈𝗌𝖾} ⊆Eout is the forwarding premise.
exercise 22.12.
Under the proposed side condition, every input effect must also be an output effect. Hence an exception handler with Ein ={𝗋𝖺𝗂𝗌𝖾} can receive only an output annotation satisfying {𝗋𝖺𝗂𝗌𝖾}⊆Eout. The smallest inferred-looking result is therefore 𝗁𝖺𝗇𝖽𝗅𝖾𝑀𝗐𝗂𝗍𝗁𝐻𝐸:𝐹𝐴!{𝗋𝖺𝗂𝗌𝖾}, even though 𝐻𝐸 has a well-typed raise clause and no raise can escape. The rule remains safe because it forgets no possible operation; progress may still expose only names retained in the output. It fails as an elimination rule because handling 𝗋𝖺𝗂𝗌𝖾 can never remove that name.
exercise 22.13.
For example, instantiate the callback effect separately at ∅, {𝗋𝖺𝗂𝗌𝖾}, and {𝗀𝖾𝗍,𝗉𝗎𝗍}: 𝑈∅(𝐴⇒∅𝐹𝐴)⇒∅(𝐴⇒∅𝐹𝐴),𝑈∅(𝐴⇒{𝗋𝖺𝗂𝗌𝖾}𝐹𝐴)⇒∅(𝐴⇒{𝗋𝖺𝗂𝗌𝖾}𝐹𝐴),𝑈∅(𝐴⇒{𝗀𝖾𝗍,𝗉𝗎𝗍}𝐹𝐴)⇒∅(𝐴⇒{𝗀𝖾𝗍,𝗉𝗎𝗍}𝐹𝐴). The same callback effect appears on the input arrow and the returned arrow; the surrounding thunk and outer arrow remain pure. A quantified effect variable must abstract exactly those two repeated finite sets.
exercise 22.14.
If the program reads 𝑠, writes 𝑠 +1, and returns 𝑎, no raise occurs. Both orders give 𝖢𝖺𝗍𝖼𝗁(𝖲𝗍𝖺𝗍𝖾(𝑡)(𝑠0))=𝖱𝖾𝗍(𝑎,𝑠0+1)=𝖲𝗍𝖺𝗍𝖾(𝖢𝖺𝗍𝖼𝗁(𝑡))(𝑠0). If the raise is moved before the write, that write lies in the impossible continuation. State first forwards the raise before changing the store and the outside catch returns (𝑎0,𝑠0); catch first replaces the raise by 𝖱𝖾𝗍(𝑎0) before state sees any write. Thus both orders give 𝖱𝖾𝗍(𝑎0,𝑠0). Neither modified program distinguishes the two orders. Only the original write-then-raise program distinguishes rollback 𝖱𝖾𝗍(𝑎0,𝑠0) from commit 𝖱𝖾𝗍(𝑎0,𝑠0 +1).
exercise 22.15.
The outer choose is handled and the clause first resumes at 𝑏1 =𝖿𝖺𝗅𝗌𝖾. With the erroneous shallow continuation, that resumption is 𝖼𝗁𝗈𝗈𝗌𝖾()(𝑏2.𝗋𝖾𝗍𝗎𝗋𝗇𝑏2) without an enclosing 𝐻𝗍𝗐𝗂𝖼𝖾. This inner choose is the first exposed request. The deleted occurrence 𝗁𝖺𝗇𝖽𝗅𝖾 𝑋[𝑀[𝑦/𝑥]] 𝗐𝗂𝗍𝗁 𝐻𝗍𝗐𝗂𝖼𝖾 inside ̂𝑘 is precisely the handler which would capture it. Reinstalling only around the clause, rather than inside each resumption, is too late.
exercise 22.16.
Let 𝐻𝐸 have exception and return clauses but no get clause. One forwarding step is 𝗁𝖺𝗇𝖽𝗅𝖾(𝗀𝖾𝗍()(𝑠.𝗋𝖾𝗍𝗎𝗋𝗇𝑠))𝗐𝗂𝗍𝗁𝐻𝐸⟶𝗀𝖾𝗍()(𝑦.𝗁𝖺𝗇𝖽𝗅𝖾(𝗋𝖾𝗍𝗎𝗋𝗇𝑦)𝗐𝗂𝗍𝗁𝐻𝐸). The rebuilt continuation is 𝑦.𝗁𝖺𝗇𝖽𝗅𝖾(𝗋𝖾𝗍𝗎𝗋𝗇 𝑦)𝗐𝗂𝗍𝗁 𝐻𝐸; it retains the exception handler. Put this term under a state handler 𝐻𝑆. The get node is now captured by that nearest 𝐻𝑆, which supplies the current store to 𝑦; the preserved continuation then returns that value through 𝐻𝐸.
exercise 22.17.
Suppose Γ ⊢𝑐𝑋[𝖼𝗁𝗈𝗈𝗌𝖾() (𝑥.𝑀)] :𝐹𝐴!Ein and the handler produces 𝐹𝐵!Eout. Decomposition gives Γ,𝑥:𝖡𝗈𝗈𝗅⊢𝑐𝑀:𝐹𝐴!E0. For fresh 𝑦, response substitution gives Γ,𝑦 :𝖡𝗈𝗈𝗅 ⊢𝑐𝑀[𝑦/𝑥] :𝐹𝐴!E0. Effect weakening gives the same term annotation {𝖼𝗁𝗈𝗈𝗌𝖾} ∪E0, the annotation of the hole. Replacement therefore types 𝑋[𝑀[𝑦/𝑥]] at the handler input. Applying C-Handle, then C-LamΣ and V-ThunkΣ, gives Γ⊢𝑣̂𝑘:𝑈∅(𝖡𝗈𝗈𝗅⇒Eout𝐹𝐵). Parameter substitution replaces 𝑝 :1 by () in the choose-clause premise; continuation substitution then replaces 𝑘 by ̂𝑘. The resulting clause body has exactly 𝐹𝐵!Eout, the type of the reduct.
exercise 22.18.
For 𝐻𝗍𝗐𝗂𝖼𝖾 the return component is 𝑟(𝑎) =𝖱𝖾𝗍(𝑎) and its choose component is ℎ𝖼𝗁𝗈𝗈𝗌𝖾((),𝑔)=𝑔(𝖿𝖺𝗅𝗌𝖾)≫=(𝜆_.𝑔(𝗍𝗋𝗎𝖾)). The input reifies as 𝖮𝗉𝖼𝗁𝗈𝗈𝗌𝖾((),𝜆𝑏.𝖱𝖾𝗍(𝑏)). The handled-operation step reifies on both sides as 𝖱𝖾𝗍(𝖿𝖺𝗅𝗌𝖾)≫=(𝜆_.𝖱𝖾𝗍(𝗍𝗋𝗎𝖾))=𝖱𝖾𝗍(𝗍𝗋𝗎𝖾). At each resumption, 𝖿𝗈𝗋𝖼𝖾(𝗍𝗁𝗎𝗇𝗄(𝜆𝑏.𝑃)) 𝑉 and then (𝜆𝑏.𝑃) 𝑉 both reify as 𝑃[𝑉/𝑏] by the two administrative clauses. Thus the operation step and both steps at each resume satisfy the theorem’s one-step equality, and the final operational result reifies as the fold.
exercise 22.19.
For 𝐸 =2 ×[ ] and body 𝑘 3, the original term captures the doubling context: 𝗋𝖾𝗌𝖾𝗍(2×𝗌𝗁𝗂𝖿𝗍 𝑘.𝑘3)⟶𝗋𝖾𝗌𝖾𝗍((𝜆𝑥.𝗋𝖾𝗌𝖾𝗍(2𝑥))3)⟶∗6. After pushing the context into the body, the captured context is empty: 𝗋𝖾𝗌𝖾𝗍(𝗌𝗁𝗂𝖿𝗍 𝑘.2×(𝑘3))⟶𝗋𝖾𝗌𝖾𝗍(2×((𝜆𝑥.𝗋𝖾𝗌𝖾𝗍(𝑥))3))⟶∗6. So this one-invocation instance happens to be equal. With body 𝑘(𝑘 3), the original term invokes the captured doubling context twice and gives 𝗋𝖾𝗌𝖾𝗍((𝜆𝑥.𝗋𝖾𝗌𝖾𝗍(2𝑥))((𝜆𝑥.𝗋𝖾𝗌𝖾𝗍(2𝑥))3))⟶∗12. The pushed term captures only the empty context and gives 𝗋𝖾𝗌𝖾𝗍(2×((𝜆𝑥.𝗋𝖾𝗌𝖾𝗍(𝑥))((𝜆𝑥.𝗋𝖾𝗌𝖾𝗍(𝑥))3)))⟶∗6. Thus the would-be commutation law fails when the captured context is invoked twice.
exercise 22.20.
For the one-handler term, C-Op gives its body input Ein ={𝗈𝗉}, and the pure clauses let C-Handle choose Eout =∅. In the two-handler term, the inner instance has the same two annotations. Its handled result is therefore typed with input ∅ at the outer instance, whose output is again ∅.
If scoped availability itself is represented by a finite set, one layer is {𝗈𝗉} and two layers are {𝗈𝗉} ∪{𝗈𝗉}. Finite-set idempotence gives {𝗈𝗉}∪{𝗈𝗉}={𝗈𝗉}. Consequently the annotation cannot answer whether eliminating the nearest handler occurrence should leave another scoped occurrence of 𝗈𝗉 behind. Set subtraction removes the only recorded name in both cases, so it cannot express one-occurrence removal.