Lectures onType Theory
Chapter 129
Chapter 129Optional

Typed Multi-Stage Programming and an Evidence-Bounded Interpreter-Tower Case Study

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

The untyped generator pow(1,x)=x and pow(n+1,x)=xpow(n,x) should turn a known exponent three into future code x(xx). If quotation captures a generation-time variable, however, the alleged program may contain a free name after the generator returns. Staging needs a typing judgment that records which assumptions survive quotation.

The modal box closes future code

Fix simple types A,B::=bABA and terms M,N::=xλx:A.MMNbox Mlet box u=M in N. The judgment Δ;ΓM:A separates modal assumptions u::AΔ, available under boxes, from ordinary assumptions x:AΓ, available only at the present stage.

x:AΓ
Δ;Γx:A
T-Var
u::AΔ
Δ;Γu:A
T-MVar
Δ;Γ,x:AM:B
Δ;Γλx:A.M:AB
T-Abs
Δ;ΓM:ABΔ;ΓN:A
Δ;ΓMN:B
T-App
Δ;M:A
Δ;Γbox M:A
T-Box
Δ;ΓM:AΔ,u::A;ΓN:B
Δ;Γlet box u=M in N:B
T-LetBox

This is the pure dual-context modal-S4 calculus Tstage. Rule T-Box empties the ordinary context. It accepts u::b;boxu:b, but rejects ;x:bboxx:b. The rejection is the scope-extrusion counterexample, not a missing coercion.

Code values are box M. Staged reduction has two contractions and five congruence families:

(λx:A.M)NM[N/x]
TS-Beta
let box u=box M in NN[M/u]
TS-BoxBeta
MM
λx:A.Mλx:A.M
TS-Lam
MM
MNMN
TS-AppL
NN
MNMN
TS-AppR
MM
let box u=M in Nlet box u=M in N
TS-LetL
NN
let box u=M in Nlet box u=M in N
TS-LetR

There is no congruence beneath box: its body is future syntax.

The modal eta expansion is M:Alet box u=M inboxu, with u fresh. Its typing derivation first applies T-LetBox; in the body, T-MVar derives u:A and T-Box closes the empty ordinary context. Thus eta expresses local completeness of elimination followed by introduction. It is an expansion principle, not a staged evaluation step.

If multiplication is a modal constant m::bbb, repeated T-LetBox constructs u::b;box(mu(muu)):b. Each occurrence of u is modal. Replacing it by an ordinary x:b invalidates the premise of T-Box.

Lemma 129.1 — Dual substitution

The following substitutions are admissible. Δ;Γ,x:AM:B,Δ;ΓN:AΔ;ΓM[N/x]:B,Δ,u::A;ΓM:B,Δ;N:AΔ;ΓM[N/u]:B.

Proof of Lemma 129.1 — Dual substitution

Proof. For ordinary substitution, induct on the derivation of M. The variable case uses the second premise when its variable is x. Under T-Box, the premise has empty ordinary context, so x cannot occur; weakening the unchanged premise concludes the case. Abstraction and application use their induction hypotheses. In T-LetBox, apply them to both premises after alpha-renaming the modal binder away from N.

For modal substitution, use the same induction. Rule T-MVar uses the closed ordinary-context premise when its variable is u. Rule T-Box admits that premise because modal assumptions remain available under a box. The binder case is handled by alpha-renaming. These are all six rule families. ◻

Call a subterm occurrence persistent when it lies beneath a box; other occurrences are eliminable. A term is irreducible when none of the seven staged rules applies. In the labelled syntax, labels are inert metadata: a contraction transports labels on retained syntax and on every copied substitution instance, and no operational rule creates or erases a label.

Theorem 129.2 — Local eliminability and inert-label persistence

If ;M:A, MM, and M is irreducible, then every occurrence of the body N, viewed as an occurrence in M=box N, is persistent; the root occurrence box N is not asserted to lie beneath itself. Moreover, extend terms by inert labels M, label every persistent occurrence of a well-typed M, and suppose MM. Every persistent occurrence of M still carries .

Proof of Theorem 129.2 — Local eliminability and inert-label persistence

Proof. Induction on the finite reduction sequence, using the two substitution clauses at the two contractions, gives ;M:A. A simultaneous induction on a closed irreducible typing derivation gives the needed canonical-form fact. The variable case is impossible. An abstraction has arrow type. In an application, the induction hypothesis makes a closed irreducible function of arrow type an abstraction, so beta would apply. In a let-box term, the induction hypothesis makes its irreducible scrutinee a box, so let-box beta would apply. Therefore a closed irreducible term of type A is box N. Every occurrence belonging to its body N lies below that box.

For persistence, induct on the staged reduction. In ordinary beta, the modal typing restriction prevents the substituted ordinary variable from occurring beneath a box; this follows by induction on the body typing derivation. In let-box beta, every occurrence of the substituted code body was labelled in the boxed premise. Congruence preserves labels by the induction hypothesis. There is no box congruence, so no reduction changes a labelled occurrence already beneath a box. These are the two contractions and five congruence families; labels add no reduction rule. This is a theorem of the displayed unlabelled dual-context calculus extended with inert occurrence metadata. Davies and Pfenning’s source calculus also has labelled expressions E, an explicit unlabelling contraction, and labelled case analysis. Its Theorems 5–6 prove eliminability and persistence for that richer operational system [DP01]; the local theorem neither imports nor silently omits those additional rules. ◻

Theorem 129.3 — Subject reduction and scope safety

If Δ;ΓM:A and MM, then Δ;ΓM:A. Hence a closed term of type A that evaluates to box N has ;N:A; generated code has no escaped ordinary variable.

Proof of Theorem 129.3 — Subject reduction and scope safety

Proof. Induct on the reduction derivation. Beta uses ordinary substitution. Let-box beta uses modal substitution. Compatible cases rebuild their typing rule with the induction hypothesis. If a closed term evaluates to a code value, preservation gives ;box N:A; inversion of T-Box gives the conclusion. ◻

Exercise 129.1

★☆☆ The term λx:b.boxx fails to type. Repair it by taking a boxed argument and using T-LetBox. Give the complete derivation and the two-step reduction sequence on argument boxc, identifying the TS-BoxBeta step.

Related staging judgments do not share rules

MetaML-style quotation and escape use stage-indexed contexts and may include cross-stage persistence. A sound CSP rule must restrict the persisted values; persisting a mutable reference would let future code retain a cell from a completed run. Tstage has no CSP rule, reference, or run operator, so theorem 129.3 says nothing about that extension. For comparison only, fix the pure two-stage MetaML fragment with τ::=bττCodeατ, stage words A, and judgments ΓAM:τ. Its deliberately restrictive persistent-value predicate is generated by

cb is a closed immutable literal
Pers(cb:b)
Pers-Base
AMβ:CodeβτLoc(M)=
Pers(Mβ:Codeβτ)
Pers-Code

Here Loc(M) is the finite set of store locations occurring in M; it is empty throughout the pure fragment. There is no persistent function clause. The three-rule delta is

ΓAαM:τ
ΓAMα:Codeατ
MML-Quote
ΓAM:Codeατ
ΓAααM:τ
MML-Escape
ΓAv:τPers(v:τ)
ΓAα%αv:τ
MML-CSP

The side condition is load-bearing. If it admitted locations, then let r=ref 0 in get(%αr)α would return code containing r after the allocating run ended. The two constructors of Pers reject that location. This card states no store theorem; it exposes one exact safe fragment and the smallest premise that a reference-bearing extension would have to replace with a world-indexed store invariant.

The source comparison is not a renaming of Tstage. Davies and Pfenning’s two-level Mini-ML has run-time types τ::=natτττ×τ1 and compile-time types σ::=natσσσ×σ1τ. Terms have separately underlined run-time and overlined compile-time constructors for variables, abstraction, application, fixed points, pairs, projections, unit, zero, successor, and natural-number case. The two judgments are Δ;Γre:τ and Δce:σ. For p{r,c}, every constructor has the following phase-indexed schema (with Cr=Δ;Γ, Cc=Δ, and a binder added to the corresponding context):

x:TCp
Cppx:T
2-Varp
Cp,x:T1pe:T2
Cppλpx:T1.e:T1T2
2-Lamp
Cppe1:T1T2Cppe2:T1
Cppe1@pe2:T2
2-Appp
Cp,x:Tpe:T
Cppfixpx:T.e:T
2-Fixp
Cppe1:T1Cppe2:T2
Cppe1,e2p:T1×T2
2-Pairp
Cppe:T1×T2
Cppprojip(e):Ti
2-Projp
Cppp:1
2-Unitp
Cppzp:nat
2-Zerop
Cppe:nat
Cppspe:nat
2-Succp
Cppe0:natCppez:TCp,x:natpes:T
Cppcasep e0 of zezsxes:T
2-Casep

The two phase-transition rules are the entire rule delta:

Δce:τ
Δ;Γre:τ
2-Down
Δ;re:τ
Δce:τ
2-Up

The mutually recursive translation on run-time syntax and || on compile-time syntax is homomorphic on each phase’s constructors, maps |τ| to τ, and has the decisive equations e=unbox1|e|,|e|=boxe. Here the target is the source’s implicit multi-world Mini-ML, whose unbox1 discharges one context-stack boundary. Write SiM:A for that target judgment: S is the stack of ordinary contexts, variables are selected from its active component, box pushes an empty component, and unbox1 pops one component. Write S,x:T for extension of its active component. The function and modal rules are

x:Tlast(S)
Six:T
I-Var
S,x:TiM:U
Siλx:T.M:TU
I-Abs
SiM:TUSiN:T
SiMN:U
I-App

For every ordinary context Γ, the two boundary rules are

S;iM:T
SiboxM:T
I-Box
SiM:T
S;Γiunbox1M:T
I-Unbox1

Thus I-Box enters one future world and I-Unbox1 uses code from its immediate predecessor; the latter is not an unindexed run operation. The remaining constructor schemas are

S,x:TiM:T
Sifix x:T.M:T
I-Fix
SiM:T1SiN:T2
SiM,N:T1×T2
I-Pair
SiM:T1×T2
Siprojj(M):Tj
I-Proj
Si:1
I-Unit
Siz:nat
I-Zero
SiM:nat
SisM:nat
I-Succ
SiM:natSiMz:TS,x:natiMs:T
Sicase M of zMzsxMs:T
I-Case

These displayed schemas are every target family used by the translation. This is the exact JACM card on printed pages 35–40; the term translation is on printed page 39 and the embedding theorem is on printed page 40. It is not the dual-context calculus above.

Theorem 129.4 — Two-level embedding

Both directions hold simultaneously: Δ;Γre:τ|Δ|;Γie:τ,Δce:σ|Δ|i|e|:|σ|. On each reverse implication, the target type is in the image of the displayed type translation. This theorem concerns typing; the two-level source has no direct reduction semantics in the selected presentation.

Proof of Theorem 129.4 — Two-level embedding

Proof. This is an exact import of Davies–Pfenning Theorem 15 [DP01]. The forward and reflection directions are proved simultaneously by structural induction on the two translations. Every homomorphic constructor uses the corresponding source and target rule plus its induction hypotheses. The only nonhomomorphic cases are 2-Down and 2-Up: the former translates to unbox1, and target inversion recovers the compile-time premise; the latter translates to box, and target inversion recovers the empty run-time context. Those inversions also prove that a target type in the translation image reflects to the unique source phase type. These cases and the homomorphic schema cover every printed rule family. ◻

For a self-contained arithmetic comparison, fix annotated terms a::=nSxsSxdDa1+ba2a1ba2,b{S,D}. An S-operation requires two S-operands. A D-operation accepts either binding time and embeds a computed numeral as a residual numeral. The formation judgment sAa:b, where s is defined on every static variable, has rules

sAnS:S
A-Nat
xsdom(s)
sAxsS:S
A-SVar
sAxdD:D
A-DVar
sAa1:SsAa2:S
sAa1Sa2:S
A-Op-S
sAa1:b1sAa2:b2
sAa1Da2:D
A-Op-D

Define specializeA by recursion on a derivation of that judgment: specializeA(nS,s)=val(n),specializeA(xsS,s)=val(s(xs)),specializeA(xdD,s)=code(xd),specializeA(a1Sa2,s)=val(n1n2),specializeA(a1Da2,s)=code(q1q2), where {+,}, specializeA(ai,s)=val(ni) in the static equation, specializeA(ai,s)=qi in the dynamic equation, val(n)=n, and code(r)=r. Its annotation erasure |a|A removes superscripts. This grammar is the nonrecursive, conditional-free arithmetic sublanguage of the Scheme0 source card definition 127.1: xsS marks a variable in the static environment, xdD marks a residual variable, and the superscript on an operator is its binding-time annotation. No Scheme0 call, conditional, memo-table operation, or annotation inference is translated here.

For a residual arithmetic term r, define its Tstage body rA by mapping a numeral n to a modal numeral constant n¯::b, a residual variable xd to the modal variable xd::b, and r1r2 to ¯r1Ar2A, where ¯::bbb is modal. For a specialization result q, let Δd(a,q) contain n¯::b for every numeral n occurring in q, ¯::bbb for every operator occurring in q, and xd::b for every dynamic variable of a. This context is finite because a and q are finite syntax. Put codeA(r):=boxrA and use the meta-level observation execA(codeA(r),d)=nevalA(r,d)=n. Neither execA nor evalA is a reduction rule of Tstage.

Proposition 129.5 — Type preservation for the Scheme0 arithmetic bridge

Suppose sAa:bA and specializeA(a,s)=q, where bA{S,D}. Then Δd(a,q);qA:bandΔd(a,q);codeA(q):b. Thus every result of the displayed Scheme0-fragment specializer translates to closed-ordinary-context Tstage code; the only free target names are the modal residual variables and modal arithmetic constants recorded in Δd(a,q).

Proof of Proposition 129.5 — Type preservation for the Scheme0 arithmetic bridge

Proof. Induct on the derivation of sAa:bA, retaining the equation that defines q. A numeral specializes to val(n), and a static variable specializes to val(s(xs)); T-MVar types the corresponding modal numeral constant at b. A dynamic variable specializes to code(xd), and T-MVar types xd:b.

In A-Op-S, both operands specialize to recorded numerals and the result is val(n1n2), so the numeral-constant case applies. In A-Op-D, the two induction hypotheses type q1A and q2A at b. Rule T-MVar types ¯:bbb; two uses of T-App give the translated residual operation type b. These are all formation rules. In every case, T-Box applies because the ordinary context is empty, yielding the second judgment. ◻

Lemma 129.6 — Arithmetic specialization for both binding times

Suppose sAa:b, specializeA(a,s)=q, and d assigns every dynamic variable of a. Whenever the arithmetic on either side is defined, evalA(|a|A,(s,d))=evalA(q,d). Here a residual numeral evaluates to itself, so the right side also covers q=val(n).

Proof of Lemma 129.6 — Arithmetic specialization for both binding times

Proof. Induct on the displayed binding-time derivation, with the assertion quantified over its conclusion b and output q. A numeral evaluates to itself. A static variable is looked up in s, and a dynamic variable is looked up in d; these are exactly their specialized outputs. For A-Op-S, both induction hypotheses yield the recorded numerals, and specialization computes the same primitive. For A-Op-D, apply the induction hypothesis separately to each premise, whether its conclusion is S or D. Erasure evaluates the primitive on those two values, while specialization embeds each val(ni) as the residual numeral ni and leaves each code(ri) as ri. The residual primitive therefore receives the identical pair of values. These are all three leaves, both primitive symbols, and both operation rules. ◻

Proposition 129.7 — Annotated arithmetic commuting instance

Suppose sAa:D and specializeA(a,s)=code(r). For every dynamic environment d, execA(codeA(r),d)=evalA(|a|A,(s,d)) whenever the displayed primitive arithmetic operations are defined.

Proof of Proposition 129.7 — Annotated arithmetic commuting instance

Proof. Apply lemma 129.6 with q=code(r). Its right side is evalA(r,d), which is equivalent by definition to execA(codeA(r),d). Symmetry gives the displayed orientation. The primitive-definedness condition is unchanged. ◻

Tagless-final staging is another interface. Begin with the initial syntax t::=lit(n)var(x)mul(t,t). For an environment d, its evaluator has the three equations evaltf(lit(n),d)=n,evaltf(var(x),d)=d(x),evaltf(mul(t,u),d)=evaltf(t,d)evaltf(u,d). Its serializer is the fold that maps a literal to its decimal numeral, a variable to its name, and multiplication to the parenthesized concatenation (s*r). These are two algebras for the same signature lit:NR,var:VarR,mul:RRR.

The final representation of an initial tree t is the polymorphic operation t^ that accepts such an algebra and returns its interpretation. The evaluator and serializer are therefore added one at a time by choosing R=(VarN)N and R=String, respectively. Structural induction gives their exact common representation property.

Proposition 129.8 — Pure tagless-final representation

For every initial arithmetic tree t, algebra A=(R,lit,var,mul), and tagless encoding t^, foldA(t)=t^(A).

Proof of Proposition 129.8 — Pure tagless-final representation

Proof. Induct on t. A literal and a variable use the corresponding algebra operation on both sides. A multiplication node applies the induction hypotheses to its two children and then the same mul operation. These are all three constructors. ◻

Now add a partial-evaluation algebra. Its carrier is q::=known(n)later(t),reify(known(n))=lit(n),reify(later(t))=t. Literals are known, variables are later, and multiplication computes on two known inputs: mulpe(known(n1),known(n2))=known(n1n2). For every other pair (q1,q2), it residualizes: mulpe(q1,q2)=later(mul(reify(q1),reify(q2))). For t0=mul(lit(2),mul(lit(3),lit(4))), evaluation returns 24, serialization returns (2*(3*4)), and partial evaluation returns known(24). For t1=mul(lit(2),mul(var(x),lit(3))), partial evaluation returns the same tree under later; the algebra performs no reassociation rule.

Proposition 129.9 — Pure tagless-final partial-evaluation equation

Suppose t^(Ape)=q. For every environment d assigning every variable of t, evaltf(reify(q),d)=evaltf(t,d).

Proof of Proposition 129.9 — Pure tagless-final partial-evaluation equation

Proof. Induct on t. Literal and variable cases are their defining equations. For multiplication, apply both induction hypotheses. When both outputs are known, their recorded naturals are the two source evaluations, so multiplying them is sound. Otherwise reification rebuilds multiplication from two reified outputs, and the evaluator uses the induction hypotheses componentwise. These are the two partial-evaluator alternatives. ◻

The course examples separately calculate evaluation, serialization, and de Bruijn partial evaluation. Those examples concern the displayed tagless final algebra; they neither represent arbitrary Tstage terms nor prove a host optimizer correct [CKcS09].

The optimizing evaluators of Wei, Tan, and Zhong form another separate card [WTZ26]. Their binding-time-annotated object language is interpreted successively by a stateful evaluator, a one-continuation evaluator, a two-continuation evaluator, a delimited-control evaluator, and a CEKM machine. The two continuations separate the rest of the generated expression from the boundary at which pending bindings are inserted. The optimizing evaluator then composes seven independently selectable reflection clauses in this priority order: atom, inline, fold, partialStatic, simplifyCond, CSE, DCE, followed by a default clause which introduces a fresh residual let-binding. The ordering is part of this implementation card: changing it can expose a different expression to a later clause.

For example, apply only the CSE clause to the dynamic expression (23)+(23). The first multiplication is inserted as x0. The cache maps the second, syntactically equal pure multiplication to the same x0, and the default clause names the sum. The printed result is let x0=23 in let x1=x0+x0 in x1. The calculation establishes only the displayed result. It does not prove contextual equivalence of the evaluator families, soundness of every optimization, or compiler correctness for LMS, MetaOCaml, or Tstage.

To connect the final interface back to the modal card, define one modal literal constant n::b for each numeral, a modal variable constant x::b for each object variable, and a modal multiplication constant m::bbb. Then define G(lit(n))=boxn,G(var(x))=boxx,G(mul(t,u))=let box x=G(t) in let box y=G(u) in box(mxy). The two let-boxes sequence the generator’s recursive results; modal substitution yields G(mul(lit(2),lit(3)))box(m23). The modal calculus treats these constants symbolically; it contains no numeral, variable-lookup, or multiplication reduction. Changing the outer sequencing order changes the evaluation order of the generators even though the final pure code is alpha-equivalent. With effectful generators, order must instead be preserved by naming the left result before evaluating the right: let a=G(t) in let b=G(u) in combine(a,b). This is a let-insertion calculation, not an equation that permits reordering.

MetaOCaml’s code types, quotation, escape, run, CSP, and let insertion form one system card. Lightweight modular staging instead represents object operations by a host interface and uses host normalization plus explicit code combinators [RO10]. These cards differ from the modal calculus in their syntax, typing judgments, and reduction relations. Neither supplies a theorem about Tstage: such a transfer would require a typed translation and a simulation theorem.

MacoCaml is a third card. Its core types and expressions include τ::=IntUnitττRefIntCodeτ,e::=i()xλx:τ.eeerefe!ee:=ee$e. The source elaboration judgment is σ1;Ω;Γne:τe;σ2,{c,s,q}. It records the input heap σ1, compile-time evaluation context Ω, type context Γ, integer level n, compiler mode , elaborated core expression e, and output heap σ2. A local variable declaration records the same level at which it may be used. Mode c is ordinary compilation, s is compile-time computation inside a top-level splice, and q is quotation. The staging rules are

σ1;Ω;Γn+1qe:τe;σ2
σ1;Ω;Γncse:Codeτe;σ2
MC-Quote
σ1;Ω;Γn1se:Codeτe;σ2
σ1;Ω;Γnq$e:τ$e;σ2
MC-Splice

Here cs abbreviates one instance for each of the two modes. A top-level splice instead uses compile-time evaluation:

σ1;Ω;Γn1se:Codeτe;σ2σ2;Ωe0v;σ3
σ1;Ω;Γnc$e:τv;σ3
MC-CodeGen

Runtime definitions def k=e bind k at level zero; macro definitions defm=e bind m at level minus one. A top-level splice is the compilation interface: typing interleaves with its evaluation, requires a quoted result, and inserts the quoted body into the compiled module. A compiled module contains no top-level splice.

Composition shifts levels explicitly. Importing a module at level zero makes its runtime definitions available at level zero; an import shifts them to level minus one for compile-time use. Macro-bearing modules compose through those leveled imports rather than through textual substitution. Quotation retains binding information. Splice elaboration chooses each binder outside the identifiers occurring in the caller module, quoted value, and generated body before binder insertion. In the paper’s power example the generated parameter is therefore x1, which does not occur in the caller; the caller’s x cannot be captured. The compiled code for exponent five is λx1.x1(x1(x1(x1(x11)))), and applying it to three returns 243.

Theorem 129.10 — MacoCaml elaboration soundness

Suppose Γ ok, σ1 ok, and σ1;ΓcΩ. If σ1;Ω;Γne:τe;σ2, then the core judgment under the empty heap is ;Γne:τ. Moreover, the elaborated expression has source level shape e1 when =q, and e0 otherwise. Thus no location allocated in the compile-time heap is free in compiled core code.

Proof of Theorem 129.10 — MacoCaml elaboration soundness

Proof. This is the exact expression instance of the source elaboration-soundness theorem [XWNY23]. Its simultaneous induction over expression and structure elaboration uses preservation for the evaluation premise of MC-CodeGen; the level-shape conclusion prevents the returned quotation body from capturing a location at the negative typing level. The source proves the corresponding module and structure instances by the same simultaneous induction. ◻

Preservation, progress, and phase distinction belong to this exact level-indexed calculus [XWNY23]. Its quotation/composition interface is neither MetaOCaml execution nor T-Box, so those proofs strengthen neither theorem 129.3 nor theorem 129.4.

The pure Tan–Wei calculus λ|2| is a fourth system card [TW26]. It freezes stages s{1,2} and the surface grammar t::=()nxλx.tlet x=t in tlift trun tapps(t,t)fixstifzs(t,t,t)tst. Evaluation adds the administrative forms and values g::=code treflect tletc x=t in tλcx.t,v::=n()λx.tcode t. A pure evaluation context E contains the ordinary left-to-right application, operation, fixed-point, conditional, lift, and let frames. A reification context P may additionally contain λcx.[],letc x=t in[],run[],ifz2(v,[],t),ifz2(v1,v2,[]). The decisive head step, for a name x fresh for P,E,t, is P[E[reflect t]]P[letc x=t in E[code x]]. Thus a reflected fragment is named once at its reification boundary. The freshness premise and the restriction of E to ordinary pure frames are what preserve binding and left-to-right evaluation order.

The static card has ϵ::=,τ::=unitnatτ1ϵτ2rep(τ)frag(τ),Γ::=Γ,xs:τ. The judgment Γst:τϵ checks a term at a specified stage. The reification judgment Γt:τϵ either embeds a pure stage-one term or packages a fragment as complete code:

Γ1t:τ
Γt:τ
TW-Pure
Γ1t:frag(τ)ϵ
Γt:rep(τ)ϵ
TW-Rep

The rules for code, reflect, and inserted bindings expose the control effect:

Γ2t:τ
Γ1code t:rep(τ)
TW-Code
Γ2t:τ
Γ1reflect t:frag(τ)
TW-Reflect
Γ2t1:τ1Γ,x2:τ1t2:rep(τ2)ϵWF2(τ1)
Γ1letc x=t1 in t2:rep(τ2)
TW-LetC

In particular, complete code may contain divergence or a generated-stage effect and therefore is not treated as an unrestricted pure fragment. The remaining surface rules propagate the join of premise effects; every stage-two code-producing operator concludes with effect . Function types at stage two require latent effect .

Stage erasure |t| changes every stage annotation to 1, deletes lift, run, code, reflect, and λc, maps inserted letc to ordinary let, and erases rep, frag, and latent effects. For stage-erased types, define the step-indexed value and term interpretations by V[[unit]]={(k,(),())kN},V[[nat]]={(k,n,n)k,nN},(k,λx.t1,λx.t2)V[[τ1τ2]] exactly when, for every jk and (j,v1,v2)V[[τ1]], (j,app1(λx.t1,v1),app1(λx.t2,v2))E[[τ2]]. Moreover, (k,t1,t2)E[[τ]] exactly when j<k. v1.t1jv1v2.t2v2  (kj,v1,v2)V[[τ]]. The environment interpretation starts with G[[]]={(k,,)kN} and extends two substitutions with values related by V at every declaration x2:τ. Write t1logt2:τ when the two terms have erased typing Γ2ti:τ and all related substitution instances belong to E in both directions.

Contextual equivalence has a narrower observation than equality of printed base results. For every well-typed closing context C, t1ctxt2:τ means C[t1] terminates if and only if C[t2] terminates. Distinct observable base values can be separated by a context, but the definition itself is termination-based.

Lemma 129.11 — Tan–Wei logical-relation interface

For the exact λ|2| card:

  1. if Γ2t:τ, then tlogt:τ;

  2. if t1logt2:τ, then t1ctxt2:τ;

  3. if Γt1:τϵ and t1t2, then |t1|ctx|t2|:|τ|; and

  4. contextual equivalence is transitive at every erased typing judgment.

Proof of Lemma 129.11 — Tan–Wei logical-relation interface

Proof. These are exact source imports: Theorems 5.4, 5.5, 5.8, and 5.3, respectively . The definitions above print their complete signatures. Within Instar/TwoLevelRec/, the Lean declarations are:

LogicalEquiv/Fundamental.lean
contains log_equiv.fundamental.

LogicalEquiv/Soundness.lean
contains log_equiv.soundness.

SemanticsPreservation/Preservation.lean
contains semantics_preservation.

CtxEquiv/Transitivity.lean
contains ctx_equiv.trans.

The imported proofs cover the compatibility families, substitution, and the reification-context case; no theorem for Tstage, MetaOCaml, or LMS is a consequence. ◻

The extension λ|2|ref adds locations, stores of natural numbers, ref(nat), and stage-indexed allocation, get, and put. The executing alloc1, get1, and put1 forms may be typed only beneath the stage-2 judgment; the stage-two forms build fragments beneath the stage-1 judgment. Moreover, run requires a store-free argument. Its world WN×N is a partial bijection between locations; stores σ1,σ2 are related at W when they contain equal naturals at every related pair. The value clause for references is (k,W,1,2)V[[ref(nat)]](1,2)W. The term clause quantifies over every pair of stores related by W: a left run terminating in j<k steps must be matched on the right by a run to related values and stores at an extension WW, with remaining index kj. This is the exact Kripke extension of the displayed pure relation, rather than a persistence theorem for host-language references.

A forbidden first-stage reference shows why the restriction is necessary. Allocate r=0, lift a function whose body increments r, and finally lift the contents of r. Lifting the function executes its body during generation, so the generated answer is one; erasure leaves that body beneath a lambda and returns zero. A second counterexample makes evaluation order visible. With a first-stage r=0, residualize a conditional whose first branch increments and reads r, and whose second branch only reads r. Reifying the first branch first produces ifz(b,1,1); reifying the second first produces ifz(b,1,0). The erased program selects one branch before its effect, so it has the latter behavior. The accepted repair allocates r with alloc2 and uses only stage-two get and put. The first-stage store then remains empty, and generation returns let r=alloc10 in ifz1(b,let _=put1(r,get1r+1) in get1r,get1r). Its effects occur only when the generated conditional executes, in the same order as the stage erasure.

Theorem 129.12 — Source-bounded staging-erasure equivalence

In λ|2|, if t1:rep(τ)ϵ and t1code t2, then |t1|ctxt2:τ in the empty closing environment. In λ|2|ref, if t1:rep(τ)ϵ and ,t1,code t2, then the same contextual equivalence holds under the printed stage-two/store-free restrictions.

Proof of Theorem 129.12 — Source-bounded staging-erasure equivalence

Proof. For the pure card, induct on the multistep reduction. Reflexivity gives contextual reflexivity. At a successor step, item 3 of lemma 129.11 gives equivalence of the two erasures; compose it with the induction hypothesis by item 4. At the terminal code value, |code t2|=t2. This is the proof assembly of source Theorems 5.9–5.10 [TW26].

For the reference card, use the world-indexed versions of the fundamental, soundness, one-step, and transitivity results. The initial world and both first-stage stores are empty; the store-free run premise ensures that first-stage reduction never allocates a location. Iteration therefore yields the displayed contextual equivalence. This is source Theorem 6.4 [TW26]. Its world-indexed signature is different from the pure theorem’s signature, so neither result transfers to another staging card without a separate interpretation theorem. ◻

Exercise 129.2

★★☆ Translate the annotated expression 2S+D(xdDD3S) into the displayed modal arithmetic fragment. Derive its type and check the commuting equation at x=4.

An evidence-bounded tower

A tower with object program p, interpreter I, and meta-interpreter J evaluates JIp. Ordinary execution retains two dispatch layers. The comparison card is Amin–Rompf’s untyped multi-level kernel λ↑↓, not Tstage. Its source and internal syntax is e::=xLit(n)Str(s)Lam(f,x,e)App(e,e)Cons(e,e)Let(x,e,e)If(e,e,e)1(e)2(e,e)Lift(e)Run(e,e)g,g::=Code(e)Reflect(e)Lamc(f,x,e)Letc(x,e,e),v::=Lit(n)Str(s)Lam(f,x,e)Cons(v,v)Code(e). Here 1 ranges over the three predicates and pair projections, and 2 over addition, subtraction, multiplication, and equality. Reflect and Letc implement ordered let insertion; they are not user quotation forms. The level parameter is the first argument of Run(b,e). Evaluation of b to code emits a residual run; any non-code value executes the reified code at the present level: evalms(ρ,Run(b,e))=reflectc(Run(b,q))evalms(ρ,b)=Code(b),evalms(ρ,Run(b,e))=evalmsg(ρ,q)evalms(ρ,b)Code(). In both equations, q=reifyc(evalms(ρ,e)). The polymorphic Lift maps a numeral to numeral syntax, a pair componentwise, code to residual Lift, and a closure to code by two-level eta expansion. Thus stage polymorphism is operational: the Pink interpreter abstracts over maybeLift, instantiated by the identity for eval and by Lift for evalc. The calculus has no typing judgment that could be imported into Tstage.

For a Pink program p, write psrc for its quoted S-expression and [[p]] for its translation to administrative normal form in λ↑↓. Define eval1=eval,evaln+1=evalnevalsrc. The paper proposes, with experimental rather than formal proof evidence, [[eval psrc]]Pink[[p]],[[run 0 (evalc psrc)]]Pink[[p]],[[evalc psrc]][[p]],[[(evaln evalcsrc) psrc]][[p]](n1). The observation in the first two lines is Pink contextual behavior; the last two demand the exact administrative-normal-form code, which is the claimed optimality. These are Propositions 4.2–4.6 of the source, where the authors state that formal proofs are absent [AR18].

This evidence has a hard boundary. The source supplies experiments rather than a proof of the claimed contextual equations, so it does not prove run(collapse(J,I,p))towerJIp. Accordingly, no tower-collapse theorem appears in this chapter’s theorem ledger.

Sources and seminar

The principal calculus, its dual-context proofs, and the two-level embedding are those of Davies and Pfenning [DP01]. The staged-reference and tower cards are intentionally source-bounded [TW26, AR18]. No public theorem from those cards transfers to the modal calculus by notation alone.

Suggested first pass.

None of these problems is a prerequisite for a later chapter. Begin with exercise 129.3, then complete exercise 129.8.

Exercise 129.3

★★☆ Construct a boxed exponentiation generator for exponent four. Give every context in its derivation, reduce the generator to a code value, and type the code body under the empty ordinary context.

Exercise 129.4

★★☆ Extend the term grammar with references and propose unrestricted CSP. Exhibit a reference that outlives its allocation stage. State a value restriction that blocks this term; do not claim preservation for the extension.

Exercise 129.5

★★☆ For a fixed dimension two, construct a generator for matrix multiplication whose input entries are modal naturals. Derive the code type of one output entry, then display the four output entries. State the exact point at which unknown dimensions would require dependent types absent from Tstage.

Exercise 129.6

★★☆ Let generators G1,G2 each append their name to a stage-zero log and return boxed naturals. Calculate the logs produced by left-to-right let insertion and by the reordered nesting. Repair the latter so that it produces the former log and the same residual addition.

Exercise 129.7

★★★ Fix object terms p:nat, an interpreter I:Codenatnat, and a meta-interpreter J:Code(Codenatnat)Codenatnat. Type the finite expression JIp. Identify which type is missing for an additional interpreter level and explain why this typing calculation proves no tower-collapse equation.

Exercise 129.8 — Practical: modal stage checker

★★★ Practical project.tstage-code-checker Implement in Agda or Kappa a finite checker for the six rule families, modal substitution beneath nested let-box, index decrement and contraction, and exponentiation. Require boxed-modal acceptance, boxed-ordinary rejection, results 7 and 27, and accepted index decrement. Implement the five-rule binding-time card with static and dynamic prints. Replay the ordinary-capture, index-decrement, and static-addition mutants; name respectively the missing T-Box premise, violated substitution invariant, and broken static-operator equation. The model makes no tower or effect claim.

Search the book

Type to search the local edition.