Prerequisites. Direct starred prerequisites: none. No later core chapter depends on this route.
A first-order operation node contains an operation and the continuation which receives its response. This is enough for reading state, raising an exception, or choosing a Boolean. It is not enough for 𝖼𝖺𝗍𝖼𝗁(𝑀1,𝑀2), because both arguments are computations. An implementation of catch must inspect the completed translation of 𝑀1, and it must retain the completed translation of 𝑀2 for the failure case. A first-order fold recursively translates only the response continuation. Neither computation argument has a place in which that recursion can occur.
One repair is a global translation that pattern matches on every source constructor, but then adding an operation requires editing the translator. A modular representation instead associates a translation clause with each operation and composes independently defined clauses.
Two recursive operations are distinct. For a higher-order node with fork family 𝜓 and continuation 𝑘, monadic bind changes only 𝑘(𝑟) to 𝑘(𝑟)≫=𝑓. Elaboration applies its recursive map to every 𝜓(𝑠) and every 𝑘(𝑟). Hence bind recursion ranges over responses, while elaboration recursion ranges over forks and responses.
The missing recursive positions
Recall the first-order signature used in chapter 22. It is a pair Δ=(𝖮𝗉Δ,𝖱𝖾𝗍Δ),𝖱𝖾𝗍Δ:𝖮𝗉Δ→𝐒𝐞𝐭. Its free tree is generated by 𝗉𝗎𝗋𝖾(𝑎):𝖥𝗋𝖾𝖾Δ(𝐴),𝑎:𝐴,𝗂𝗆𝗉𝗎𝗋𝖾(𝑜,𝑘):𝖥𝗋𝖾𝖾Δ(𝐴),𝑜:𝖮𝗉Δ,𝑘:𝖱𝖾𝗍Δ(𝑜)→𝖥𝗋𝖾𝖾Δ(𝐴). For a family 𝐺 :𝐒𝐞𝐭 →𝐒𝐞𝐭, a generator 𝑔 :𝐴 →𝐺(𝐴), and an ordinary algebra 𝑎𝑜:(𝖱𝖾𝗍Δ(𝑜)→𝐺(𝐴))→𝐺(𝐴), the fold equations are 𝖿𝗈𝗅𝖽𝑔,𝑎(𝗉𝗎𝗋𝖾(𝑥))=𝑔(𝑥),𝖿𝗈𝗅𝖽𝑔,𝑎(𝗂𝗆𝗉𝗎𝗋𝖾(𝑜,𝑘))=𝑎𝑜(𝖿𝗈𝗅𝖽𝑔,𝑎∘𝑘). The recursive calls in the second line are indexed by responses to 𝑜. There is no other recursive position.
Suppose we try to add a catch operation at result type 𝐴. The most direct first-order arity would be 𝖼𝖺𝗍𝖼𝗁𝐴:(𝖥𝗋𝖾𝖾Δ(𝐴)×𝖥𝗋𝖾𝖾Δ(𝐴))⇝𝐴. This is not an ordinary operation signature of the form (24.1). Its parameter type mentions both the ambient target signature Δ and the result type 𝐴. Worse, even if the pair in (24.1) were admitted as an opaque parameter, the fold in (24.1) would receive the two trees without recursively folded results. The catch clause would then have to invoke the global translator itself.
A second attempt stores source syntax rather than target trees: 𝖼𝖺𝗍𝖼𝗁𝐴:(𝖲𝗒𝗇𝗍𝖺𝗑𝐻(𝐴)×𝖲𝗒𝗇𝗍𝖺𝗑𝐻(𝐴))⇝𝐴. Now the signature mentions the entire source signature 𝐻, and every target algebra which handles catch must know how to translate arbitrary 𝖲𝗒𝗇𝗍𝖺𝗑𝐻-trees. The operation-specific clause is no longer modular. Equations (24.1) and (24.1) expose the same defect from opposite sides: the recursively translated computation arguments are absent from the first-order node interface.
Call an algebra clause local to its interface when its only recursive inputs are the folded response family of (24.1). Such a clause receives neither source syntax nor a recursive translator. Let an ordinary free-tree fold have exactly that interface. Consider a constructor with a designated computation argument 𝑀:𝖲𝗒𝗇𝗍𝖺𝗑𝐻(𝐵). Suppose that 𝑀 is not a response branch 𝑘(𝑟). The fold cannot pass the recursively folded value of 𝑀 to its algebra clause unless either
𝑀 is placed in the response continuation of the constructor, changing its binding and sequencing role; or
the algebra clause is given an additional recursive translator for the whole source signature.
Thus no interface-local clause for (24.1) supports modular elaboration of such a constructor.
Referenced from 2 locations
Proof of Proposition 24.1 — First-order fold obstruction
Proof. The fold’s operation clause receives only 𝑜 and the family 𝖿𝗈𝗅𝖽𝑔,𝑎 ∘𝑘. Every recursive result available to the algebra is therefore the image of some response branch 𝑘(𝑟). If 𝑀 is not one of these branches, its folded value is absent. Putting 𝑀 into 𝑘 makes it a computation selected by an operation response and followed by the surrounding continuation; that is not the role of an independently scoped argument. Otherwise the only way for the clause to obtain a folded value of 𝑀 is to call a translator not supplied by (24.1). Such a translator is indexed by the whole source signature, so the clause ceases to be modular. ◻
★☆☆ Assume (24.1) is nevertheless admitted as an ordinary operation parameter. Write the type of the catch component of a fold algebra 𝖥𝗋𝖾𝖾Δ(𝐴) →𝐺(𝐴). Show that the component receives untranslated source computations and identify the additional argument it would need in order to translate them. Explain why that argument mentions the whole source signature rather than only catch.
Referenced from 3 locations
Higher-order signatures
The missing information is not an untyped list of child trees. Different computation arguments may return different types, and their number and result types may depend on the operation. We therefore index the recursive positions by an ordinary effect signature.
Write 𝖤𝖿𝖿𝖾𝖼𝗍 for the class of small ordinary signatures Δ =(𝖮𝗉Δ,𝖱𝖾𝗍Δ), where 𝖮𝗉Δ :𝐒𝐞𝐭 and 𝖱𝖾𝗍Δ :𝖮𝗉Δ →𝐒𝐞𝐭. Thus 𝖥𝗈𝗋𝗄𝐻(𝑜) :𝖤𝖿𝖿𝖾𝖼𝗍 below is an ordinary signature, not an effectful computation or an additional universe of terms.
A higher-order effect signature is a triple 𝐻=(𝖮𝗉𝖧𝐻,𝖥𝗈𝗋𝗄𝐻,𝖱𝖾𝗍𝖧𝐻) with 𝖮𝗉𝖧𝐻:𝐒𝐞𝐭,𝖥𝗈𝗋𝗄𝐻:𝖮𝗉𝖧𝐻→𝖤𝖿𝖿𝖾𝖼𝗍,𝖱𝖾𝗍𝖧𝐻:𝖮𝗉𝖧𝐻→𝐒𝐞𝐭. For 𝑜 :𝖮𝗉𝖧𝐻, the ordinary signature 𝖥𝗈𝗋𝗄𝐻(𝑜) indexes the computation arguments of 𝑜. A fork index 𝑠:𝖮𝗉𝖥𝗈𝗋𝗄𝐻(𝑜) names one such argument, whose result type is 𝖱𝖾𝗍𝖥𝗈𝗋𝗄𝐻(𝑜)(𝑠). The set 𝖱𝖾𝗍𝖧𝐻(𝑜) is the response type supplied to the ordinary continuation after the higher-order operation has completed.
Referenced from 3 locations
The word fork refers to the family of nested computations; it is not a parallel-evaluation claim. The signature specifies their shape, not an execution order.
Higher-order signatures have a disjoint sum. We write 𝐻1 ⊞𝐻2 for the signature defined by 𝖮𝗉𝖧𝐻1⊞𝐻2=𝖮𝗉𝖧𝐻1+𝖮𝗉𝖧𝐻2,𝖥𝗈𝗋𝗄𝐻1⊞𝐻2(𝗂𝗇𝗅(𝑜))=𝖥𝗈𝗋𝗄𝐻1(𝑜),𝖥𝗈𝗋𝗄𝐻1⊞𝐻2(𝗂𝗇𝗋(𝑜))=𝖥𝗈𝗋𝗄𝐻2(𝑜),𝖱𝖾𝗍𝖧𝐻1⊞𝐻2(𝗂𝗇𝗅(𝑜))=𝖱𝖾𝗍𝖧𝐻1(𝑜),𝖱𝖾𝗍𝖧𝐻1⊞𝐻2(𝗂𝗇𝗋(𝑜))=𝖱𝖾𝗍𝖧𝐻2(𝑜). The symbol ⊞ is used only for this higher-order signature sum. For ordinary signatures Δ𝑖 =(𝖮𝗉Δ𝑖,𝖱𝖾𝗍Δ𝑖), define their disjoint sum Δ1 ⊕𝗌𝗂𝗀Δ2 by 𝖮𝗉Δ1⊕𝗌𝗂𝗀Δ2=𝖮𝗉Δ1+𝖮𝗉Δ2,𝖱𝖾𝗍Δ1⊕𝗌𝗂𝗀Δ2(𝗂𝗇𝗅𝑜)=𝖱𝖾𝗍Δ1(𝑜),𝖱𝖾𝗍Δ1⊕𝗌𝗂𝗀Δ2(𝗂𝗇𝗋𝑜)=𝖱𝖾𝗍Δ2(𝑜). Thus ⊕𝗌𝗂𝗀 composes first-order target operations, whereas ⊞ also composes their fork signatures. Unparenthesized chains of either signature-sum symbol associate to the right; this parsing convention does not make the two nestings definitionally equal.
For a higher-order signature 𝐻 and a set 𝐴, the set of hefty trees 𝖧𝖾𝖿𝗍𝗒𝐻(𝐴) is generated by 𝗉𝗎𝗋𝖾(𝑎):𝖧𝖾𝖿𝗍𝗒𝐻(𝐴),𝑎:𝐴,𝗂𝗆𝗉𝗎𝗋𝖾(𝑜,𝜓,𝑘):𝖧𝖾𝖿𝗍𝗒𝐻(𝐴), where the second constructor has data 𝑜:𝖮𝗉𝖧𝐻,𝜓:∏𝑠:𝖮𝗉𝖥𝗈𝗋𝗄𝐻(𝑜)𝖧𝖾𝖿𝗍𝗒𝐻(𝖱𝖾𝗍𝖥𝗈𝗋𝗄𝐻(𝑜)(𝑠)),𝑘:𝖱𝖾𝗍𝖧𝐻(𝑜)→𝖧𝖾𝖿𝗍𝗒𝐻(𝐴). We call 𝜓 the fork, the family of computation arguments, and 𝑘 the continuation that receives the operation result.
The word “generated” denotes the least indexed family closed under these two constructors. Equivalently, it is the W-type of the displayed set-indexed polynomial signature; the ambient set theory is assumed to admit these W-types. Its induction principle is simultaneous in the result index. For predicates P𝐴(𝑀) at every set 𝐴, it is enough to prove pure:P𝐴(𝗉𝗎𝗋𝖾(𝑎));impure:(∀𝑠.P𝖱𝖾𝗍𝖥𝗈𝗋𝗄𝐻(𝑜)(𝑠)(𝜓(𝑠)))∧(∀𝑟.P𝐴(𝑘(𝑟)))⟹P𝐴(𝗂𝗆𝗉𝗎𝗋𝖾(𝑜,𝜓,𝑘)). Unlike the nested scoped syntax of definition 23.2, this is a direct indexed polynomial W-type; it needs no diagonal transfinite-chain hypothesis.
Referenced from 4 locations
The two function fields in (24.3) play different roles. The family 𝜓 contains computations owned by the operation. The function 𝑘 contains the computation which follows the operation’s result. This distinction will control bind.
Catch as a typed fork
To make operations polymorphic in their result type, fix a small universe of codes 𝖳𝗒 with interpretation 𝖵𝖺𝗅 :𝖳𝗒 →𝐒𝐞𝐭. No closure property of this universe is used below; it may contain only the codes required by the examples.
The signature 𝖢𝖺𝗍𝖼𝗁 has one operation 𝖼𝖺𝗍𝖼𝗁(𝑡) for each 𝑡 :𝖳𝗒, with 𝖥𝗈𝗋𝗄𝖢𝖺𝗍𝖼𝗁(𝖼𝖺𝗍𝖼𝗁(𝑡))=(𝟐,𝜆𝑏.𝖵𝖺𝗅(𝑡)),𝖱𝖾𝗍𝖧𝖢𝖺𝗍𝖼𝗁(𝖼𝖺𝗍𝖼𝗁(𝑡))=𝖵𝖺𝗅(𝑡). Thus the fork has two indices, 𝗍𝗍 and 𝖿𝖿, and both indexed computations return a value of type 𝖵𝖺𝗅(𝑡).
Referenced from 2 locations
First take the head-summand row 𝐻 =𝖢𝖺𝗍𝖼𝗁 ⊞𝐻′. For 𝑀1,𝑀2 :𝖧𝖾𝖿𝗍𝗒𝐻(𝖵𝖺𝗅(𝑡)), define 𝖼𝖺𝗍𝖼𝗁(𝑀1,𝑀2):=𝗂𝗆𝗉𝗎𝗋𝖾(𝜄ℓ(𝖼𝖺𝗍𝖼𝗁(𝑡)),𝜆𝑏.𝗂𝖿 𝑏 𝗍𝗁𝖾𝗇 𝑀1 𝖾𝗅𝗌𝖾 𝑀2,𝗉𝗎𝗋𝖾). The fork stores the protected and fallback computations. The continuation is 𝗉𝗎𝗋𝖾 because the operation itself returns whichever branch value is selected. A surrounding bind will replace this continuation without altering either branch.
Let 𝑡 code ℕ, let 𝑀1,𝑀2 :𝖧𝖾𝖿𝗍𝗒𝐻(ℕ), and let the whole surrounding computation return 𝐵. In a node 𝗂𝗆𝗉𝗎𝗋𝖾(𝖼𝖺𝗍𝖼𝗁(𝑡),𝜓,𝑘) :𝖧𝖾𝖿𝗍𝗒𝐻(𝐵), one has 𝜓(𝗍𝗍),𝜓(𝖿𝖿):𝖧𝖾𝖿𝗍𝗒𝐻(ℕ),𝑘:ℕ→𝖧𝖾𝖿𝗍𝗒𝐻(𝐵). The branch type is fixed by the code carried by the operation; the final type 𝐵 is fixed by the surrounding continuation. Conflating these indices would reject ordinary sequencing after catch.
Referenced from 2 locations
The container shape (24.3) is deliberately specific. It represents a family of strictly positive recursive positions indexed by an ordinary signature. A semantically describable higher-order operator whose syntax places the recursive family negatively is not admitted merely because one can write a set-theoretic endofunctor for it.
★☆☆ Define a higher-order signature 𝖥𝗂𝗋𝗌𝗍 with one operation 𝖿𝗂𝗋𝗌𝗍(𝑡) carrying three computations of type 𝖵𝖺𝗅(𝑡) and returning a value of type 𝖵𝖺𝗅(𝑡). Give its fork signature and write the analogue of (24.2.1) for 𝖿𝗂𝗋𝗌𝗍(𝑀0,𝑀1,𝑀2).
Referenced from 4 locations
Sequencing is not structural recursion
For an ordinary free tree, bind can be defined by the fold because every recursive child is a continuation branch. In a hefty tree, the fork contains computations which lie inside the operation’s scope. Sequencing after the operation must not be pushed into them.
The tempting equation is a full structural recursion: 𝗉𝗎𝗋𝖾(𝑥)𝖻𝗂𝗇𝖽×𝑔=𝑔(𝑥),𝗂𝗆𝗉𝗎𝗋𝖾(𝑜,𝜓,𝑘)𝖻𝗂𝗇𝖽×𝑔=𝗂𝗆𝗉𝗎𝗋𝖾(𝑜,𝜆𝑠.𝜓(𝑠)𝖻𝗂𝗇𝖽×𝑔,𝜆𝑥.𝑘(𝑥)𝖻𝗂𝗇𝖽×𝑔). For a general hefty node, the second line is not even well typed. If the whole node has result type 𝐴, then 𝑔 has domain 𝐴, whereas 𝜓(𝑠) returns 𝖱𝖾𝗍𝖥𝗈𝗋𝗄𝐻(𝑜)(𝑠), which need not be 𝐴. Equation (24.3) is typeable only in a homogeneous special case where every fork result in the node is 𝐴. The catch smart constructor (24.2.1) is such a case. There the equation typechecks and is still semantically wrong: it pushes 𝑔 into both branches and also places 𝑔 in the operation continuation: 𝖼𝖺𝗍𝖼𝗁(𝑀1,𝑀2)𝖻𝗂𝗇𝖽×𝑔=𝗂𝗆𝗉𝗎𝗋𝖾(𝖼𝖺𝗍𝖼𝗁(𝑡),𝜆𝑏.(𝗂𝖿 𝑏 𝗍𝗁𝖾𝗇 𝑀1 𝖾𝗅𝗌𝖾 𝑀2)𝖻𝗂𝗇𝖽×𝑔,𝑔). Any elaborator which runs one branch and then invokes the continuation will therefore run 𝑔 twice.
The hefty bind traverses only an operation’s continuation. For 𝑀:𝖧𝖾𝖿𝗍𝗒𝐻(𝐴),𝑔:𝐴→𝖧𝖾𝖿𝗍𝗒𝐻(𝐵), define 𝗉𝗎𝗋𝖾(𝑥)≫=𝖧𝑔=𝑔(𝑥),𝗂𝗆𝗉𝗎𝗋𝖾(𝑜,𝜓,𝑘)≫=𝖧𝑔=𝗂𝗆𝗉𝗎𝗋𝖾(𝑜,𝜓,𝜆𝑥.𝑘(𝑥)≫=𝖧𝑔). Only the continuation is traversed. Put 𝑀 ≫𝖧𝑁:=𝑀≫=𝖧(𝜆_.𝑁).
Referenced from 6 locations
For catch, equation (24.6) gives 𝖼𝖺𝗍𝖼𝗁(𝑀1,𝑀2)≫=𝖧𝑔=𝗂𝗆𝗉𝗎𝗋𝖾(𝖼𝖺𝗍𝖼𝗁(𝑡),𝜆𝑏.𝗂𝖿 𝑏 𝗍𝗁𝖾𝗇 𝑀1 𝖾𝗅𝗌𝖾 𝑀2,𝑔). The branch selected as successful produces a value for 𝑔 exactly once.
Assume a lifted first-order operation 𝗍𝗂𝖼𝗄 :𝟏 ⇝𝟏. Let 𝐶=𝖼𝖺𝗍𝖼𝗁(𝗉𝗎𝗋𝖾(⋆),𝗉𝗎𝗋𝖾(⋆)),𝑔=𝜆_.𝗍𝗂𝖼𝗄. Under the correct bind, the selected pure branch returns to the continuation 𝑔, so one tick is produced. Under (24.3), the selected branch has already become 𝗍𝗂𝖼𝗄, and the node continuation is also 𝑔; a catch elaborator which invokes the continuation after the selected branch therefore produces 𝗍𝗂𝖼𝗄≫𝖧𝗍𝗂𝖼𝗄. The defect is semantic, not a type error. Both trees have result type 𝟏.
Referenced from 5 locations
Let 𝐻 be a higher-order signature and let 𝐴,𝐵,𝐶 be sets. Fix 𝑥:𝐴,𝑀:𝖧𝖾𝖿𝗍𝗒𝐻(𝐴),𝑓:𝐴→𝖧𝖾𝖿𝗍𝗒𝐻(𝐵),𝑔:𝐵→𝖧𝖾𝖿𝗍𝗒𝐻(𝐶). Taking equality of operation nodes to compare their function fields extensionally, hefty bind satisfies 𝗉𝗎𝗋𝖾(𝑥)≫=𝖧𝑓=𝑓(𝑥),𝑀≫=𝖧𝗉𝗎𝗋𝖾=𝑀,(𝑀≫=𝖧𝑓)≫=𝖧𝑔=𝑀≫=𝖧(𝜆𝑥.𝑓(𝑥)≫=𝖧𝑔).
Referenced from 4 locations
Proof of Theorem 24.8 — Monad equations for hefty bind
Proof. The first equation is definitional. For the right unit, induct on 𝑀. The pure case is immediate. In the operation case, 𝗂𝗆𝗉𝗎𝗋𝖾(𝑜,𝜓,𝑘)≫=𝖧𝗉𝗎𝗋𝖾(24.6)=𝗂𝗆𝗉𝗎𝗋𝖾(𝑜,𝜓,𝜆𝑥.𝑘(𝑥)≫=𝖧𝗉𝗎𝗋𝖾)𝐼𝐻=𝗂𝗆𝗉𝗎𝗋𝖾(𝑜,𝜓,𝑘). The fork is unchanged, so no induction hypothesis is required there.
For associativity, the pure case is definitional. In the operation case, expand the left side twice: (𝗂𝗆𝗉𝗎𝗋𝖾(𝑜,𝜓,𝑘)≫=𝖧𝑓)≫=𝖧𝑔(24.6)=𝗂𝗆𝗉𝗎𝗋𝖾(𝑜,𝜓,𝜆𝑥.(𝑘(𝑥)≫=𝖧𝑓)≫=𝖧𝑔)𝐼𝐻=𝗂𝗆𝗉𝗎𝗋𝖾(𝑜,𝜓,𝜆𝑥.𝑘(𝑥)≫=𝖧(𝜆𝑦.𝑓(𝑦)≫=𝖧𝑔))(24.6)=𝗂𝗆𝗉𝗎𝗋𝖾(𝑜,𝜓,𝑘)≫=𝖧(𝜆𝑦.𝑓(𝑦)≫=𝖧𝑔). Pointwise equality of the continuation fields gives constructor equality. ◻
★☆☆ Use the program in example 24.7, followed by a lifted operation 𝗋𝖾𝖺𝖽𝖳𝗂𝖼𝗄𝗌 :𝟏 ⇝ℕ. Assume the target handler starts at zero, increments on 𝗍𝗂𝖼𝗄, and returns the current count on 𝗋𝖾𝖺𝖽𝖳𝗂𝖼𝗄𝗌. Calculate the result obtained with (24.6) and with (24.3).
Referenced from 3 locations
★★☆ Define 𝖿𝗆𝖺𝗉𝖧(𝑓,𝑀):=𝑀≫=𝖧(𝗉𝗎𝗋𝖾 ∘𝑓). Derive its two constructor equations and prove 𝖿𝗆𝖺𝗉𝖧(𝗂𝖽,𝑀)=𝑀,𝖿𝗆𝖺𝗉𝖧(𝑔∘𝑓,𝑀)=𝖿𝗆𝖺𝗉𝖧(𝑔,𝖿𝗆𝖺𝗉𝖧(𝑓,𝑀)).
Referenced from 3 locations
The structural catamorphism
Elaboration has the opposite recursive requirement. It must translate every nested computation before the operation-specific clause is invoked. The algebra therefore receives already-translated forks and continuations.
Let 𝐺 :𝐒𝐞𝐭 →𝐒𝐞𝐭. A hefty algebra 𝛼 :𝖠𝗅𝗀𝖧(𝐻,𝐺) consists, for every result set 𝐴, of an operation 𝛼𝐴(𝑜,−,−):⎛⎜
⎜
⎜
⎜
⎜⎝∏𝑠:𝖮𝗉𝖥𝗈𝗋𝗄𝐻(𝑜)𝐺(𝖱𝖾𝗍𝖥𝗈𝗋𝗄𝐻(𝑜)(𝑠))⎞⎟
⎟
⎟
⎟
⎟⎠→(𝖱𝖾𝗍𝖧𝐻(𝑜)→𝐺(𝐴))→𝐺(𝐴) for each 𝑜 :𝖮𝗉𝖧𝐻.
Referenced from 2 locations
Given a polymorphic family 𝑔𝑋 :𝑋 →𝐺(𝑋) for every set 𝑋, and 𝛼 :𝖠𝗅𝗀𝖧(𝐻,𝐺), the hefty catamorphism is the family 𝖼𝖺𝗍𝖺𝖧𝑔,𝛼,𝑋 :𝖧𝖾𝖿𝗍𝗒𝐻(𝑋) →𝐺(𝑋). In its defining equations, write 𝐶𝑋:=𝖼𝖺𝗍𝖺𝖧𝑔,𝛼,𝑋,𝑅𝑜(𝑠):=𝖱𝖾𝗍𝖥𝗈𝗋𝗄𝐻(𝑜)(𝑠). Then 𝐶𝑋(𝗉𝗎𝗋𝖾(𝑥))=𝑔𝑋(𝑥),𝐶𝑋(𝗂𝗆𝗉𝗎𝗋𝖾(𝑜,𝜓,𝑘))=𝛼𝑋(𝑜,𝜆𝑠.𝐶𝑅𝑜(𝑠)(𝜓(𝑠)),𝜆𝑥.𝐶𝑋(𝑘(𝑥))). We omit the final index when the domain determines it.
Referenced from 4 locations
Equation (24.10) traverses both recursive fields. A bind with 𝑔 :𝐴 →𝖧𝖾𝖿𝗍𝗒𝐻(𝐵) cannot supply the polymorphic generator required at arbitrary fork-result types. Even in the homogeneous catch fragment, where those types coincide with 𝐴, structural recursion would transform the forks and recreate the semantic defect of (24.3).
Let (𝑓𝑋)𝑋 be a family 𝑓𝑋 :𝖧𝖾𝖿𝗍𝗒𝐻(𝑋) →𝐺(𝑋) satisfying the two equations in (24.10) at every set 𝑋, for fixed 𝑔 and 𝛼. Then, for every 𝑋, 𝑓𝑋=𝖼𝖺𝗍𝖺𝖧𝑔,𝛼,𝑋 extensionally.
Referenced from 3 locations
Proof of Lemma 24.11 — Unique structural solution
Proof. Apply the displayed induction principle of definition 24.3 to the simultaneously quantified predicate P𝑋(𝑀)⟺𝑓𝑋(𝑀)=𝖼𝖺𝗍𝖺𝖧𝑔,𝛼,𝑋(𝑀). The pure case is the common generator equation. In the operation case, the hypotheses give 𝑓𝑌𝑠(𝜓(𝑠)) =𝖼𝖺𝗍𝖺𝑌𝑠(𝜓(𝑠)) for each fork and 𝑓𝑋(𝑘(𝑥)) =𝖼𝖺𝗍𝖺𝑋(𝑘(𝑥)) for each response. Function extensionality equates both function fields, and the common algebra equation equates the results. ◻
★★☆ Restrict to the catch-only homogeneous fragment at one fixed branch type 𝐴. Suppose there were a hefty algebra 𝛽𝑔 such that 𝑀≫=𝖧𝑔=𝖼𝖺𝗍𝖺𝖧𝑔,𝛽𝑔(𝑀) for every 𝐴-result computation 𝑀, with the generator extended arbitrarily at unused result indices. Choose distinct values 𝑥,𝑦 :𝐴 and a constant function 𝑔 :𝐴 →𝖧𝖾𝖿𝗍𝗒𝐻(𝐵). Apply the operation equation to 𝖼𝖺𝗍𝖼𝗁(𝗉𝗎𝗋𝖾(𝑥),𝗉𝗎𝗋𝖾(𝑥)) and 𝖼𝖺𝗍𝖼𝗁(𝗉𝗎𝗋𝖾(𝑦),𝗉𝗎𝗋𝖾(𝑦)). Show that the catamorphism presents identical recursively folded fields to 𝛽𝑔, whereas the correct binds in (24.3) retain different forks. Conclude that bind is not definable by the structural catamorphism interface.
Referenced from 3 locations
Elaboration into first-order trees
The target of elaboration is an ordinary free tree. The source operation clause may arrange, handle, duplicate, or discard the translated computation arguments, but it cannot receive an ill-typed one.
For a higher-order source signature 𝐻 and an ordinary target signature Δ, an elaboration is a hefty algebra 𝐸:𝖤𝗅𝖺𝖻𝗈𝗋𝖺𝗍𝗂𝗈𝗇(𝐻,Δ):=𝖠𝗅𝗀𝖧(𝐻,𝜆𝐴.𝖥𝗋𝖾𝖾Δ(𝐴)). Its induced translation is 𝖾𝗅𝖺𝖻𝗈𝗋𝖺𝗍𝖾𝐸:=𝖼𝖺𝗍𝖺𝖧𝗉𝗎𝗋𝖾,𝐸:𝖧𝖾𝖿𝗍𝗒𝐻(𝐴)→𝖥𝗋𝖾𝖾Δ(𝐴).
Referenced from 3 locations
Unfolding the definition gives 𝖾𝗅𝖺𝖻𝗈𝗋𝖺𝗍𝖾𝐸(𝗉𝗎𝗋𝖾(𝑥))=𝗉𝗎𝗋𝖾(𝑥),𝖾𝗅𝖺𝖻𝗈𝗋𝖺𝗍𝖾𝐸(𝗂𝗆𝗉𝗎𝗋𝖾(𝑜,𝜓,𝑘))=𝐸𝐴(𝑜,𝖾𝗅𝖺𝖻𝗈𝗋𝖺𝗍𝖾𝐸∘𝜓,𝖾𝗅𝖺𝖻𝗈𝗋𝖺𝗍𝖾𝐸∘𝑘). These are the generic elaboration equations. An operation-specific clause is written against translated subcomputations, not raw source syntax.
Assume 𝐸 has the algebra type (24.12). For every set 𝐴, 𝑀:𝖧𝖾𝖿𝗍𝗒𝐻(𝐴)⟹𝖾𝗅𝖺𝖻𝗈𝗋𝖺𝗍𝖾𝐸(𝑀):𝖥𝗋𝖾𝖾Δ(𝐴). More specifically, in an operation node the recursive calls supplied to the clause have exactly the types ̂𝜓(𝑠):𝖥𝗋𝖾𝖾Δ(𝖱𝖾𝗍𝖥𝗈𝗋𝗄𝐻(𝑜)(𝑠)),̂𝑘(𝑥):𝖥𝗋𝖾𝖾Δ(𝐴).
Referenced from 6 locations
Proof of Theorem 24.13 — Typing by construction
Proof. Induct on 𝑀. A pure node is translated by the target constructor 𝗉𝗎𝗋𝖾 :𝐴 →𝖥𝗋𝖾𝖾Δ(𝐴). For an operation node, the induction hypotheses give (24.13) pointwise for the fork and continuation. The component 𝐸𝐴(𝑜, −, −) has exactly these two families as its premises and returns 𝖥𝗋𝖾𝖾Δ(𝐴). This establishes (24.13). ◻
The theorem says neither that an untyped source parser produces a hefty tree nor that elaboration simulates a source operational semantics. It says that once source syntax is represented by the indexed family 𝖧𝖾𝖿𝗍𝗒𝐻, every well-typed algebra clause produces a target tree with the same result index.
Lifting ordinary operations
Let 𝖭𝗂𝗅 be the empty ordinary signature. An ordinary signature Δ0 embeds into a higher-order signature 𝖫𝗂𝖿𝗍(Δ0) by 𝖮𝗉𝖧𝖫𝗂𝖿𝗍(Δ0)=𝖮𝗉Δ0,𝖥𝗈𝗋𝗄𝖫𝗂𝖿𝗍(Δ0)(𝑜)=𝖭𝗂𝗅,𝖱𝖾𝗍𝖧𝖫𝗂𝖿𝗍(Δ0)(𝑜)=𝖱𝖾𝗍Δ0(𝑜). A lifted operation has no nested computation arguments.
Use proof-relevant row insertion witnesses. For ordinary signatures, 𝖨𝗇𝗌(Δ;Δ0,Δ′) is generated by 𝑋𝖨𝗇𝗌(Δ0⊕𝗌𝗂𝗀Δ′;Δ0,Δ′)𝖨𝗇𝗌(Δ;Δ0,Δ′)𝖨𝗇𝗌(Δ1⊕𝗌𝗂𝗀Δ;Δ0,Δ1⊕𝗌𝗂𝗀Δ′). A witness 𝑤 determines injections 𝜄ℓ𝑤:𝖮𝗉Δ0→𝖮𝗉Δ,𝜄𝑟𝑤:𝖮𝗉Δ′→𝖮𝗉Δ, together with the response-type transports induced by those injections. For higher-order signature rows, 𝖧𝖨𝗇𝗌(𝐻;𝐻0,𝐻′) is generated by the corresponding rules 𝑋𝖧𝖨𝗇𝗌(𝐻0⊞𝐻′;𝐻0,𝐻′)𝖧𝖨𝗇𝗌(𝐻;𝐻0,𝐻′)𝖧𝖨𝗇𝗌(𝐻1⊞𝐻;𝐻0,𝐻1⊞𝐻′). A witness 𝑤𝐻 determines the operation injection 𝜄𝐻𝑤𝐻 :𝖮𝗉𝖧𝐻0 →𝖮𝗉𝖧𝐻, together with the induced fork and response transports used by the smart constructors above.
Referenced from 3 locations
The witness of definition 24.14 extends a head injection. That injection appears in (24.2.1). The witness selects one occurrence of 𝖢𝖺𝗍𝖼𝗁 and supplies the required fork and response transports.
For example, the throw summand in the transaction row has the concrete witness 𝑋𝖨𝗇𝗌(𝖳𝗁𝗋𝗈𝗐⊕𝗌𝗂𝗀𝖭𝗂𝗅;𝖳𝗁𝗋𝗈𝗐,𝖭𝗂𝗅)𝖨𝗇𝗌(𝖲𝗍𝖺𝗍𝖾⊕𝗌𝗂𝗀(𝖳𝗁𝗋𝗈𝗐⊕𝗌𝗂𝗀𝖭𝗂𝗅);𝖳𝗁𝗋𝗈𝗐,𝖲𝗍𝖺𝗍𝖾⊕𝗌𝗂𝗀𝖭𝗂𝗅). The outer rule skips 𝖲𝗍𝖺𝗍𝖾; the inner base rule selects 𝖳𝗁𝗋𝗈𝗐. The catch summand of the higher-order transaction row requires two skips. Put 𝐻2:=𝖢𝖺𝗍𝖼𝗁⊞𝖫𝗂𝖿𝗍(𝖭𝗂𝗅),𝐻1:=𝖫𝗂𝖿𝗍(𝖳𝗁𝗋𝗈𝗐)⊞𝐻2. Its exact witness is 𝑋𝖧𝖨𝗇𝗌(𝐻2;𝖢𝖺𝗍𝖼𝗁,𝖫𝗂𝖿𝗍(𝖭𝗂𝗅))𝖧𝖨𝗇𝗌(𝐻1;𝖢𝖺𝗍𝖼𝗁,𝖫𝗂𝖿𝗍(𝖳𝗁𝗋𝗈𝗐)⊞𝖫𝗂𝖿𝗍(𝖭𝗂𝗅))𝖧𝖨𝗇𝗌(𝖫𝗂𝖿𝗍(𝖲𝗍𝖺𝗍𝖾)⊞𝐻1;𝖢𝖺𝗍𝖼𝗁,𝖫𝗂𝖿𝗍(𝖲𝗍𝖺𝗍𝖾)⊞𝖫𝗂𝖿𝗍(𝖳𝗁𝗋𝗈𝗐)⊞𝖫𝗂𝖿𝗍(𝖭𝗂𝗅)). The two outer rules skip the lifted state and throw summands; the base rule selects 𝖢𝖺𝗍𝖼𝗁. This is the witness suppressed later in (24.8).
Insertion witnesses are proof relevant. Consequently the implementation infers a witness from its indices only for a duplicate-free ordered row of atomic summands. On such rows, induction on the row proves that 𝖨𝗇𝗌 and 𝖧𝖨𝗇𝗌 have at most one derivation at fixed indices. If a row repeats a summand, the witness must remain explicit: different witnesses can select different occurrences, and this chapter claims no coherence theorem identifying their elaborations.
Regard a right-associated signature row as an ordered list of atomic summands, and call it duplicate free when no summand identity occurs twice. If Δ is duplicate free and Δ0 is atomic, there is at most one pair consisting of a residual row Δ′ and a derivation of 𝖨𝗇𝗌(Δ;Δ0,Δ′). If 𝐻 is duplicate free and 𝐻0 is atomic, there is at most one pair consisting of a residual row 𝐻′ and a derivation of 𝖧𝖨𝗇𝗌(𝐻;𝐻0,𝐻′).
Referenced from 4 locations
Proof of Proposition 24.15 — Canonical insertion on duplicate-free rows
Proof. Induct on the ordered row. The empty row has no derivation. If its head is the requested atomic summand, the base rule yields the tail as residual. A step derivation would require a second occurrence in the tail, which duplicate freeness excludes. If the head differs, the base rule is impossible and every derivation uses the step rule; the induction hypothesis uniquely determines both the tail residual and the tail derivation, hence also the residual with the skipped head restored. For 𝖧𝖨𝗇𝗌, induct on the ordered row 𝐻. The empty row has no derivation. If the head is 𝐻0, the base constructor determines the residual 𝐻′ as the tail; an HIns-Step derivation would require another 𝐻0 in that tail and contradict duplicate freeness. If the head differs from 𝐻0, HIns-Base cannot apply, so both candidate derivations end in HIns-Step. Their premises have the form 𝖧𝖨𝗇𝗌(𝐻tail;𝐻0,𝐻′tail); the induction hypothesis identifies both the tail residual and its derivation, after which the common skipped head identifies 𝐻′ and the two outer derivations. ◻
For 𝑤 :𝖨𝗇𝗌(Δ;Δ0,Δ′), the ordinary-operation elaborator is 𝐸𝗅𝗂𝖿𝗍,𝑤(𝑜,𝜓,𝑘)=𝗂𝗆𝗉𝗎𝗋𝖾(𝜄ℓ𝑤(𝑜),𝑘∘𝑞ℓ𝑤,𝑜), where 𝑞ℓ𝑤,𝑜 :𝖱𝖾𝗍Δ(𝜄ℓ𝑤(𝑜)) →𝖱𝖾𝗍Δ0(𝑜) is the response transport. The fork 𝜓 has empty domain by (24.5.1). Equation (24.5.1) simply reproduces the ordinary operation in the target row. The empty lifted signature has the unique algebra 𝐸𝗇𝗂𝗅, because there is no operation case to define.
★☆☆ Let 𝑜 :𝖮𝗉Δ0 and let ↑𝑜 be the hefty smart constructor whose fork is empty and whose continuation is 𝗉𝗎𝗋𝖾. Use (24.5) and (24.5.1) to calculate 𝖾𝗅𝖺𝖻𝗈𝗋𝖺𝗍𝖾𝐸𝗅𝗂𝖿𝗍,𝑤( ↑𝑜). State the response type of its target continuation, including the transport 𝑞ℓ𝑤,𝑜.
Referenced from 3 locations
A modular catch elaborator
The target signature contains an ordinary exception operation 𝗍𝗁𝗋𝗈𝗐:𝟏⇝𝟎.
Write 𝖳𝗁𝗋𝗈𝗐 for this signature. If 𝑤 :𝖨𝗇𝗌(Δ;𝖳𝗁𝗋𝗈𝗐,Δ′), the ordinary throw handler has type 𝗋𝗎𝗇𝖳𝗁𝗋𝗈𝗐𝑤:𝖥𝗋𝖾𝖾Δ(𝐴)→𝖥𝗋𝖾𝖾Δ′(𝖬𝖺𝗒𝖻𝖾(𝐴)). It maps a pure value to 𝗌𝗈𝗆𝖾(𝑥), maps throw to 𝗇𝗈𝗇𝖾, and forwards every residual operation recursively. Its complete equations use the two-field target constructor 𝗂𝗆𝗉𝗎𝗋𝖾(𝑜,𝑘):𝖥𝗋𝖾𝖾Δ(𝐴). The three-field 𝗂𝗆𝗉𝗎𝗋𝖾(𝑜,𝜓,𝑘) used earlier is the hefty constructor. Here 𝗋𝖾𝗌(𝑜,𝑘) abbreviates 𝗂𝗆𝗉𝗎𝗋𝖾(𝜄𝑟𝑤(𝑜),𝑘), with the response transport induced by the injection. The equations are 𝗋𝗎𝗇𝖳𝗁𝗋𝗈𝗐𝑤(𝗉𝗎𝗋𝖾(𝑥))=𝗉𝗎𝗋𝖾(𝗌𝗈𝗆𝖾(𝑥)),𝗋𝗎𝗇𝖳𝗁𝗋𝗈𝗐𝑤(𝗂𝗆𝗉𝗎𝗋𝖾(𝜄ℓ𝑤(𝗍𝗁𝗋𝗈𝗐),𝑘0))=𝗉𝗎𝗋𝖾(𝗇𝗈𝗇𝖾),𝗋𝗎𝗇𝖳𝗁𝗋𝗈𝗐𝑤(𝗋𝖾𝗌(𝑜,𝑘))=𝗂𝗆𝗉𝗎𝗋𝖾(𝑜,𝜆𝑟.𝗋𝗎𝗇𝖳𝗁𝗋𝗈𝗐𝑤(𝑘(𝑟))). Here 𝑘0 :0 →𝖥𝗋𝖾𝖾Δ(𝐴) is the unique empty-domain continuation. The smart notation 𝗍𝗁𝗋𝗈𝗐 suppresses exactly this vacuous field. The same insertion witness gives effect masking 𝗆𝖺𝗌𝗄𝑤:𝖥𝗋𝖾𝖾Δ′(𝐴)→𝖥𝗋𝖾𝖾Δ(𝐴), which reinjects every residual operation into the larger row. Its equations are 𝗆𝖺𝗌𝗄𝑤(𝗉𝗎𝗋𝖾(𝑥))=𝗉𝗎𝗋𝖾(𝑥),𝗆𝖺𝗌𝗄𝑤(𝗂𝗆𝗉𝗎𝗋𝖾(𝑜,𝑘))=𝗋𝖾𝗌(𝑜,𝜆𝑟.𝗆𝖺𝗌𝗄𝑤(𝑘(𝑟))). In particular, masking does not add an operation node to a pure tree.
Referenced from 2 locations
For target free trees, ordinary free-monad bind is the structural recursion 𝗉𝗎𝗋𝖾(𝑥)≫=𝑓=𝑓(𝑥),𝗂𝗆𝗉𝗎𝗋𝖾(𝑜,𝑘)≫=𝑓=𝗂𝗆𝗉𝗎𝗋𝖾(𝑜,𝜆𝑟.𝑘(𝑟)≫=𝑓). The three bind equations are 𝗉𝗎𝗋𝖾(𝑥)≫=𝑓=𝑓(𝑥)𝐹𝑟𝑒𝑒−𝑢𝑛𝑖𝑡ℓ,𝑡≫=𝗉𝗎𝗋𝖾=𝑡𝐹𝑟𝑒𝑒−𝑢𝑛𝑖𝑡𝑟,(𝑡≫=𝑓)≫=𝑔=𝑡≫=(𝜆𝑥.𝑓(𝑥)≫=𝑔)𝐹𝑟𝑒𝑒−𝑎𝑠𝑠𝑜𝑐. For target trees, write 𝑚≫𝑛:=𝑚≫=(𝜆_.𝑛). This target abbreviation is distinct from the source sequencing 𝑀 ≫𝖧𝑁 of definition 24.6. The first equation is the pure clause of (24.3). Induction on 𝑡 proves the second and third equations; the impure case applies the induction hypothesis to every response branch.
Referenced from 3 locations
We also use 𝗆𝖺𝗒𝖻𝖾(𝑓,𝑁)(𝗌𝗈𝗆𝖾(𝑥))=𝑓(𝑥),𝗆𝖺𝗒𝖻𝖾(𝑓,𝑁)(𝗇𝗈𝗇𝖾)=𝑁.
The tempting clause 𝗋𝗎𝗇𝖳𝗁𝗋𝗈𝗐𝑤(̂𝜓(𝗍𝗍))≫=𝗆𝖺𝗒𝖻𝖾(̂𝑘,̂𝜓(𝖿𝖿)≫=̂𝑘) is ill typed: the handled protected branch lies in 𝖥𝗋𝖾𝖾Δ′, whereas both continuations return trees in 𝖥𝗋𝖾𝖾Δ. The missing map is precisely 𝗆𝖺𝗌𝗄𝑤 :𝖥𝗋𝖾𝖾Δ′ →𝖥𝗋𝖾𝖾Δ.
For 𝑤 :𝖨𝗇𝗌(Δ;𝖳𝗁𝗋𝗈𝗐,Δ′), define 𝐸𝖼𝖺𝗍𝖼𝗁,𝑤 :𝖤𝗅𝖺𝖻𝗈𝗋𝖺𝗍𝗂𝗈𝗇(𝖢𝖺𝗍𝖼𝗁,Δ) by 𝐸𝖼𝖺𝗍𝖼𝗁,𝑤,𝐴(𝖼𝖺𝗍𝖼𝗁(𝑡),̂𝜓,̂𝑘):=𝗆𝖺𝗌𝗄𝑤(𝗋𝗎𝗇𝖳𝗁𝗋𝗈𝗐𝑤(̂𝜓(𝗍𝗍)))≫=𝗆𝖺𝗒𝖻𝖾(̂𝑘,̂𝜓(𝖿𝖿)≫=̂𝑘).
Referenced from 2 locations
The first branch is interpreted as protected computation. Its throw effect is handled locally, producing a possible result in the residual target row; mask then places that residual tree back in the full target row. On 𝗌𝗈𝗆𝖾(𝑥), the operation continuation receives 𝑥. On 𝗇𝗈𝗇𝖾, the fallback runs and its result is supplied to the same continuation.
For the data supplied to the component in (24.18), every subexpression has the following type: ̂𝜓(𝗍𝗍),̂𝜓(𝖿𝖿):𝖥𝗋𝖾𝖾Δ(𝖵𝖺𝗅(𝑡)),𝗋𝗎𝗇𝖳𝗁𝗋𝗈𝗐𝑤(̂𝜓(𝗍𝗍)):𝖥𝗋𝖾𝖾Δ′(𝖬𝖺𝗒𝖻𝖾(𝖵𝖺𝗅(𝑡))),𝗆𝖺𝗌𝗄𝑤(𝗋𝗎𝗇𝖳𝗁𝗋𝗈𝗐𝑤(̂𝜓(𝗍𝗍))):𝖥𝗋𝖾𝖾Δ(𝖬𝖺𝗒𝖻𝖾(𝖵𝖺𝗅(𝑡))),̂𝑘:𝖵𝖺𝗅(𝑡)→𝖥𝗋𝖾𝖾Δ(𝐴),̂𝜓(𝖿𝖿)≫=̂𝑘:𝖥𝗋𝖾𝖾Δ(𝐴),𝗆𝖺𝗒𝖻𝖾(̂𝑘,̂𝜓(𝖿𝖿)≫=̂𝑘):𝖬𝖺𝗒𝖻𝖾(𝖵𝖺𝗅(𝑡))→𝖥𝗋𝖾𝖾Δ(𝐴). Consequently (24.18) has type 𝖥𝗋𝖾𝖾Δ(𝐴).
Referenced from 2 locations
Proof of Proposition 24.19 — Typing of the catch clause
Proof. The fork type is the first family of (24.9), specialized by (24.4). Equations (24.16) and (24.16) type the two nested transformations. The second family of (24.9) types the continuation, and ordinary free-monad bind types ̂𝜓(𝖿𝖿)≫=̂𝑘. Both branches of (24.6) have type 𝖥𝗋𝖾𝖾Δ(𝐴). Therefore 𝗆𝖺𝗒𝖻𝖾(̂𝑘,̂𝜓(𝖿𝖿)≫=̂𝑘):𝖬𝖺𝗒𝖻𝖾(𝖵𝖺𝗅(𝑡))→𝖥𝗋𝖾𝖾Δ(𝐴). Binding the masked computation to this function has type 𝖥𝗋𝖾𝖾Δ(𝐴). ◻
Thus the catch algebra is well typed, which is the operation case of theorem 24.13. No separate preservation proof is needed because the branch and continuation result indices are arguments of the clause type itself.
Let 𝑥 :𝖵𝖺𝗅(𝑡) and 𝑀 :𝖧𝖾𝖿𝗍𝗒𝐻(𝖵𝖺𝗅(𝑡)). Combine the catch elaborator with whatever components translate the operations of 𝑀, and write the combined elaborator as 𝐸. Put 𝑁 =𝖾𝗅𝖺𝖻𝗈𝗋𝖺𝗍𝖾𝐸(𝑀). Then 𝖾𝗅𝖺𝖻𝗈𝗋𝖺𝗍𝖾𝐸(𝖼𝖺𝗍𝖼𝗁(𝗉𝗎𝗋𝖾(𝑥),𝑀))(24.5),(24.18)=𝗆𝖺𝗌𝗄𝑤(𝗋𝗎𝗇𝖳𝗁𝗋𝗈𝗐𝑤(𝗉𝗎𝗋𝖾(𝑥)))≫=𝗆𝖺𝗒𝖻𝖾(𝗉𝗎𝗋𝖾,𝑁≫=𝗉𝗎𝗋𝖾)(24.1),(24.2)=𝗉𝗎𝗋𝖾(𝗌𝗈𝗆𝖾(𝑥))≫=𝗆𝖺𝗒𝖻𝖾(𝗉𝗎𝗋𝖾,𝑁≫=𝗉𝗎𝗋𝖾)𝐹𝑟𝑒𝑒−𝑢𝑛𝑖𝑡ℓ=𝗉𝗎𝗋𝖾(𝑥). The fallback is translated structurally before the catch clause is invoked, but it is not placed in the resulting target tree when the protected branch is already pure and successful.
Referenced from 2 locations
Let 𝗍𝗁𝗋𝗈𝗐𝖧 be the lifted source throw operation. Its lift elaborator produces the target throw node. Put 𝑁 =𝖾𝗅𝖺𝖻𝗈𝗋𝖺𝗍𝖾𝐸(𝑀). Therefore 𝖾𝗅𝖺𝖻𝗈𝗋𝖺𝗍𝖾𝐸(𝖼𝖺𝗍𝖼𝗁(𝗍𝗁𝗋𝗈𝗐𝖧,𝑀))(24.5),(24.18)=𝗆𝖺𝗌𝗄𝑤(𝗉𝗎𝗋𝖾(𝗇𝗈𝗇𝖾))≫=𝗆𝖺𝗒𝖻𝖾(𝗉𝗎𝗋𝖾,𝑁≫=𝗉𝗎𝗋𝖾)(24.2)=𝗉𝗎𝗋𝖾(𝗇𝗈𝗇𝖾)≫=𝗆𝖺𝗒𝖻𝖾(𝗉𝗎𝗋𝖾,𝑁≫=𝗉𝗎𝗋𝖾)𝐹𝑟𝑒𝑒−𝑢𝑛𝑖𝑡ℓ=𝑁≫=𝗉𝗎𝗋𝖾𝐹𝑟𝑒𝑒−𝑢𝑛𝑖𝑡𝑟=𝑁. The throw is handled during elaboration of the catch node; it is not exposed to a later top-level throw handler.
Referenced from 2 locations
★☆☆ Let 𝑓 have type 𝖵𝖺𝗅(𝑡)→𝖧𝖾𝖿𝗍𝗒𝐻(𝐵). Calculate the elaboration of 𝖼𝖺𝗍𝖼𝗁(𝗍𝗁𝗋𝗈𝗐𝖧,𝑀)≫=𝖧𝑓 through the point at which the target translation of 𝑀 is sequenced with that of 𝑓. Identify the single occurrence of the translated continuation.
Referenced from 3 locations
Composition by disjoint cases
A global elaborator should contain no clause which knows how many other operations exist. Disjoint signature sum makes that requirement a case split.
For 𝐸1:𝖠𝗅𝗀𝖧(𝐻1,𝐺),𝐸2:𝖠𝗅𝗀𝖧(𝐻2,𝐺), define 𝐸1⋎𝐸2:𝖠𝗅𝗀𝖧(𝐻1⊞𝐻2,𝐺) by the two equations (𝐸1⋎𝐸2)𝐴(𝗂𝗇𝗅(𝑜),𝜓,𝑘)=𝐸1,𝐴(𝑜,𝜓,𝑘),(𝐸1⋎𝐸2)𝐴(𝗂𝗇𝗋(𝑜),𝜓,𝑘)=𝐸2,𝐴(𝑜,𝜓,𝑘). The operation ⋎ composes elaboration clauses; it is unrelated to logical disjunction. Its unparenthesized chains associate to the right.
Referenced from 3 locations
Let 𝐸𝑖:𝖤𝗅𝖺𝖻𝗈𝗋𝖺𝗍𝗂𝗈𝗇(𝐻𝑖,Δ)(𝑖=1,2). Then 𝐸1 ⋎𝐸2 is an elaboration of 𝐻1 ⊞𝐻2 into the same target signature Δ, and 𝖾𝗅𝖺𝖻𝗈𝗋𝖺𝗍𝖾𝐸1⋎𝐸2(𝗂𝗆𝗉𝗎𝗋𝖾(𝗂𝗇𝗅(𝑜),𝜓,𝑘))=𝐸1,𝐴(𝑜,𝖾𝗅𝖺𝖻𝗈𝗋𝖺𝗍𝖾𝐸1⋎𝐸2∘𝜓,𝖾𝗅𝖺𝖻𝗈𝗋𝖺𝗍𝖾𝐸1⋎𝐸2∘𝑘),𝖾𝗅𝖺𝖻𝗈𝗋𝖺𝗍𝖾𝐸1⋎𝐸2(𝗂𝗆𝗉𝗎𝗋𝖾(𝗂𝗇𝗋(𝑜),𝜓,𝑘))=𝐸2,𝐴(𝑜,𝖾𝗅𝖺𝖻𝗈𝗋𝖺𝗍𝖾𝐸1⋎𝐸2∘𝜓,𝖾𝗅𝖺𝖻𝗈𝗋𝖺𝗍𝖾𝐸1⋎𝐸2∘𝑘). Thus adding 𝐸2 does not alter the clause defining 𝐸1, although both clauses recursively use the same combined elaborator on their children.
Referenced from 3 locations
Proof of Theorem 24.23 — Modular elaboration equations
Proof. The algebra type follows from (24.2): every operation is either an 𝐻1-operation or an 𝐻2-operation, and its fork and return types reduce to those of the selected summand. Expand (24.5), then apply the corresponding equation of (24.22). No other case occurs. ◻
For three higher-order signatures, define the reassociation bijection on operation tags by 𝜌(𝗂𝗇𝗅(𝗂𝗇𝗅𝑜))=𝗂𝗇𝗅𝑜,𝜌(𝗂𝗇𝗅(𝗂𝗇𝗋𝑜))=𝗂𝗇𝗋(𝗂𝗇𝗅𝑜),𝜌(𝗂𝗇𝗋𝑜)=𝗂𝗇𝗋(𝗂𝗇𝗋𝑜). Its inverse uses the three equations in reverse. Case reduction in section 24.2 gives, for every tag 𝑢, canonical bijections 𝖮𝗉𝖥𝗈𝗋𝗄(𝐻1⊞𝐻2)⊞𝐻3(𝑢)≅𝖮𝗉𝖥𝗈𝗋𝗄𝐻1⊞(𝐻2⊞𝐻3)(𝜌𝑢) and equal response sets at corresponding fork tags, together with 𝖱𝖾𝗍𝖧(𝐻1⊞𝐻2)⊞𝐻3(𝑢)≅𝖱𝖾𝗍𝖧𝐻1⊞(𝐻2⊞𝐻3)(𝜌𝑢). These maps are the identity after the relevant 𝐻𝑖-case is selected. The reassociation transport sends an operation by 𝜌, reindexes each fork along the first bijection and its response equality, and reindexes the continuation along the last bijection. These are the three transports used below.
Composition is associative after transport along the canonical reassociation bijection between (𝐻1 ⊞𝐻2) ⊞𝐻3 and 𝐻1 ⊞(𝐻2 ⊞𝐻3). The two source types are not definitionally identical; after transporting operation, fork, and response indices, both composite algebras agree extensionally.
Referenced from 3 locations
Proof of Proposition 24.24 — Composition coherence under reassociation
Proof. Case-analyze the transported operation tag. An 𝐻1-tag dispatches to 𝐸1 on both sides, an 𝐻2-tag to 𝐸2, and an 𝐻3-tag to 𝐸3. In every case the fork and continuation are recursively translated by the same combined elaborator. Pointwise equality at every fork and response index, followed by function extensionality, equates the two transported function fields. ◻
★★☆ Define the canonical operation-tag bijection ((𝖮𝗉𝖧𝐻1+𝖮𝗉𝖧𝐻2)+𝖮𝗉𝖧𝐻3)≅(𝖮𝗉𝖧𝐻1+(𝖮𝗉𝖧𝐻2+𝖮𝗉𝖧𝐻3)). Check all three operation-tag cases and show that (𝐸1 ⋎𝐸2) ⋎𝐸3 and 𝐸1 ⋎(𝐸2 ⋎𝐸3) agree after transporting along this bijection. Explain why literal equality without transport has the wrong type.
Referenced from 4 locations
State, throw, and catch in one tree
Let 𝖲𝗍𝖺𝗍𝖾 be the ordinary signature with operations 𝗉𝗎𝗍:ℕ⇝𝟏,𝗀𝖾𝗍:𝟏⇝ℕ. Thus 𝗉𝗎𝗍(𝑛) is the operation instance carrying parameter 𝑛 :ℕ; its response is ⋆ :𝟏. Consider the source and target signatures 𝐻𝗍𝗋:=𝖫𝗂𝖿𝗍(𝖲𝗍𝖺𝗍𝖾)⊞𝖫𝗂𝖿𝗍(𝖳𝗁𝗋𝗈𝗐)⊞𝖢𝖺𝗍𝖼𝗁⊞𝖫𝗂𝖿𝗍(𝖭𝗂𝗅),Δ𝗍𝗋:=𝖲𝗍𝖺𝗍𝖾⊕𝗌𝗂𝗀𝖳𝗁𝗋𝗈𝗐⊕𝗌𝗂𝗀𝖭𝗂𝗅. The component elaborators compose as 𝐸𝗍𝗋:=𝐸𝗅𝗂𝖿𝗍,𝖲𝗍𝖺𝗍𝖾⋎𝐸𝗅𝗂𝖿𝗍,𝖳𝗁𝗋𝗈𝗐⋎𝐸𝖼𝖺𝗍𝖼𝗁⋎𝐸𝗇𝗂𝗅. The displayed rows are duplicate free, so proposition 24.15 gives one insertion witness at each component boundary. Equation (24.8) records the semantic order of the four components.
The transaction is 𝗍𝗋𝖺𝗇𝗌𝖺𝖼𝗍:=𝗉𝗎𝗍(1)≫𝖧𝖼𝖺𝗍𝖼𝗁(𝗉𝗎𝗍(2)≫𝖧𝗍𝗁𝗋𝗈𝗐𝖧,𝗉𝗎𝗋𝖾(⋆))≫𝖧𝗀𝖾𝗍. The catch handles the throw, but its target clause forwards state. Hence the state update to 2 survives the failed protected branch.
The state handler used here deliberately discards the final store. For a residual signature 𝜀, define 𝗋𝗎𝗇𝖲𝗍𝖺𝗍𝖾𝑠:𝖥𝗋𝖾𝖾𝖲𝗍𝖺𝗍𝖾⊕𝗌𝗂𝗀𝜀(𝐴)→𝖥𝗋𝖾𝖾𝜀(𝐴) by 𝗋𝗎𝗇𝖲𝗍𝖺𝗍𝖾𝑠(𝗉𝗎𝗋𝖾(𝑥))=𝗉𝗎𝗋𝖾(𝑥),𝗋𝗎𝗇𝖲𝗍𝖺𝗍𝖾𝑠(𝗉𝗎𝗍(𝑛,𝑘))=𝗋𝗎𝗇𝖲𝗍𝖺𝗍𝖾𝑛(𝑘(⋆)),𝗋𝗎𝗇𝖲𝗍𝖺𝗍𝖾𝑠(𝗀𝖾𝗍(𝑘))=𝗋𝗎𝗇𝖲𝗍𝖺𝗍𝖾𝑠(𝑘(𝑠)),𝗋𝗎𝗇𝖲𝗍𝖺𝗍𝖾𝑠(𝗋𝖾𝗌(𝑜,𝑘))=𝗂𝗆𝗉𝗎𝗋𝖾(𝑜,𝜆𝑟.𝗋𝗎𝗇𝖲𝗍𝖺𝗍𝖾𝑠(𝑘(𝑟))).
Referenced from 2 locations
This is the finite model’s 𝗁𝖲𝗍 observation. The handler of chapter 22 that returns (𝑥,𝑠) is a different interface; substituting it would change the proposition’s result type.
Let 𝗋𝗎𝗇𝖲𝗍𝖺𝗍𝖾0 be the ordinary deep state handler started at 0, and let 𝗎𝗇 eliminate the empty target signature. Then 𝗎𝗇(𝗋𝗎𝗇𝖳𝗁𝗋𝗈𝗐(𝗋𝗎𝗇𝖲𝗍𝖺𝗍𝖾0(𝖾𝗅𝖺𝖻𝗈𝗋𝖺𝗍𝖾𝐸𝗍𝗋(𝗍𝗋𝖺𝗇𝗌𝖺𝖼𝗍))))=𝗌𝗈𝗆𝖾(2).
Referenced from 6 locations
Proof of Proposition 24.26 — Global-state transaction calculation
Proof. First expose the catch clause. The protected target tree is 𝗉𝗎𝗍(2) ≫𝗍𝗁𝗋𝗈𝗐. The local throw handler forwards the put and handles the following throw: 𝗋𝗎𝗇𝖳𝗁𝗋𝗈𝗐(𝗉𝗎𝗍(2)≫𝗍𝗁𝗋𝗈𝗐)(24.1) (forward)=𝗉𝗎𝗍(2)≫𝗋𝗎𝗇𝖳𝗁𝗋𝗈𝗐(𝗍𝗁𝗋𝗈𝗐)(24.1) (throw)=𝗉𝗎𝗍(2)≫𝗉𝗎𝗋𝖾(𝗇𝗈𝗇𝖾). Masking reinjects this residual state tree into Δ𝗍𝗋. Expose the 𝗇𝗈𝗇𝖾 branch, fallback bind, and unit conversions separately. Abbreviate the forwarded target and the fallback by 𝑡0:=𝗉𝗎𝗍(2)≫𝗉𝗎𝗋𝖾(𝗇𝗈𝗇𝖾),𝑡𝟏:𝖳𝗒,𝖵𝖺𝗅(𝑡𝟏)=𝟏,̂𝑘:=𝜆_.𝗀𝖾𝗍,𝑓:=𝗉𝗎𝗋𝖾(⋆)≫=̂𝑘,𝑞:=𝗆𝖺𝗒𝖻𝖾(̂𝑘,𝑓),𝑐𝗍𝗋:=𝐸𝖼𝖺𝗍𝖼𝗁(𝖼𝖺𝗍𝖼𝗁(𝑡𝟏),𝜆𝑏.𝗂𝖿 𝑏 𝗍𝗁𝖾𝗇(𝗉𝗎𝗍(2)≫𝗍𝗁𝗋𝗈𝗐) 𝖾𝗅𝗌𝖾 𝗉𝗎𝗋𝖾(⋆),̂𝑘). Then the remaining calculation fits on one semantic step per line: 𝖾𝗅𝖺𝖻𝗈𝗋𝖺𝗍𝖾𝐸𝗍𝗋(𝗍𝗋𝖺𝗇𝗌𝖺𝖼𝗍)𝑙𝑖𝑓𝑡𝗉𝗎𝗍(1)𝑎𝑛𝑑ℎ𝑒𝑓𝑡𝑦𝑏𝑖𝑛𝑑=𝗉𝗎𝗍(1)≫𝑐𝗍𝗋(24.18)=𝗉𝗎𝗍(1)≫(𝗆𝖺𝗌𝗄(𝗋𝗎𝗇𝖳𝗁𝗋𝗈𝗐(𝗉𝗎𝗍(2)≫𝗍𝗁𝗋𝗈𝗐))≫=𝑞)(24.8)=𝗉𝗎𝗍(1)≫(𝗆𝖺𝗌𝗄(𝗉𝗎𝗍(2)≫𝗉𝗎𝗋𝖾(𝗇𝗈𝗇𝖾))≫=𝑞)(24.2),𝑎𝑏𝑏𝑟𝑒𝑣𝑖𝑎𝑡𝑖𝑜𝑛𝑠=𝗉𝗎𝗍(1)≫(𝑡0≫=𝑞)𝐹𝑟𝑒𝑒−𝑎𝑠𝑠𝑜𝑐=𝗉𝗎𝗍(1)≫𝗉𝗎𝗍(2)≫(𝗉𝗎𝗋𝖾(𝗇𝗈𝗇𝖾)≫=𝑞)(24.6),𝑛𝑜𝑛𝑒𝑏𝑟𝑎𝑛𝑐ℎ=𝗉𝗎𝗍(1)≫𝗉𝗎𝗍(2)≫𝑓𝐹𝑟𝑒𝑒−𝑢𝑛𝑖𝑡ℓ=𝗉𝗎𝗍(1)≫𝗉𝗎𝗍(2)≫𝗀𝖾𝗍. Now use the four equations of equation 24.4; no state transition is hidden in prose: 𝗋𝗎𝗇𝖲𝗍𝖺𝗍𝖾0(𝗉𝗎𝗍(1)≫𝗉𝗎𝗍(2)≫𝗀𝖾𝗍)(24.4) (put)=𝗋𝗎𝗇𝖲𝗍𝖺𝗍𝖾1(𝗉𝗎𝗍(2)≫𝗀𝖾𝗍)(24.4) (put)=𝗋𝗎𝗇𝖲𝗍𝖺𝗍𝖾2(𝗀𝖾𝗍)(24.4) (get)=𝗋𝗎𝗇𝖲𝗍𝖺𝗍𝖾2(𝗉𝗎𝗋𝖾(2))(24.4) (pure)=𝗉𝗎𝗋𝖾(2). Consequently 𝗋𝗎𝗇𝖲𝗍𝖺𝗍𝖾0(𝖾𝗅𝖺𝖻𝗈𝗋𝖺𝗍𝖾𝐸𝗍𝗋(𝗍𝗋𝖺𝗇𝗌𝖺𝖼𝗍))=𝗉𝗎𝗋𝖾(2). Finally, the pure equation of equation 24.1 gives 𝗋𝗎𝗇𝖳𝗁𝗋𝗈𝗐(𝗉𝗎𝗋𝖾(2))=𝗉𝗎𝗋𝖾(𝗌𝗈𝗆𝖾(2)),𝗎𝗇(𝗉𝗎𝗋𝖾(𝗌𝗈𝗆𝖾(2)))=𝗌𝗈𝗆𝖾(2), which is the displayed result. ◻
The source operation is an interface, not a fixed semantics. Replace only the catch component by 𝐸𝗉𝗋𝗈𝗍𝖾𝖼𝗍𝖾𝖽(𝖼𝖺𝗍𝖼𝗁(𝑡),𝜓,𝑘):=𝜓(𝗍𝗍)≫=𝑘. This component always runs the protected branch and never handles its throw. All other components in (24.8) remain unchanged. The same source transaction then reaches the outer throw handler and returns 𝗇𝗈𝗇𝖾. The change is local to one algebra clause.
★★☆ Replace 𝐸𝖼𝖺𝗍𝖼𝗁 in (24.8) by (24.8). Calculate the elaborated target tree through the state handler and then the throw handler. Show that the final result is 𝗇𝗈𝗇𝖾, while the state update to 2 is performed before the throw aborts the remaining continuation.
Referenced from 3 locations
Lawfulness is stated after observation
Intrinsic typing ensures that the catch clause returns a target tree of the right result type. It does not ensure that the clause behaves like exception catch. Lawfulness is an equational obligation on the interpretation obtained after elaboration and handling.
For this section put the target throw signature at the front: 𝖳𝗁𝗋𝗈𝗐 ⊕𝗌𝗂𝗀𝜀. Let ℎ𝐴:𝖥𝗋𝖾𝖾𝖳𝗁𝗋𝗈𝗐⊕𝗌𝗂𝗀𝜀(𝐴)→𝖥𝗋𝖾𝖾𝜀(𝖬𝖺𝗒𝖻𝖾(𝐴)) be the throw handler, and let 𝗆𝖺𝗌𝗄𝐴:𝖥𝗋𝖾𝖾𝜀(𝐴)→𝖥𝗋𝖾𝖾𝖳𝗁𝗋𝗈𝗐⊕𝗌𝗂𝗀𝜀(𝐴) be masking. These are the front-summand instances of 𝗋𝗎𝗇𝖳𝗁𝗋𝗈𝗐𝑤 and 𝗆𝖺𝗌𝗄𝑤 from (24.1), (24.2); this section suppresses the unique canonical witness and writes ℎ and 𝗆𝖺𝗌𝗄 to keep the law equations legible. Let 𝐸0:𝖤𝗅𝖺𝖻𝗈𝗋𝖺𝗍𝗂𝗈𝗇(𝐻,𝖳𝗁𝗋𝗈𝗐⊕𝗌𝗂𝗀𝜀) be any elaboration for the remaining higher-order operations, and define 𝐸:=𝐸𝗅𝗂𝖿𝗍,𝖳𝗁𝗋𝗈𝗐⋎𝐸𝖼𝖺𝗍𝖼𝗁⋎𝐸0,𝗋𝗎𝗇(𝑀):=ℎ(𝖾𝗅𝖺𝖻𝗈𝗋𝖺𝗍𝖾𝐸(𝑀)). Thus 𝗋𝗎𝗇:𝖧𝖾𝖿𝗍𝗒𝖫𝗂𝖿𝗍(𝖳𝗁𝗋𝗈𝗐)⊞𝖢𝖺𝗍𝖼𝗁⊞𝐻(𝐴)→𝖥𝗋𝖾𝖾𝜀(𝖬𝖺𝗒𝖻𝖾(𝐴)).
Two structural facts about the target handler contain the proof mechanism.
For 𝑚:𝖥𝗋𝖾𝖾𝖳𝗁𝗋𝗈𝗐⊕𝗌𝗂𝗀𝜀(𝐴),𝑓:𝐴→𝖥𝗋𝖾𝖾𝖳𝗁𝗋𝗈𝗐⊕𝗌𝗂𝗀𝜀(𝐵), we have ℎ(𝑚≫=𝑓)=ℎ(𝑚)≫=𝗆𝖺𝗒𝖻𝖾(ℎ∘𝑓,𝗉𝗎𝗋𝖾(𝗇𝗈𝗇𝖾)).
Referenced from 4 locations
Proof of Lemma 24.27 — Throw handling distributes through bind
Proof. Induct on 𝑚. If 𝑚 =𝗉𝗎𝗋𝖾(𝑥), both sides are ℎ(𝑓(𝑥)). If 𝑚 is throw, both sides are 𝗉𝗎𝗋𝖾(𝗇𝗈𝗇𝖾). If 𝑚 is a residual operation 𝗂𝗆𝗉𝗎𝗋𝖾(𝑜,𝑘), handling and bind both forward it. The two sides are operation nodes with continuation families 𝑟↦ℎ(𝑘(𝑟)≫=𝑓)and𝑟↦ℎ(𝑘(𝑟))≫=𝗆𝖺𝗒𝖻𝖾(ℎ∘𝑓,𝗉𝗎𝗋𝖾(𝗇𝗈𝗇𝖾)), which agree pointwise by the induction hypothesis. ◻
For 𝑛 :𝖥𝗋𝖾𝖾𝜀(𝐴), ℎ(𝗆𝖺𝗌𝗄𝑛)=𝑛≫=(𝗉𝗎𝗋𝖾∘𝗌𝗈𝗆𝖾).
Referenced from 4 locations
Proof of Lemma 24.28 — Handling a masked tree
Proof. Induct on 𝑛. A pure value is mapped to 𝗉𝗎𝗋𝖾(𝗌𝗈𝗆𝖾(𝑥)) on both sides. A residual operation is forwarded by 𝗆𝖺𝗌𝗄, then forwarded by ℎ; ordinary bind also forwards it. The continuation families agree by the induction hypothesis. ◻
These lemmas reduce the higher-order catch operation to an ordinary equation on handled results.
Fix 𝑡 :𝖳𝗒, put 𝐴 =𝖵𝖺𝗅(𝑡), and take 𝑀1,𝑀2:𝖧𝖾𝖿𝗍𝗒𝖫𝗂𝖿𝗍(𝖳𝗁𝗋𝗈𝗐)⊞𝖢𝖺𝗍𝖼𝗁⊞𝐻(𝐴). Put 𝑅𝑖 =𝗋𝗎𝗇(𝑀𝑖). Then 𝗋𝗎𝗇(𝖼𝖺𝗍𝖼𝗁(𝑀1,𝑀2))=𝑅1≫=𝗆𝖺𝗒𝖻𝖾(𝗉𝗎𝗋𝖾∘𝗌𝗈𝗆𝖾,𝑅2).
Referenced from 5 locations
Proof of Proposition 24.29 — Observable catch equation
Proof. Write 𝑚𝑖 =𝖾𝗅𝖺𝖻𝗈𝗋𝖺𝗍𝖾𝐸(𝑀𝑖), and abbreviate 𝑞2=𝗆𝖺𝗒𝖻𝖾(𝗉𝗎𝗋𝖾,𝑚2),𝑄2=𝗆𝖺𝗒𝖻𝖾(ℎ∘𝑞2,𝗉𝗎𝗋𝖾(𝗇𝗈𝗇𝖾)). Expand the smart constructor, then the catch algebra, and only then use the right unit: 𝗋𝗎𝗇(𝖼𝖺𝗍𝖼𝗁(𝑀1,𝑀2))(24.2.1)=ℎ(𝐸𝖼𝖺𝗍𝖼𝗁(𝖼𝖺𝗍𝖼𝗁(𝑡),𝜆𝑏.𝑚𝑏,𝗉𝗎𝗋𝖾))(24.18)=ℎ(𝗆𝖺𝗌𝗄(ℎ(𝑚1))≫=𝗆𝖺𝗒𝖻𝖾(𝗉𝗎𝗋𝖾,𝑚2≫=𝗉𝗎𝗋𝖾))𝐹𝑟𝑒𝑒−𝑢𝑛𝑖𝑡𝑟=ℎ(𝗆𝖺𝗌𝗄(ℎ(𝑚1))≫=𝗆𝖺𝗒𝖻𝖾(𝗉𝗎𝗋𝖾,𝑚2)). Apply lemma 24.27 and then lemma 24.28: ℎ(𝗆𝖺𝗌𝗄(ℎ(𝑚1))):𝖥𝗋𝖾𝖾𝜀(𝖬𝖺𝗒𝖻𝖾(𝖬𝖺𝗒𝖻𝖾(𝐴))),𝑧:𝖬𝖺𝗒𝖻𝖾(𝐴). The outer 𝖬𝖺𝗒𝖻𝖾 records the result of handling the masked tree; the inner one is the result already produced by ℎ(𝑚1). (24.9)𝑙𝑒𝑚𝑚𝑎24.27=ℎ(𝗆𝖺𝗌𝗄(ℎ(𝑚1)))≫=𝑄2𝑙𝑒𝑚𝑚𝑎24.28=(ℎ(𝑚1)≫=(𝗉𝗎𝗋𝖾∘𝗌𝗈𝗆𝖾))≫=𝑄2𝐹𝑟𝑒𝑒−𝑎𝑠𝑠𝑜𝑐=ℎ(𝑚1)≫=(𝜆𝑧:𝖬𝖺𝗒𝖻𝖾(𝐴).(𝗉𝗎𝗋𝖾(𝗌𝗈𝗆𝖾(𝑧)))≫=𝑄2)𝐹𝑟𝑒𝑒−𝑢𝑛𝑖𝑡ℓ=ℎ(𝑚1)≫=(𝜆𝑧.𝑄2(𝗌𝗈𝗆𝖾(𝑧)))(24.6)=ℎ(𝑚1)≫=(𝜆𝑧.ℎ(𝑞2(𝑧))). For 𝑧 =𝗌𝗈𝗆𝖾(𝑥), the final function returns 𝗉𝗎𝗋𝖾(𝗌𝗈𝗆𝖾(𝑥)); for 𝑧 =𝗇𝗈𝗇𝖾, it returns ℎ(𝑚2). Substituting 𝑅𝑖 =ℎ(𝑚𝑖) gives (24.29). ◻
Fix codes 𝑡𝐴,𝑡𝐵:𝖳𝗒. Put 𝐴 =𝖵𝖺𝗅(𝑡𝐴) and 𝐵 =𝖵𝖺𝗅(𝑡𝐵). Fix a residual higher-order signature 𝐻, and put 𝐻𝑐=𝖫𝗂𝖿𝗍(𝖳𝗁𝗋𝗈𝗐)⊞𝖢𝖺𝗍𝖼𝗁⊞𝐻. Let 𝑥:𝐴,𝑘:𝐴→𝖧𝖾𝖿𝗍𝗒𝐻𝑐(𝐵),𝑀,𝑀1,𝑀′1,𝑀2,𝑀′2:𝖧𝖾𝖿𝗍𝗒𝐻𝑐(𝐴). Write 𝗍𝗁𝗋𝗈𝗐𝖧𝑋 :𝖧𝖾𝖿𝗍𝗒𝐻𝑐(𝑋) for the lifted empty-response throw at result index 𝑋. The interpretation (24.9) satisfies 𝗋𝗎𝗇(𝗍𝗁𝗋𝗈𝗐𝖧𝐴≫=𝖧𝑘)=𝗋𝗎𝗇(𝗍𝗁𝗋𝗈𝗐𝖧𝐵),(bind--throw)𝗋𝗎𝗇(𝖼𝖺𝗍𝖼𝗁(𝗉𝗎𝗋𝖾(𝑥),𝑀))=𝗋𝗎𝗇(𝗉𝗎𝗋𝖾(𝑥)),(catch--return)𝗋𝗎𝗇(𝖼𝖺𝗍𝖼𝗁(𝗍𝗁𝗋𝗈𝗐𝖧𝐴,𝑀))=𝗋𝗎𝗇(𝑀),(catch--throw1)𝗋𝗎𝗇(𝖼𝖺𝗍𝖼𝗁(𝑀,𝗍𝗁𝗋𝗈𝗐𝖧𝐴))=𝗋𝗎𝗇(𝑀),(catch--throw2). Moreover, if 𝗋𝗎𝗇(𝑀1)=𝗋𝗎𝗇(𝑀′1),𝗋𝗎𝗇(𝑀2)=𝗋𝗎𝗇(𝑀′2), then 𝗋𝗎𝗇(𝖼𝖺𝗍𝖼𝗁(𝑀1,𝑀2))=𝗋𝗎𝗇(𝖼𝖺𝗍𝖼𝗁(𝑀′1,𝑀′2)).
Referenced from 7 locations
Proof of Theorem 24.30 — Lawfulness equations for modular catch
Proof. A lifted throw has no response. Hefty bind changes only its impossible continuation, so bind–throw is immediate after elaboration and handling.
For the remaining equations use (24.29). Since 𝗋𝗎𝗇(𝗉𝗎𝗋𝖾(𝑥))=𝗉𝗎𝗋𝖾(𝗌𝗈𝗆𝖾(𝑥)),𝗋𝗎𝗇(𝗍𝗁𝗋𝗈𝗐𝖧)=𝗉𝗎𝗋𝖾(𝗇𝗈𝗇𝖾), catch–return and catch–throw1 are the two left-unit cases of (24.29). For catch–throw2, the branch function becomes 𝗆𝖺𝗒𝖻𝖾(𝗉𝗎𝗋𝖾∘𝗌𝗈𝗆𝖾,𝗉𝗎𝗋𝖾(𝗇𝗈𝗇𝖾))=𝗉𝗎𝗋𝖾, pointwise on 𝖬𝖺𝗒𝖻𝖾(𝐴). Equation (24.29) therefore reduces to the right unit of target bind.
For (24.30), substitute the first assumed equality for the left operand of the bind in (24.29). The second assumed equality says that the two branch functions agree at every returned value. Function extensionality equates those functions, and congruence of target bind gives the result. ◻
The equalities are deliberately stated after 𝗋𝗎𝗇. Two source trees may elaborate to different target trees yet become equal after the throw handler observes them. Replacing (24.30) by raw syntactic equality would state a stronger and generally false interface.
★★☆ Using only proposition 24.29 and the free-monad laws, prove 𝗋𝗎𝗇(𝖼𝖺𝗍𝖼𝗁(𝖼𝖺𝗍𝖼𝗁(𝑀1,𝑀2),𝑀3))=𝗋𝗎𝗇(𝖼𝖺𝗍𝖼𝗁(𝑀1,𝖼𝖺𝗍𝖼𝗁(𝑀2,𝑀3))). Do not claim equality of the two source trees.
Referenced from 3 locations
★☆☆ For each of the following claims, state whether it follows from theorem 24.13, from theorem 24.30, or from neither:
the target tree has the same result index as the source hefty tree;
catch satisfies catch–throw after observation;
elaboration simulates every source reduction step;
every target operation is handled after finitely many target steps;
a parser and typechecker construct the intended indexed source tree.
Give one sentence identifying the missing definition or theorem for every claim answered “neither.”
Referenced from 3 locations
Three syntaxes, three invariants
Scoped syntax and hefty trees both add recursive positions beyond an ordinary continuation, but enforce different invariants:
| Syntax |
Node data beyond an operation |
Recursive invariant |
Modular interpretation step |
| Ordinary free tree |
Response continuation 𝑘(𝑟) |
Fold recurses through every response branch |
One first-order algebra component |
| Scoped syntax of chapter 23 |
Ordinary arguments, a distinguished scoped computation, continuation, and explicit substitution structure |
Substitution distributes through each declared scoped position and preserves binding |
An elementwise interpretation respecting the scoped-substitution equations |
| Hefty tree |
Fork family 𝜓(𝑠) plus an ordinary continuation 𝑘(𝑟) |
Catamorphism recurses through both fields; monadic bind recurses only through 𝑘 |
One hefty algebra component receiving already elaborated forks and continuation |
A scoped signature distinguishes one particular binding pattern and carries explicit substitution equations. A hefty signature records a dependent family of computation arguments and leaves their target arrangement to the elaboration clause. A translation must map the scoped body and continuation to explicit fork and response indices, and it must commute with scoped substitution: translating after substitution must equal target bind after translation. The constructor shapes alone establish none of these equations.
Mechanized boundary.
The published Agda development formalizes the indexed definitions, modular composition, catch equations, and concrete transaction normalizations used here. Its lawfulness interface assumes function extensionality, and its bind-throw field proves the homogeneous result-code instance; the heterogeneous form in theorem 24.30 is proved locally. The finite Kappa capsule checks the displayed elaboration equations. Neither artifact contains a source operational semantics or a source–target simulation theorem; appendix E records the exact archive and executable boundaries.
The definitions and selected examples in this chapter follow Poulsen and van der Rest’s intrinsically typed Agda development and its accompanying paper [PvdR23]. The proofs are reconstructed locally at the signatures displayed here; the citation supplies provenance, not omitted premises.
Suggested first pass.
Begin with exercise 24.12, exercise 24.13, continue with exercise 24.14, and finish with the practical project exercise 24.17.
★★★ For the three-branch signature of exercise 24.2, take 𝖥𝗈𝗋𝗄𝖥𝗂𝗋𝗌𝗍(𝖿𝗂𝗋𝗌𝗍(𝑡))=({0,1,2},𝜆_.𝖵𝖺𝗅(𝑡)),𝖱𝖾𝗍𝖧𝖥𝗂𝗋𝗌𝗍(𝖿𝗂𝗋𝗌𝗍(𝑡))=𝖵𝖺𝗅(𝑡). Thus this exercise is independent of the earlier exercise’s answer. Define an elaborator which runs the branches from left to right, treating target throw as failure and returning the first successful value. If all three branches throw, emit one target throw. Give the complete algebra clause, including every mask, handler, bind, and 𝗆𝖺𝗒𝖻𝖾; then derive the types of all intermediate terms. Calculate its action on 𝖿𝗂𝗋𝗌𝗍(𝗍𝗁𝗋𝗈𝗐𝖧,𝗉𝗎𝗋𝖾(2),𝗉𝗎𝗋𝖾(3)).
Referenced from 4 locations
★★☆ Starting from (24.18), reprove catch–throw2 directly by structural induction on the elaborated target tree, without using proposition 24.29. State the induction predicate, and give the pure, throw, and forwarded-operation cases. Compare the resulting proof with the factorized proof through lemma 24.27, lemma 24.28.
Referenced from 4 locations
★★☆ Define the pair-returning observation 𝗋𝗎𝗇𝖲𝗍𝖺𝗍𝖾×𝑠. Replace only the pure equation of equation 24.4 with 𝗋𝗎𝗇𝖲𝗍𝖺𝗍𝖾×𝑠(𝗉𝗎𝗋𝖾(𝑥))=𝗉𝗎𝗋𝖾(𝑥,𝑠) and retaining the put, get, and forwarding equations. Put 𝑇:=𝖾𝗅𝖺𝖻𝗈𝗋𝖺𝗍𝖾𝐸𝗍𝗋(𝗍𝗋𝖺𝗇𝗌𝖺𝖼𝗍). Calculate 𝗋𝗎𝗇𝖳𝗁𝗋𝗈𝗐(𝗋𝗎𝗇𝖲𝗍𝖺𝗍𝖾0(𝑇))and𝗋𝗎𝗇𝖳𝗁𝗋𝗈𝗐(𝗋𝗎𝗇𝖲𝗍𝖺𝗍𝖾×0(𝑇)). Explain why the results are 𝗌𝗈𝗆𝖾(2) and 𝗌𝗈𝗆𝖾(2,2), respectively, and why this comparison changes only the observation of final state, not catch’s rollback policy.
Referenced from 4 locations
★★★ Generalize exercise 24.8 to a finite binary tree of higher-order signature sums. Define a canonical flattening to a list of summands, transport every operation, fork, and return index along the flattening, and prove that any two parenthesizations of the same ordered list of component elaborators agree after transport. Explain why permutation of the list is an additional theorem rather than part of reassociation.
Referenced from 3 locations
★★★ Consider the finite source grammar 𝑀::=𝗋𝖾𝗍𝗎𝗋𝗇(𝑛)∣𝗉𝗎𝗍(𝑛)∣𝗀𝖾𝗍(𝑥.𝑀)∣𝗍𝗁𝗋𝗈𝗐∣𝖼𝖺𝗍𝖼𝗁(𝑀,𝑀)∣𝑀;𝑀 and configurations ⟨𝑠,𝑀⟩. Read this as a sorted grammar: 𝗉𝗎𝗍(𝑛) :𝟏, 𝗀𝖾𝗍 :ℕ, catch’s branches have one result sort, and sequencing discards its left result. Define the embedding ⌈ −⌉ into the intrinsically sorted hefty syntax by ⌈𝗋𝖾𝗍𝗎𝗋𝗇(𝑛)⌉=𝗉𝗎𝗋𝖾(𝑛),⌈𝗉𝗎𝗍(𝑛)⌉=𝗉𝗎𝗍𝖧(𝑛),⌈𝗀𝖾𝗍(𝑥.𝑀)⌉=𝗀𝖾𝗍𝖧≫=𝖧(𝜆𝑥.⌈𝑀⌉),⌈𝗍𝗁𝗋𝗈𝗐⌉=𝗍𝗁𝗋𝗈𝗐𝖧,⌈𝖼𝖺𝗍𝖼𝗁(𝑀,𝑁)⌉=𝖼𝖺𝗍𝖼𝗁(⌈𝑀⌉,⌈𝑁⌉),⌈𝑀;𝑁⌉=⌈𝑀⌉≫𝖧⌈𝑁⌉. The transaction from (24.8) is now expressible using the final sequencing clause. The root steps are ⟨𝑠,𝗋𝖾𝗍𝗎𝗋𝗇(𝑛);𝑀⟩⟼𝖧⟨𝑠,𝑀⟩,⟨𝑠,𝗉𝗎𝗍(𝑛);𝑀⟩⟼𝖧⟨𝑛,𝑀⟩,⟨𝑠,𝗀𝖾𝗍(𝑥.𝑀)⟩⟼𝖧⟨𝑠,𝑀[𝑠/𝑥]⟩,⟨𝑠,𝖼𝖺𝗍𝖼𝗁(𝗋𝖾𝗍𝗎𝗋𝗇(𝑛),𝑁)⟩⟼𝖧⟨𝑠,𝗋𝖾𝗍𝗎𝗋𝗇(𝑛)⟩,⟨𝑠,𝖼𝖺𝗍𝖼𝗁(𝗍𝗁𝗋𝗈𝗐,𝑁)⟩⟼𝖧⟨𝑠,𝑁⟩, plus left congruence under catch and sequencing. Relate ⟨𝑠,𝑀⟩ to the target tree 𝗋𝗎𝗇𝖲𝗍𝖺𝗍𝖾𝑠(𝖾𝗅𝖺𝖻𝗈𝗋𝖺𝗍𝖾𝐸𝗍𝗋(⌈𝑀⌉)). For each of the five roots, show a nonempty target calculation to the tree related to the source reduct; in the catch–throw case expose the catch clause before using equation 24.1, equation 24.2. Then state one property still outside this finite simulation (for example, inference into intrinsically typed hefty syntax or absence of unhandled operations for arbitrary sums).
Referenced from 4 locations
★★★ Practical project.hefty-elaboration Implement the finite source and target fragments used in example 24.7, proposition 24.26. The source must represent pure nodes, lifted state and throw operations, a catch node with two computation arguments, the correct bind (24.6), the incorrect structural bind (24.3), the catamorphic elaborator (24.5), and composition by operation-tag dispatch. The target must implement free-tree bind, state handling, throw handling, and empty-signature elimination.
Maintain this invariant: every node carries a result-type tag; both catch branches have the operation’s declared branch type; the operation continuation returns the node’s result type; and each source operation tag is accepted by exactly one elaboration component. Produce four decidable checks:
the global transaction returns 𝗌𝗈𝗆𝖾(2);
replacing only the catch component by (24.8) returns 𝗇𝗈𝗇𝖾;
the correct bind makes the counted example return 1, while (24.3) makes it return 2;
constructing a catch whose branches have different result tags is rejected before elaboration.
The accepted run must report all four outcomes and terminate unsuccessfully if any expected value changes.
Referenced from 5 locations