Prerequisites. Direct starred prerequisites: Proof-producing tactics are required only for the tactic-state interface; quotation, staging, and hygienic expansion use the smaller syntax-and-substitution route. No later core chapter depends on this route.
Consider a macro 𝗍𝗐𝗂𝖼𝖾(𝑒) that expands to 𝗅𝖾𝗍𝑥=𝑒𝗂𝗇𝑥+𝑥. Expanding it inside a program that already binds 𝑥 is unsafe when identifiers are represented by strings: the macro’s binder may capture occurrences supplied by the caller, or a caller binder may capture occurrences introduced by the macro. Renaming the printed variable after expansion is too late. The expander must record binding information while it constructs syntax.
Fix disjoint countable sets of atoms, raw binder tokens 𝛽, raw reference tokens 𝜔, expanded origin tokens 𝑜, and scope stamps. Every raw syntax tree uses each binder token at most once. This global uniqueness condition is part of raw well-formedness, even when two binders lie in disjoint subtrees or at different staging levels. A raw binder is written 𝑎𝛽. Every raw reference is written 𝑎𝜔𝛽: its stable token is 𝜔, and its lexical target is the particular binder token 𝛽, not the printed atom 𝑎. Thus in 𝜆𝑎𝛽0.𝜆𝑎𝛽1.𝑎𝜔𝛽0, the reference still targets 𝛽0 despite the intervening binder named 𝑎.
An expanded binding is a triple (𝑎,𝑆𝑏,𝑜), where 𝑆𝑏 is a finite scope set. An origin environmentΞ is a finite partial map 𝑜↦(𝑎,𝑆𝑏). Its entry is a candidate for a scoped reference 𝑎𝑆𝑟 when 𝑆𝑏⊆𝑆𝑟. The partial operation 𝗋𝖾𝗌𝗈𝗅𝗏𝖾Ξ(𝑎𝑆𝑟) selects the origin of the unique candidate maximal by inclusion. It is undefined when there is no candidate or when two distinct candidates are maximal. Expanded references retain their raw token 𝜔 or carry the marker 𝗇𝖾𝗐(𝑜𝑡) naming a declared target origin 𝑜𝑡.
The subset clause makes lexical information monotone. Comparing only the atom would resolve every printed 𝑥 to the nearest textual binder and would reintroduce capture.
Raw terms are trees with typed binders 𝑟::=𝑎𝜔𝛽∣𝜆(𝑎𝛽:𝐴).𝑟∣𝑟𝑟∣𝗊𝗎𝗈𝗍𝖾(𝑟)∣𝗌𝗉𝗅𝗂𝖼𝖾(𝑟)∣𝑚(𝑟1,…,𝑟𝑘). Scoped terms have the same constructors but every identifier is a scoped identifier and every reference resolves by definition 116.1. A syntax object is a scoped term together with its phase number. Raw syntax has no scoped-binding claim, but it is well formed only when its binder tokens are pairwise distinct and each lexical target 𝛽 on a reference names the unique enclosing binder carrying 𝛽. Scoped syntax records enough data to check the corresponding expanded binding claim. Every source-typing and expansion judgment below ranges only over well-formed raw syntax.
A transformer package 𝑃 partitions its nodes into copies substituted from named input nodes and newly constructed nodes. For an introduction stamp 𝑖, 𝗆𝖺𝗋𝗄𝑖(𝑃)=ℎ copies every substituted node byte-for-byte and adds 𝑖 to every constructed identifier. Every constructed binder 𝑎𝑆[𝑜;𝗇𝖾𝗐(𝑜)] has its own origin. Every free constructed reference is marked 𝗇𝖾𝗐(𝑜𝑡), where 𝑜𝑡 is either such a constructed origin or an origin exported by the transformer-definition environment 𝐷. The latter records an origin environment Ξ𝐷 for its exports.
The judgment 𝐷;Ξ⊢ℎ𝗉𝗋𝗈𝗏 is defined by structural recursion on scoped syntax. At a copied source reference 𝑎𝑆[𝑜;𝗌𝗋𝖼(𝜔)], it requires 𝗋𝖾𝗌𝗈𝗅𝗏𝖾Ξ(𝑎𝑆)=𝑜. At a constructed reference 𝑎𝑆[𝑜;𝗇𝖾𝗐(𝑜𝑡)], it requires 𝗋𝖾𝗌𝗈𝗅𝗏𝖾Ξ∪Ξ𝐷(𝑎𝑆)=𝑜=𝑜𝑡 and requires 𝑜𝑡 to be a constructed origin in scope or an origin exported by 𝐷. At a transformer-created binder 𝜆(𝑎𝑆[𝑜;𝗇𝖾𝗐(𝑜)]:𝐴).ℎ0, it checks ℎ0 under Ξ,𝑜↦(𝑎,𝑆). A binder marked 𝗌𝗋𝖼(𝛽) extends Ξ by the same clause. Applications check both subterms under Ξ, and quotation and splicing check their subterm under Ξ. Thus the origin environment is extended under every binder that a transformer creates; checking only free references is not the provenance judgment. Finally, the judgment requires every copied node to be unchanged. It fails on an unresolved, ambiguous, or mismatched reference, and it rejects duplicate binder origins.
The scoped expansion environment¯𝐸 is a finite stack of records 𝛽↦(𝑎,𝑆𝑏,𝑜). Lookup is by binder token, so shadowing an atom does not overwrite an older record. Its origin projection is 𝜋𝑜(¯𝐸)(𝛽)=𝑜, and its origin environment is Ξ(¯𝐸)(𝑜)=(𝑎,𝑆𝑏). These two projections are not source typing environments. Let 𝐿 be the lexical scopes on the node being expanded and let 𝑈 be the finite set of stamps already used. Write Scopes(𝐷,¯𝐸,𝑟) for the finite set of stamps occurring in 𝐷, ¯𝐸, and 𝑟. An expansion judgment is well formed only when Scopes(𝐷,¯𝐸,𝑟)⊆𝑈; the vector form uses the union of the component scope sets. The judgment 𝐷;¯𝐸;𝑛;𝐿;𝑈⊢𝑟⟼ℎ▹𝑈′ is the least relation generated by the following rules. Argument vectors are expanded left to right by the two rules
𝐷;¯𝐸;𝑛;𝐿;𝑈⊢[]⟼[]▹𝑈
Exp-Args-Nil
𝐷;¯𝐸;𝑛;𝐿;𝑈⊢𝑟⟼ℎ▹𝑈1𝐷;¯𝐸;𝑛;𝐿;𝑈1⊢⃗𝑟⟼⃗ℎ▹𝑈2
𝐷;¯𝐸;𝑛;𝐿;𝑈⊢(𝑟,⃗𝑟)⟼(ℎ,⃗ℎ)▹𝑈2
Exp-Args-Cons
Single terms are generated by the following six rules.
Here 𝐶(𝑃) is the finite set of origins on constructed binders. The two fresh choices therefore name the finite sets they avoid: 𝑖 avoids the threaded stamp set 𝑈𝑘, and every member of 𝐶(𝑃) avoids 𝐹𝑃. The transformer table stores an arity and an executable partial meta-function, not a trusted typing theorem. If it diverges or produces a package that fails 𝗉𝗋𝗈𝗏, then Exp-Macro has no derivation.
Expand 𝗍𝗐𝗂𝖼𝖾(𝑥) under the caller binder 𝑥{𝑐}. If the transformer chooses introduction stamp 𝑖, its output has the form 𝗅𝖾𝗍𝑥{𝑖}=𝑥{𝑐}𝗂𝗇𝑥{𝑖}+𝑥{𝑖}. The caller occurrence resolves to the binder carrying 𝑐; the two introduced occurrences resolve to the binder carrying 𝑖. Equal printed atoms do no work in this calculation.
Suppose 𝐷;¯𝐸;𝑛;𝐿;𝑈⊢𝑟⟼ℎ▹𝑈′. Every reference in ℎ originating from an input subterm resolves to the image of the same input binding, and every reference introduced by a transformer resolves either to a binding introduced by that transformer or to a binding named explicitly in the transformer’s definition environment.
Proof. Use mutual induction on term expansion and argument-vector expansion. The strengthened claims carry 𝜋𝑜(¯𝐸), which maps raw binder tokens to expanded origins, and Ξ(¯𝐸), which maps those origins to atoms and scope sets. Exp-Args-Nil has no references. Exp-Args-Cons applies the term claim to its head and the vector claim to its tail under the threaded stamp set. In Exp-Var, lookup uses the stable token 𝛽, and 𝗋𝖾𝗌𝗈𝗅𝗏𝖾Ξ(¯𝐸)(𝑎𝑆𝑏)=𝜋𝑜(¯𝐸)(𝛽) is the second premise. In particular, a reference whose token targets an outer binder keeps that binder’s scope set rather than acquiring the scope of an intervening same-atom binder.
In Exp-Lam, let 𝑜 and 𝑠 be the origin and stamp in the rule. The exact side conditions and the well-formedness invariant give 𝑠∉𝑈,𝑜∉domΞ(¯𝐸)∪Orig(𝐷). The body induction hypothesis is taken under ¯𝐸,𝛽↦(𝑎,𝐿∪{𝑠},𝑜). Equivalently, provenance resolution traverses the output binder under Ξ(¯𝐸),𝑜↦(𝑎,𝐿∪{𝑠}), exactly the binder clause of definition 116.3. Every older candidate lacks 𝑠, whereas the new binder has it. Hence the new binder is the unique maximal candidate for its source occurrences, and no older reference acquires the new stamp. The two induction hypotheses of Exp-App preserve both maps in the threaded stamp sets.
In Exp-Macro, argument syntax retains its scope sets and origin tokens by the copying clause of 𝗆𝖺𝗋𝗄. The introduction stamp 𝑖∉𝑈𝑘 is absent from every copied identifier, because all stamps on the expanded arguments belong to 𝑈𝑘. Hence a constructed binder cannot capture an argument reference. Traverse the marked package by 𝐷;Ξ(¯𝐸)⊢ℎ𝗉𝗋𝗈𝗏. At each constructed binder the provenance judgment extends the origin environment before checking its body. Consequently a nested constructed reference resolves against all and only the constructed binders in lexical scope together with the caller and exported origins. The reference clause then gives its declared constructed origin or its declared origin in 𝐷. The disjointness 𝐶(𝑃)∩𝐹𝑃=∅ prevents a constructed origin from identifying an older origin. Exp-Quote and Exp-Splice change only the phase component and apply their term induction hypothesis. These cases cover every term and vector rule. ◻
Deleting the distinction between constructed and substituted syntax breaks the proof. If the stamp 𝑖 is also attached to the argument 𝑥{𝑐}, then it becomes 𝑥{𝑐,𝑖} and the introduced binder is a candidate; a maximality comparison may capture it.
★★☆ Define a macro that expands 𝗌𝗐𝖺𝗉(𝑒1,𝑒2) to two nested lets whose printed binders are both 𝑥. Assign concrete scope sets and show the unique resolver result for every occurrence when 𝑒1 itself contains a free printed 𝑥.
A staged context contains declarations 𝑥:𝐴@𝑛. The judgment Γ⊢𝑛𝑒:𝐴 means that 𝑒 is available at phase 𝑛. In addition to the ordinary typed lambda-calculus rules at a fixed phase, the calculus has
Γ⊢𝑛+1𝑒:𝐴
Γ⊢𝑛𝗊𝗎𝗈𝗍𝖾(𝑒):𝖢𝗈𝖽𝖾(𝐴)
Q-Quote
Γ⊢𝑛𝑒:𝖢𝗈𝖽𝖾(𝐴)
Γ⊢𝑛+1𝗌𝗉𝗅𝗂𝖼𝖾(𝑒):𝐴
Q-Splice
(𝑥:𝐴@𝑛)∈Γ
Γ⊢𝑛𝑥:𝐴
Q-Var
There is no rule that uses 𝑥:𝐴@𝑛 directly at phase 𝑛+1.
A source context Γ maps source origins to declarations 𝐴@𝑛, and the source environment𝐸0 maps binder tokens to source origins. In particular, 𝐸0 is not the scoped stack ¯𝐸 and is not the expanded-origin projection 𝜋𝑜(¯𝐸). A macro signature 𝑀 records only an intended arity and source type 𝑀(𝑚)=(𝐴1,…,𝐴𝑘;𝐴;𝑛); it is not evidence that the executable transformer respects that type. Write 𝐸0;Γ⊢𝗌𝗋𝖼𝑛𝑟:𝐴. The ordinary variable, lambda, application, quotation, splice, and macro rules are
𝐸0(𝛽)=𝑜𝑠Γ(𝑜𝑠)=𝐴@𝑛
𝐸0;Γ⊢𝗌𝗋𝖼𝑛𝑎𝜔𝛽:𝐴
Src-Var
𝛽∉dom𝐸0𝑜𝑠∉domΓ∪ran𝐸0𝐸0,𝛽↦𝑜𝑠;Γ,𝑜𝑠:𝐴@𝑛⊢𝗌𝗋𝖼𝑛𝑟:𝐵
𝐸0;Γ⊢𝗌𝗋𝖼𝑛𝜆(𝑎𝛽:𝐴).𝑟:𝐴→𝐵
Src-Lam
𝐸0;Γ⊢𝗌𝗋𝖼𝑛𝑟1:𝐴→𝐵𝐸0;Γ⊢𝗌𝗋𝖼𝑛𝑟2:𝐴
𝐸0;Γ⊢𝗌𝗋𝖼𝑛𝑟1𝑟2:𝐵
Src-App
𝐸0;Γ⊢𝗌𝗋𝖼𝑛+1𝑟:𝐴
𝐸0;Γ⊢𝗌𝗋𝖼𝑛𝗊𝗎𝗈𝗍𝖾(𝑟):𝖢𝗈𝖽𝖾(𝐴)
Src-Quote
𝐸0;Γ⊢𝗌𝗋𝖼𝑛𝑟:𝖢𝗈𝖽𝖾(𝐴)
𝐸0;Γ⊢𝗌𝗋𝖼𝑛+1𝗌𝗉𝗅𝗂𝖼𝖾(𝑟):𝐴
Src-Splice
𝑀(𝑚)=(𝐴1,…,𝐴𝑘;𝐴;𝑛)𝐸0;Γ⊢𝗌𝗋𝖼𝑛𝑟𝑖:𝐴𝑖(1≤𝑖≤𝑘)
𝐸0;Γ⊢𝗌𝗋𝖼𝑛𝑚(𝑟1,…,𝑟𝑘):𝐴
Src-Macro
The first premise of Src-Macro checks a source call against its declared interface. It deliberately says nothing about the transformer output; that output is checked independently after expansion.
If 𝑥:ℕ@0, the raw quotation 𝗊𝗎𝗈𝗍𝖾(𝑥+1) is rejected: its body needs 𝑥 at phase one. If 𝑐:𝖢𝗈𝖽𝖾(ℕ)@0, then 𝗊𝗎𝗈𝗍𝖾(𝗌𝗉𝗅𝗂𝖼𝖾(𝑐)+1) is typed by one application of Q-Splice followed by Q-Quote. The splice marks the only permitted cross-phase dependency.
Proof. By induction on the typing derivation of 𝑒. The matching variable case has 𝑘=𝑛 by Q-Var and is exactly the second premise. A different variable is preserved by context substitution. The lambda and application cases apply the induction hypotheses under the unchanged phase. In Q-Quote, apply the induction hypothesis to the premise at 𝑘+1 and rebuild quotation. In Q-Splice, write 𝑘=𝑗+1, apply the induction hypothesis to the premise at phase 𝑗, and rebuild splice at phase 𝑘. The phase annotations rule out a variable occurrence at any unhandled phase. ◻
Let ̂Γ map expanded origins to declarations. The judgment 𝖢𝗈𝗆𝗉𝖺𝗍𝜌(𝐸0;Γ;¯𝐸;̂Γ) holds when 𝜌:domΓ→dom̂Γ is a bijection satisfying both of the following clauses:
dom𝐸0=dom¯𝐸, domΓ=ran𝐸0, and dom̂Γ=domΞ(¯𝐸). If 𝐸0(𝛽)=𝑜𝑠 and ¯𝐸(𝛽)=(𝑎,𝑆,𝑜ℎ), then 𝜌(𝑜𝑠)=𝑜ℎ=𝜋𝑜(¯𝐸)(𝛽);
for every 𝑜𝑠∈domΓ, ̂Γ(𝜌(𝑜𝑠))=Γ(𝑜𝑠).
Thus 𝜌 records an origin renaming, not an equality between source and expanded origins. For vectors, the judgments 𝐸0;Γ⊢𝗌𝗋𝖼𝑛⃗𝑟:⃗𝐴 and ̂Γ⊢𝑛⃗ℎ:⃗𝐴 mean that the corresponding judgment holds componentwise.
Suppose 𝖢𝗈𝗆𝗉𝖺𝗍𝜌(𝐸0;Γ;¯𝐸;̂Γ). The following two claims hold mutually.
If 𝑟 contains no macro call, 𝐸0;Γ⊢𝗌𝗋𝖼𝑛𝑟:𝐴, and 𝐷;¯𝐸;𝑛;𝐿;𝑈⊢𝑟⟼ℎ▹𝑈′, then ̂Γ⊢𝑛ℎ:𝐴 and 𝐷;Ξ(¯𝐸)⊢ℎ𝗉𝗋𝗈𝗏.
If every component of ⃗𝑟 contains no macro call, 𝐸0;Γ⊢𝗌𝗋𝖼𝑛⃗𝑟:⃗𝐴, and 𝐷;¯𝐸;𝑛;𝐿;𝑈⊢⃗𝑟⟼⃗ℎ▹𝑈′, then ̂Γ⊢𝑛⃗ℎ:⃗𝐴, and every component of ⃗ℎ satisfies provenance under 𝐷;Ξ(¯𝐸).
Proof of Theorem 116.10 — Mutual typing and provenance of macro-free expansion
Proof. Prove the term and vector claims by mutual induction on their expansion derivations, inverting the source-typing derivation in each term case. The origin bijection is the invariant that relates a source variable to the scoped reference produced from it.
Variable case. Let 𝐸0(𝛽)=𝑜𝑠. By compatibility, ¯𝐸(𝛽)=(𝑎,𝑆𝑏,𝜌(𝑜𝑠)) and ̂Γ(𝜌(𝑜𝑠))=Γ(𝑜𝑠)=𝐴@𝑛. The resolver premise of Exp-Var gives 𝗋𝖾𝗌𝗈𝗅𝗏𝖾Ξ(¯𝐸)(𝑎𝑆𝑏)=𝜌(𝑜𝑠). Hence Q-Var derives the target typing judgment, and the reference clause of provenance derives the second conclusion.
Binder case. Invert Src-Lam and Exp-Lam. Write 𝑜𝑠 for the source origin and 𝑜ℎ for the expanded origin chosen by the two rules. Their token and origin side conditions are 𝛽∉dom𝐸0,𝛽∉dom¯𝐸,𝑜𝑠∉domΓ∪ran𝐸0,𝑜ℎ∉domΞ(¯𝐸)∪Orig(𝐷). The first two conditions ensure that extending either token map adds one new key rather than overwriting a same-token binder. The latter two ensure that extending either origin context adds one new origin. Therefore 𝜌′=𝜌∪{𝑜𝑠↦𝑜ℎ} is a bijection between the extended context domains. It satisfies 𝖢𝗈𝗆𝗉𝖺𝗍𝜌′(𝐸0,𝛽↦𝑜𝑠;Γ,𝑜𝑠:𝐴@𝑛;¯𝐸,𝛽↦(𝑎,𝐿′,𝑜ℎ);̂Γ,𝑜ℎ:𝐴@𝑛). Apply the term induction hypothesis to the body with 𝜌′. It gives the body typing judgment under ̂Γ,𝑜ℎ:𝐴@𝑛 and provenance under Ξ(¯𝐸),𝑜ℎ↦(𝑎,𝐿′). Lambda introduction gives the target type, and the binder clause of definition 116.3 gives provenance for the whole lambda.
Application and stage cases. In Src-App/Exp-App, the two term induction hypotheses give the function and argument typings and their provenance judgments; application elimination and the application provenance clause give the conclusions. In Src-Quote/Exp-Quote, apply the term induction hypothesis at phase 𝑛+1, then apply Q-Quote and the quotation provenance clause. In Src-Splice/Exp-Splice, apply it at phase 𝑛, then apply Q-Splice and the splicing provenance clause. The macro-free hypothesis excludes Src-Macro/Exp-Macro.
Vector cases.Exp-Args-Nil gives the empty target vector and an empty family of provenance derivations. In Exp-Args-Cons, the term induction hypothesis gives both conclusions for the head. The vector induction hypothesis gives them for the tail under the threaded stamp set 𝑈1. Combining the componentwise judgments proves the vector claim. These cases exhaust both inductive definitions. ◻
A call 𝑚(⃗𝑟) with 𝑀(𝑚)=(⃗𝐴;𝐴;𝑛) is accepted relative to 𝐸0,Γ,¯𝐸,̂Γ only when there is a bijection 𝜌 such that 𝖢𝗈𝗆𝗉𝖺𝗍𝜌(𝐸0;Γ;¯𝐸;̂Γ),𝐸0;Γ⊢𝗌𝗋𝖼𝑛⃗𝑟:⃗𝐴, and expansion derives 𝐷;¯𝐸;𝑛;𝐿;𝑈⊢𝑚(⃗𝑟)⟼ℎ▹𝑈′. The ordinary staged checker then independently derives ̂Γ⊢𝑛ℎ:𝐴, and the accepted output must satisfy 𝐷;Ξ(¯𝐸)⊢ℎ𝗉𝗋𝗈𝗏. The target judgment is deliberately under ̂Γ, not the source-origin context Γ. Neither the target derivation nor provenance is stored in 𝐷, and neither is inferred from the macro signature.
If a transformer loops, no Exp-Macro derivation exists. If it returns 𝗍𝗋𝗎𝖾 where ℕ was declared, expansion may succeed but definition 116.11 rejects the call. Thus the local theorem proves exactly the macro-free inductive system; typed macro output crosses a separate checker boundary rather than an assumed principal transformer case.
A generated declaration contains a fresh global name 𝑐, a closed type 𝐴, and a term 𝑡. The command expander may propose the declaration only after hygienic expansion and staged checking. The environment is extended by 𝑐:𝐴 only when the ordinary kernel independently checks ⋅⊢𝑡:𝐴. Name freshness is checked against the finite global signature.
A derived command 𝖽𝖾𝗋𝗂𝗏𝖾𝖯𝖺𝗂𝗋𝖬𝖺𝗉(𝐴,𝐵) can therefore generate the declaration 𝗉𝖺𝗂𝗋𝖬𝖺𝗉:(𝐴→𝐴′)→(𝐵→𝐵′)→𝐴×𝐵→𝐴′×𝐵′ with the term that maps the two projections. Expansion convenience does not alter the kernel rule for products.
Proof of Corollary 116.13 — Generated declarations preserve kernel trust
Proof. Admission is defined to require exactly that derivation. Freshness ensures that checking the term cannot use the declaration being installed. ◻
The maximal-subset rule is executable because binding resolution enumerates a finite table and deletes dominated candidates. The binding table and the compile-time environment remain separate objects. The no-capture theorem is proved above for the smaller expander fixed in this chapter.
Generative and analytical macros. The expander above constructs syntax and never looks inside it. A macro that inspects its argument needs more: it must be told, at typing time, what a successful match makes available. The calculus that fixes this is separate from the expander above, and is frozen here.
Write 𝜆▴ for the comparison calculus. It is exactly the union of Figures 1–4 on printed pp. 5, 6, 8, and 11 of the companion technical report; no host-language feature is implicit. Its core signature is 𝑇::=𝐶∣𝑇→𝑇∣⌈𝑇⌉,𝑡::=𝑐∣𝑥∣𝜆𝑥:𝑇.𝑡∣𝑡𝑡∣𝖿𝗂𝗑𝑡∣⌈𝑡⌉∣⌊𝑡⌋∣𝑡𝑠𝗆𝖺𝗍𝖼𝗁⌈𝑡𝑝⌉𝗍𝗁𝖾𝗇𝑡𝑡𝖾𝗅𝗌𝖾𝑡𝑒∣𝖻𝗂𝗇𝖽(𝑥:𝑇;⃗𝑥:⃗𝑇),𝑝::=𝖽𝖾𝖿𝑥=⌈𝑡⌉𝗂𝗇𝑝∣𝖾𝗏𝖺𝗅𝑡,Γ::=⋅∣Γ,𝑥:𝑖𝑇,Σ::=⋅∣Σ,𝑥:𝑇,Ω::=⋅∣Ω,𝑥=𝑡. The notation 𝖻𝗂𝗇𝖽(𝑥:𝑇;⃗𝑥:⃗𝑇) is this book’s unambiguous print name for the report’s bind-pattern glyph. It binds the matched subterm of result type 𝑇 to 𝑥, permitting exactly the locally bound variables ⃗𝑥:⃗𝑇. It is a pattern form, not an ordinary term constructor. All variables are globally unique, terms are identified up to renaming, and 𝑖∈ℕ. The exact judgment signature is Γ∣Σ⊢𝑖𝑡:𝑇,Σ⊢𝑝:𝑇,𝑡→𝑖𝑡′,𝑝∣Ω→𝑝𝑝′∣Ω′,Γ𝑝∣Σ⊢𝑖𝑡𝑝:𝑇⊣Γ𝑡,𝑡𝑠◂𝑡𝑝/𝑡𝑡⟹Φ𝑡′𝑡. Figure 1 supplies precisely T-Const, T-Var, T-Abs, T-App, T-Fix, T-Quote, and T-Splice; E-App-1, E-Abs, E-Beta, E-App-2, E-Fix, E-Fix-Red, E-Quote, E-Splice, and E-Splice-Red; and the eight value rules V-Const, V-Abs-0, V-Quote, V-Var, V-Fix, V-Abs, V-App, and V-Splice. Figure 2 adds exactly T-Link, T-Eval, T-Def, V-Eval, E-Link, E-Eval, E-Macro, and E-Compile. Figure 3 adds exactly T-Match, the six pattern-typing rules T-Pat-Const through T-Pat-Bind, the five match congruence/selection rules, the six pattern-reduction rules E-Pat-Const through E-Pat-Bind, and V-Match. Figure 4 adds only the library-pattern rules T-Pat-Link and E-Pat-Link, while threading Σ and Ω through the preceding judgments. These inventories and the four cited figures fix every premise, side condition, and conclusion used below.
In particular, quotation raises the staging level, splicing lowers it, and T-Splice requires 𝑖≥1. A binding 𝑥:𝑖𝑇 is usable only at level 𝑖, so local cross-stage persistence is unavailable; library entries are stage-polymorphic through T-Link. Pattern typing returns the environment Γ𝑡 available only in the then branch. Patterns contain constants, variables, abstraction, application, 𝖿𝗂𝗑, and bind patterns, but no quotation, splice, or match form.
For the preservation theorem below, Definition 3.24 on printed p. 10 fixes the predicate 𝖶𝖥𝖲𝗎𝖻(Γ𝑝,Γ𝛿,Φ), printed in the source with the glyph ⊩: Φ is a bijection from dom(Γ𝑝) to dom(Γ𝛿), the two domains are disjoint, and whenever 𝑥𝑝:1𝑇∈Γ𝑝, one has Φ(𝑥𝑝):1𝑇∈Γ𝛿.
Two macros make the phase structure necessary. A generative macro 𝗉𝗈𝗐𝖢𝗈𝖽𝖾(𝑥,𝑛) with 𝑥:⌈ℕ⌉ and 𝑛:ℕ returns ⌈1⌉ when 𝑛 is zero and ⌈⌊𝑥⌋×⌊𝗉𝗈𝗐𝖢𝗈𝖽𝖾(𝑥,𝑛−1)⌋⌉ otherwise: it consumes an ordinary natural number and produces syntax. An analytical macro 𝗎𝗇𝗅𝗂𝖿𝗍𝖡𝗈𝗈𝗅(𝑥) with 𝑥:⌈𝟐⌉ matches 𝑥 against ⌈𝗍𝗍⌉ and ⌈𝖿𝖿⌉ and returns the corresponding element of 𝟐: it consumes syntax and produces an ordinary value. Their combination forces three distinct times. The library definition of 𝗉𝗈𝗐𝖢𝗈𝖽𝖾 is compiled and stored in Ω before any call site is examined; that is generation time. A call site ⌊𝗉𝗈𝗐𝖢𝗈𝖽𝖾(⌈𝑥⌉,2)⌋ is a top-level splice, so it is reduced one stage earlier than the program that contains it, and it is exactly there that 𝗎𝗇𝗅𝗂𝖿𝗍𝖡𝗈𝗈𝗅 may inspect a quoted argument; that is inspection time. The residual program is reduced last, at run time, by 𝖾𝗏𝖺𝗅. Collapsing inspection time into run time would let a match observe a value that generation has not yet produced; collapsing it into generation time would let a macro inspect a call site that does not exist yet.
For the exact syntax and rules fixed in convention 116.14, the source proves the following package.
Progress and preservation for terms at every staging level (Theorems 3.5 and 3.6). Progress is the closed statement: if ⋅⊢𝑖𝑡:𝑇, then 𝑡 is a value at level 𝑖 or steps at level 𝑖. Preservation is the open statement: if Γ⊢𝑖𝑡:𝑇 and 𝑡→𝑖𝑡′, then Γ⊢𝑖𝑡′:𝑇.
Progress and preservation for programs over a well-typed library (Theorems 3.14 and 3.15): a well-typed program is a value or steps together with its library, and stepping preserves the program’s type under an extended signature.
Preservation of pattern reduction (Theorem 3.25, printed p. 10) has exactly the five premises (1)Γ;Γ𝛿⊢1𝑡𝑠:𝑇1,(2)Γ𝑝⊢1𝑡𝑝:𝑇1⊣Γ𝑡,(3)Γ;Γ𝑡⊢0𝑡:𝑇,(4)𝑡𝑠◂𝑡𝑝/𝑡⟹Φ𝑡′,(5)𝖶𝖥𝖲𝗎𝖻(Γ𝑝,Γ𝛿,Φ). Its conclusion is Γ⊢0𝑡′:𝑇. Premise (5) has exactly the bijection, disjoint-domain, and level-one type clauses printed in convention 116.14. Preservation for a whole match is Corollary 3.26.
Proof. We reconstruct the progress and preservation inductions for the rule inventory of convention 116.14.
First prove substitution: if Γ∣Σ⊢𝑗𝑢:𝐴 and Γ,𝑥:𝑗𝐴∣Σ⊢𝑖𝑡:𝐵, then Γ∣Σ⊢𝑖𝑡[𝑢/𝑥]:𝐵. Induct on the second typing derivation. The variable case returns 𝑢 when the variable is 𝑥 and uses lookup otherwise. Under abstraction, quotation, splice, and pattern binders, alpha-rename the binder and apply the induction hypothesis in the extended context. Application and match use the induction hypotheses for all term premises; constants, links, and pattern constants are unchanged. Fixpoint uses the induction hypothesis on its function premise. These cases cover all term forms in the card. Repeating the lemma for a finite list of distinct variables gives simultaneous substitution.
For term progress strengthen the claim to a context containing only bindings at levels at least 1. Induct on term typing. A constant is a value. A variable at level 0 cannot be found in the restricted context; at a positive level it is a value. At level 0, an abstraction is a value; at a positive level its body is a value or steps by induction, yielding V-Abs or E-Abs. For application at level 0, step the left operand, then the right; if both are values, canonical forms makes the left operand an abstraction and E-Beta steps by substitution. At a positive level, either component steps by the corresponding congruence rule or both are values and V-App applies. A level-zero fixpoint either steps inside or its value argument is an abstraction and E-Fix-Red unfolds it; at a positive level it is a value exactly when its argument is. Quotation applies the induction hypothesis one level higher and is otherwise a value. A splice cannot be typed at level 0. At level 1, its operand steps at level 0, or canonical forms makes it a quotation and E-Splice-Red applies; above level 1, it steps inside or is a staged value. For a match, first step the scrutinee; when it is a quotation, the six pattern-reduction rules either produce a successful substituted then-branch or select the else branch, and a positive-stage match is otherwise a value. A library link at level 0 steps by E-Link because well-typedness of the library supplies its definition; at positive level it is a value. These are the Figure 1–4 typing families. Taking the restricted context empty proves term progress.
For term preservation, induct on typing and invert the step. Congruence steps for abstraction, application, fixpoint, quotation, splice, and match rebuild the same typing rule from the induction hypothesis. Beta and fixpoint unfolding use substitution. Splice–quotation reduction inverts T-Quote under T-Splice. A link step uses the definition typing in the well-typed library. The only remaining reduction is successful pattern reduction; it is handled below. Thus every term step preserves its type.
For program progress, induct on Σ⊢𝑝:𝑇. In 𝖾𝗏𝖺𝗅𝑡, term progress either steps 𝑡 by E-Eval or makes the program a value. In 𝖽𝖾𝖿𝑥=⌈𝑡⌉𝗂𝗇𝑝, term progress at level 1 either steps the definition body by E-Macro, or makes it a value and E-Compile extends Ω with 𝑥=𝑡. Program preservation uses term preservation in the first alternatives. In the compile alternative extend Σ with 𝑥:𝑇𝑥; weakening types the residual program under the extension, and the typing of 𝑡 makes the extended library well typed. This proves both program theorems.
It remains to prove pattern preservation. Induct on the pattern-typing derivation, retaining all five premises printed in item 3. A pattern constant inverts the scrutinee constant and returns the already typed branch. A pattern variable adds no then-branch binding and leaves the branch unchanged. A library pattern is disjoint from the local pattern domain by 𝖶𝖥𝖲𝗎𝖻, so only E-Pat-Link applies and again leaves the branch unchanged. For abstraction, application, and fix patterns, inversion of the scrutinee typing exposes exactly the component typings required by the recursive pattern premise; apply the induction hypotheses in the order of the pattern-reduction derivation and rebuild the enclosing typing. For a bind pattern, the bijection clause of 𝖶𝖥𝖲𝗎𝖻 pairs every level-one pattern variable with a distinct level-one scrutinee variable of the same type. The branch premise is therefore well typed after the simultaneous substitution supplied by the first paragraph. Disjointness prevents capture, and the domain equality shows that every branch binding is replaced exactly once. Hence the reduced branch has type 𝑇 at level 0.
Those are the six pattern forms, so pattern reduction is preserving. Inverting T-Match and applying this result proves preservation for a whole match, completing term preservation and item 3. The proof used only the frozen calculus. It states no hygiene result, no result for definition 116.6, and no product-macro theorem. ◻
Two boundaries hold. The theorem concerns the printed calculus and proves no compiler-correctness statement. Analytical inspection is not unrestricted reflection: the pattern-typing judgment matches only the simply typed fragment with 𝖿𝗂𝗑, and what the 𝗍𝗁𝖾𝗇 branch may use is exactly the output environment Γ𝑡, so a match cannot decompose a quotation, a splice, or a term whose bindings it did not itself introduce.
Sources
The maximal-subset resolver and the separation between binding tables and compile-time environments follow Flatt’s executable model [Fla16]. The 𝜆▴ card follows Stucki, Brachthäuser, and Odersky [SBO21]; its rules are Figures 1–4 on printed pp. 5, 6, 8, and 11 of the companion technical report.
Suggested first pass.
None of these problems is a prerequisite for a later chapter. Begin with exercise 116.2, then complete exercise 116.4.
★★☆ Let Γ0=(𝑐:𝖢𝗈𝖽𝖾(ℕ)@0). Draw the complete inference tree, including the ordinary addition premises, for Γ0⊢0𝗊𝗎𝗈𝗍𝖾(𝗌𝗉𝗅𝗂𝖼𝖾(𝑐)+1):𝖢𝗈𝖽𝖾(ℕ). Every node must display its phase and type. Next use the context Γ1=(𝑑:𝖢𝗈𝖽𝖾(ℕ)@1). Draw the complete nested tree for Γ1⊢0𝗊𝗎𝗈𝗍𝖾(𝗊𝗎𝗈𝗍𝖾(𝗌𝗉𝗅𝗂𝖼𝖾(𝑑))):𝖢𝗈𝖽𝖾(𝖢𝗈𝖽𝖾(ℕ)). The innermost variable premise is at phase one, the splice conclusion is at phase two, and the two quotation conclusions are at phases one and zero. Finally, for Γ𝑥=(𝑥:ℕ@0), draw the attempted tree for 𝗊𝗎𝗈𝗍𝖾(𝑥+1) and identify its missing judgment Γ𝑥⊢1𝑥:ℕ. A list of rule names is not a complete tree.
★★★ In 𝜆▴ as fixed in convention 116.14, write the analytical macro 𝗎𝗇𝗅𝗂𝖿𝗍𝖡𝗈𝗈𝗅 with both branches. Give the pattern-typing judgment of each pattern together with its output environment, state the result type of the 𝗍𝗁𝖾𝗇 and 𝖾𝗅𝗌𝖾 branches, and exhibit one pattern that the pattern-typing judgment rejects because it is outside the simply typed fragment the source permits.
★★★Practical project.scoped-hygienic-macro-expander Implement in Agda or Kappa scoped identifiers, maximal-subset resolution, the 𝗍𝗐𝗂𝖼𝖾 transformer, and a finite executable observation model for the quotation/splicing fragment. The observation model has naturals, Booleans, functions, quotation, splicing, a dedicated generative-double form, and a dedicated analytical-Boolean form. Those last two forms are test probes, not constructors of the exact 𝜆▴ card in convention 116.14; the project therefore does not implement the card’s libraries, 𝖿𝗂𝗑, general bind patterns, or pattern typing. Keep 𝐸0:𝛽↦𝑜𝑠 distinct from ¯𝐸:𝛽↦(𝑎,𝑆,𝑜ℎ), and check a finite bijection 𝜌 such that 𝜌(𝐸0(𝛽))=𝜋𝑜(¯𝐸)(𝛽). Provenance traversal must extend its origin environment beneath each constructed binder. Introduced stamps avoid all used and input-identifier stamps; constructed origins avoid caller and exported origins; variables occur only at their recorded phase.
Print the following input–output pairs:
caller-x
caller-reference-preserved
nested-x
introduced-reference-local
ambiguous
rejected: ambiguous-binding
origin-renaming
origin-renaming-preserved
nested-transformer-binders
constructed-binder-provenance
nested-quotation
nested-quotation-typed
generated-double
generated-normal-form
analyse-true
analytical-true-branch
ill-staged
rejected: ill-staged
beta-collision
beta-capture-avoided
Also run the absent-target counteroracle 𝗌𝗎𝖻𝗌𝗍2(𝗏𝖺𝗋1)(𝜆1:ℕ.𝗏𝖺𝗋1). It must print absent-target-alpha-preserved; the fresh-name bound must include the substitution target as well as the replacement and body. The nested quotation must be the exact tree from exercise 116.2. Mutating copied syntax with the introduction stamp, omitting the provenance extension, ignoring a variable’s phase, or reversing the true analytical branch must make the corresponding oracle fail. The program checks this finite observation model; it is not a mechanization of the calculus-wide progress and preservation proof. Its two probe constructors do not implement general lambda-triangle patterns.