Lectures onType Theory
Chapter 127
Chapter 127Optional

Partial Evaluation, Binding-Time Analysis, and the Futamura Projections

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

An interpreter for a fixed source program performs the same dispatch whenever its dynamic input changes. Evaluation cannot perform that dispatch before the input is known. A specializer must evaluate the source-determined work, emit the input-dependent work, and preserve divergence as well as returned values.

A residual term is the missing result

The shared source language is Jones–Gomard–Sestoft’s exact first-order Scheme0 card.

Definition 127.1 — Scheme0 source card

P::=(d1,,dm),d::=define (f x) e,e::=cxif e0 e1 e2call f eop(e),c::=nquote(v),op::=carcdrcons∣=∣+. The first definition is the goal. Programs are statically scoped, call-by-value, first order, and purely applicative; function values, partial application, lambda, assignment, and side-effecting primitives are absent. With P(f)=(x,ef), environment ρ, and partial primitive interpretation δ, write P;ρev. The list judgment P;ρev evaluates left to right. The complete big-step schemas are

P;ρc[[c]]
S0-Const
ρ(x)=v
P;ρxv
S0-Var
P;ρevδ(op,v)=v
P;ρop(e)v
S0-Op
P;ρe0falseP;ρe2v
P;ρif e0 e1 e2v
S0-If-F
P;ρe0v0v0falseP;ρe1v
P;ρif e0 e1 e2v
S0-If-T
P;ρevP(f)=(x,ef)P;[xv]efv
P;ρcall f ev
S0-Call

Here [[c]] is the value represented by the literal or quotation c. The rules are partial: an undefined primitive and a divergent call have no derivation. We write run0(P,v)=w only for a finite derivation. This card is the language of the historical mix below [JGS93].

Fix natural numbers with total addition and multiplication. For a natural exponent k, define power(k,x)={1,k=0,xpower(k1,x),k>0. At k=3, the tests and decrements depend only on 3. The three multiplications depend on x. Performing the former and retaining the latter gives x(x(x1)).

Definition 127.2 — The pe_ on numeric projection

Let variables range over a countable set. Expressions and residual expressions share the grammar e,r::=nxe1+e2e1e2if0 e0 then e1 else e2. An environment ρ:xn is finite. The partial function eval(e,ρ) evaluates operators left to right and selects one conditional branch after evaluating its test. We write eval(e,ρ)=n only when this computation returns n. A static result is known(n). A dynamic result is res(r). Define lift(known(n))=n and lift(res(r))=r.

There is a direct translation into Scheme0: translate n to the numeral constant, e1+e2 and e1e2 to primitive applications, and if0 e0 then e1 else e2 to if (= e0 0) e1 e2. Thus this is a numeric projection of Scheme0, not a second presentation of its grammar. It omits quoted lists and calls until section 127.2; the historical self-applicability theorem is never inferred from this smaller definition.

Let ρs contain the variables known during specialization. The judgment ρspeeq is generated by these rules.

ρspenknown(n)
PE-Num
ρs(x)=n
ρspexknown(n)
PE-Static
xdom(ρs)
ρspexres(x)
PE-Dynamic

For {+,}, arithmetic has two clauses.

ρspee1known(n1)ρspee2known(n2)
ρspee1e2known(n1n2)
PE-Op-S
ρspee1q1ρspee2q2(r1. q1=res(r1))(r2. q2=res(r2))
ρspee1e2res(lift(q1)lift(q2))
PE-Op-D
ρspee0known(0)ρspee1q
ρspeif0 e0 then e1 else e2q
PE-If-Z
ρspee0known(n+1)ρspee2q
ρspeif0 e0 then e1 else e2q
PE-If-N
ρspee0res(r0)ρspee1q1ρspee2q2
ρspeif0 e0 then e1 else e2res(if0 r0 thenlift(q1) elselift(q2))
PE-If-D

On if0 x then 0 else 1, choosing either branch changes the answer for one of x=0 and x=1. Rule PE-If-D must retain both branches.

Lemma 127.3 — Residual variables

If ρspeeq, every free variable of lift(q) is a free variable of e outside dom(ρs).

Proof of Lemma 127.3 — Residual variables

Proof. Induct on the specialization derivation. Rules PE-Num and PE-Static leave no variable. Rule PE-Dynamic retains its one variable, whose side condition puts it outside the static domain. Each operator conclusion takes the union of the free variables in its premises. The two known-test rules retain only the selected branch. Rule PE-If-D takes the union of the test and both branches. These are all rules. ◻

Theorem 127.4 — Online specialization equation

Let the domains of ρs and ρd be disjoint, and suppose their union assigns every free variable of e. If ρspeeq, then for every n, eval(e,ρsρd)=neval(lift(q),ρd)=n.

Proof of Theorem 127.4 — Online specialization equation

Proof. Induct on the specialization derivation. The numeral and variable cases follow by lookup. In PE-Op-S, both operands evaluate to the recorded numerals. In PE-Op-D, apply the two induction hypotheses in left-to-right order and rebuild the operator evaluation.

For PE-If-Z, the test induction hypothesis gives zero in the combined environment. Both evaluations enter the first branch, where the branch induction hypothesis gives the equivalence. Rule PE-If-N uses the second branch and the recorded nonzero value. In PE-If-D, the residual test and source test have the same value by the induction hypothesis. They select the same branch, whose induction hypothesis gives the result. ◻

Exercise 127.1

★☆☆ Specialize if0 x then (2+3) else (y1) under the empty static environment. Give the complete derivation and check theorem 127.4 at (x,y)=(0,7) and (1,7).

Recursion forces memoization and a budget

For the recursion argument, extend definition 127.2 with calls. A numeric call program is a finite list of equations f(xs;xd)=ef, separating static and dynamic parameters. Its source evaluation extends eval with

P;ρesnP;ρedvP(f)=(xs;xd;ef)P;[xsn,xdv]efv
P;ρcall f(es;ed)v
N-Call

The numeral, variable, operation, and conditional rules are the big-step counterparts of eval in definition 127.2; vector evaluation is left to right. This card translates into Scheme0 by merging the two formal-parameter lists and applying the translation above. A static call pattern is a pair (f,n). A memo table maps such a pattern to one residual function name and, after the recursive call returns, its equation. Revisiting a pattern emits a call to that name; encountering a new pattern first allocates a fresh name and then specializes its body. Different static tuples are distinct patterns, so this policy is polyvariant.

The call g(n;x)=g(n+1;x) generates the infinite pattern sequence (g,0),(g,1),. Memoization alone does not terminate.

Definition 127.5 — Fuelled specialization

The judgment b;μ;ρspeeq;μ extends the preceding rules with a natural budget b and memo table μ. A table entry has the form (f,n)h while its equation is being constructed and (f,n)(h,h(xd)=rh) afterward. The sequential vector judgments are the left-to-right lift of this scalar judgment; in particular, known(n) means that every component is known. A syntax rule passes b unchanged. Calls use exactly these rules, where P(f)=(xs;xd;ef), π=(f,n), and q^ maps lift componentwise.

b;μ;ρspeesknown(n);μ1b;μ1;ρspeedq;μ2name(μ2(π))=h
b;μ;ρspecall f(es;ed)res(call h(q^));μ2
PE-Call-Hit
0;μ;ρspeesknown(n);μ10;μ1;ρspeedq;μ2πdom(μ2)
0;μ;ρspecall f(es;ed)res(call f(n;q^));μ2
PE-Fuel

At positive fuel a fresh residual name is installed before the body is specialized:

(b+1);μ;ρspeesknown(n);μ1(b+1);μ1;ρspeedq;μ2πdom(μ2)h freshμ3=μ2[πh]b;μ3;[xsn]peefqf;μ4μ5=complete(μ4,π,h(xd)=lift(qf))
(b+1);μ;ρspecall f(es;ed)res(call h(q^));μ5
PE-Call-New

Let F0 contain each source function whose call was retained by PE-Fuel. Define Fi+1 by adjoining every source function called in the body of a function in Fi, and put F=iFi. Because P is finite, this union stabilizes. The residual program Pμ consists of the completed equations in μ together with the source equations in P whose names lie in F. Thus retained source code is closed under residual calls. A pending entry is callable only by a recursive PE-Call-Hit; its equation is the equation being constructed by the unique enclosing PE-Call-New.

The two entry states are not merely an implementation detail. A proof that uses a recursive hit must relate the hit to the equation that will exist after the allocating call finishes.

Definition 127.6 — Completed-table invariant

Call μ a table prefix of μ when μ preserves every name allocated by μ, completes every pending entry of μ, and never changes a completed equation. For kN, write Ik(μ,μ) when μ is a table prefix of μ and the following conditions hold.

  1. Each pending entry πh in μ has one allocating PE-Call-New ancestor, and μ contains the equation produced by that ancestor.

  2. For every completed entry (f,n)(h,h(xd)=rh) in μ, every dynamic environment ρd, every numeral m, and every source or residual evaluation derivation of height below k, P;[xsn,xdρd(xd)]efmPμ;ρdrhm.

  3. Every source function retained at fuel zero is accompanied by the complete call-reachable source-equation closure F.

The height bound applies to the derivation on the side from which an implication starts. It makes the invariant inductive even when a pending back edge calls its own eventual equation.

Lemma 127.7 — Memo completion and finite simulation

Suppose proof search starts with μ, returns b;μ;ρspeeq;μ, and every entry of μ is completed. If pending entries of μ have their unique allocating ancestors in that derivation, then Ik(μ,μ) holds for every k. Under the same hypotheses, e and lift(q) have equal finite-return observations for derivations of height below k.

Proof of Lemma 127.7 — Memo completion and finite simulation

Proof. Use strong induction on k, simultaneously for every table entry and for the expression at every specialization subderivation. Inside the height induction, induct on that fixed specialization derivation. Numerals, variables, primitives, and conditionals use the corresponding argument or selected-branch induction hypotheses.

For PE-Call-Hit, memo-table lookup fixes the name in the conclusion. If its entry is pending, condition 1 identifies the unique enclosing allocator and the equation that the final table supplies; if it is completed, that equation belongs to the completed entry and condition 2 applies. In either case, inversion of a finite N-Call derivation exposes an evaluation of the equation body whose height is strictly below the height of the call. Apply the outer induction hypothesis to that body and rebuild the call on the other side. This argument works in both directions and is the step that justifies a recursive back edge.

For PE-Call-New, allocation preserves all older names, the body premise supplies the new equation, and completion changes only the state of that entry. Apply the inner induction hypotheses to the argument derivations and the outer induction hypothesis to each strictly shorter finite body evaluation. Table extensions are harmless by the same evaluation-derivation argument later recorded as residual-program monotonicity: allocation uses fresh names, completion preserves old equations, and retained closure only grows. For PE-Fuel, both sides invoke the same retained source equation; condition 3 supplies every later callee. These are the three call rules. Freshness gives unique names, complete gives one final equation per allocation, and the construction of F gives condition 3. Hence the simultaneous claims hold at k+1, and induction establishes them for every k. ◻

Lemma 127.8 — Residual-program monotonicity

Suppose μ1 is extended to μ2 only by allocating fresh residual names, completing pending equations, or enlarging the retained-function set. If Pμ1;ρrn, then Pμ2;ρrn.

Proof of Lemma 127.8 — Residual-program monotonicity

Proof. Induct on the finite evaluation derivation. Numeral, variable, operator, and conditional rules do not inspect the program. In the call case, the equation used by the premise is either a completed residual equation or a retained source equation. Extension preserves completed entries, uses globally fresh names for new entries, and only enlarges the call-reachable retained set. Therefore Pμ2 retains the equation used by the premise; apply the induction hypotheses to the argument and body derivations and rebuild N-Call. ◻

Theorem 127.9 — Safe-fuel search termination and preservation

For every finite program, expression, environment, and budget, deterministic proof search from the empty memo table has no infinite search path: it returns either a unique specialization derivation or a static failure. A static failure occurs, for example, when a declared static argument does not specialize to known(n), or when an attempted static primitive is undefined. If b;;ρspeeq;μ, then source and residual evaluation have the same finite-return observations: for every dynamic environment ρd and numeral n, P;ρsρdenPμ;ρdrn, provided every encountered primitive is defined and r=lift(q). The statement makes no claim about coinductive divergence or executions stuck at an undefined primitive.

Proof of Theorem 127.9 — Safe-fuel search termination and preservation

Proof. Implement proof search by the ordered, pairwise-disjoint rule alternatives. Order its recursive calls lexicographically by budget and expression size. A syntax-directed rule searches strict subexpressions. Unfolding a new call pattern decreases the budget. A repeated pattern and PE-Fuel emit syntax and make no recursive call on a function body. The order is well founded, so search returns either the unique derivation selected by the rules or a finite static-failure result. This first conclusion does not assert that every input has a derivation.

For preservation, the successful search from the empty table has a final completed table: every allocation is completed while its enclosing PE-Call-New returns. Apply lemma 127.7 at a height strictly larger than the finite evaluation derivation in the implication under consideration. The lemma’s expression clause gives that implication, and applying it from the other side gives the converse. Quantifying over all finite heights proves the displayed equivalence. This argument does not turn absence of a finite derivation into a divergence theorem. ◻

Fuel controls transformation time, not residual size. A dynamic conditional can duplicate a large continuation. For size control, extend residual syntax with let z=r in r and the single source rule

P;ρrvP;ρ[zv]rn
P;ρlet z=r in rn
N-Let

Lemma 127.10 — Evaluation decomposition for substitution

If P;ρrv, then for every expression r and numeral n, P;ρr[r/z]nP;ρ[zv]rn. Substitution is capture avoiding; the binding of z has no scope in r.

Proof of Lemma 127.10 — Evaluation decomposition for substitution

Proof. Induct structurally on r, with a subsidiary induction on the displayed finite evaluation derivation for each direction. At the variable z, the left side is the assumed derivation of r, while the right side is lookup of v. A different variable uses the same lookup on both sides. Primitive and conditional cases apply the induction hypotheses in evaluation order; determinism of P;ρrv ensures that multiple substituted occurrences yield the same v. In a call, apply the induction hypotheses to every actual argument, then use the identical source equation and environment for the body. Alpha-renaming handles a binder in an extended residual language. These are every expression constructor, so both implications follow. This reverse implication is an anti-substitution lemma; it is not obtained by reading the ordinary substitution lemma backward. ◻

Proposition 127.11 — Let-insertion preservation

If P;ρrv, then, for every finite-return observation n, P;ρlet z=r in rnP;ρr[r/z]n.

Proof of Proposition 127.11 — Let-insertion preservation

Proof. Invert or introduce N-Let, then apply lemma 127.10 in the required direction. The termination hypothesis is necessary under call by value: when zfv(r), insertion evaluates r even though substitution does not. ◻

This preservation theorem does not bound the number of distinct static call patterns.

Exercise 127.2

★★☆ Specialize g(n;x)=g(n+1;x) from static input 0 with budgets zero, one, and two. Write the residual call at exhaustion and prove that replacing it by zero would violate theorem 127.9.

Offline annotations and the three projections

The second card is the two-level Scheme0 syntax of Jones–Gomard–Sestoft. For b{S,D}, a division τ maps variables to binding times. The annotated constructs are eb::=cxops(e)opd(e)ifs(e0,e1,e2)ifd(e0,e1,e2)callsf(es)(ed)calldf(es)(ed)lift(e). The two argument lists at a call are the callee’s static and dynamic parameters. The subscript on a call instead selects unfolding (s) or residualization (d); these are different decisions. The exact checking rules are:

τc:S
BT-Const
τ(x)=b
τx:b
BT-Var
τei:S(1ia)
τops(e1,,ea):S
BT-Op-S
τei:D(1ia)
τopd(e1,,ea):D
BT-Op-D
τe0:Sτe1:bτe2:b
τifs(e0,e1,e2):b
BT-If-S
τe0:Dτe1:Dτe2:D
τifd(e0,e1,e2):D
BT-If-D
τei:S(1ia)
τcallsf(e1,,ea)():S
BT-Call-S0

When m<a, both call forms with dynamic arguments have binding time D:

τei:S (1im)τei:D (m<ia)
τcallsf(e1,,em)(em+1,,ea):D
BT-Call-S
τei:S (1im)τei:D (m<ia)
τcalldf(e1,,em)(em+1,,ea):D
BT-Call-D
τe:S
τlift(e):D
BT-Lift

For define (f (x1,,xm)(xm+1,,xa)) e, checking uses τf(xi)=S for im and D otherwise; its body must have binding time S when m=a, and D when m<a. These are exactly the rule families of the source card [JGS93].

The check becomes operational only after its two representations are fixed. Write q::=val(v)code(r), with val(v)=v and code(r)=r. A specialization environment ξ=(ρs;ηd) maps static variables to source values and dynamic variables to residual expressions. The judgment Pb;ξoffebq is the least relation generated by the following rules. The vector forms apply the scalar rules left to right.

Pb;ξoffcval([[c]])
Off-Const
ρs(x)=v
Pb;ξoffxval(v)
Off-Var-S
ηd(x)=r
Pb;ξoffxcode(r)
Off-Var-D
Pb;ξoffeval(v)δ(op,v)=v
Pb;ξoffops(e)val(v)
Off-Op-S
Pb;ξoffecode(r)
Pb;ξoffopd(e)code(op(r))
Off-Op-D
Pb;ξoffe0val(v0)j=1 if v0false, j=2 otherwisePb;ξoffejq
Pb;ξoffifs(e0,e1,e2)q
Off-If-S
Pb;ξoffeicode(ri)(0i2)
Pb;ξoffifd(e0,e1,e2)code(if r0 r1 r2)
Off-If-D

If Pb(f)=(xs;xd;efb), unfolding and residual calls are separate rules:

Pb;ξoffesval(v)Pb;ξoffedcode(r)Pb;([xsv];[xdr])offefbq
Pb;ξoffcallsf(es)(ed)q
Off-Call-S
Pb;ξoffesval(v)Pb;ξoffedcode(r)
Pb;ξoffcalldf(es)(ed)code(call fv(r))
Off-Call-D
Pb;ξoffeval(v)
Pb;ξofflift(e)code(quote(v))
Off-Lift

Rule Off-Call-D names the residual state reached at the displayed static tuple. The rules determine residual expressions; the following graph card determines the residual program used by the soundness theorem.

Definition 127.12 — Completed offline specialization graph

A completed offline specialization graph for Pb and a main static input is a finite triple (ν,E,r0) satisfying four conditions.

  1. The finite domain of ν consists of reached states (f,v), and ν(f,v)=fv is injective.

  2. For every (f,v)dom(ν), if Pb(f)=(xs;xd;efb), then Pb;([xsv];[xdxd])offefbcode(rf,v), and E contains exactly the equation fv(xd)=rf,v.

  3. Every residual call gw(r) occurring in r0 or an equation body in E has (g,w)dom(ν).

  4. Specializing the main expression under its static input and identity residual environment yields code(r0).

Construction is deterministic: allocate fv before specializing the state’s body; a repeated state reuses that name. It returns the residual program Ps=(E,r0) only when this reachability expansion terminates. A revisit is a back edge and does not expand the graph. These are the memoization and reachability-closure conditions of the source specializer [JGS93].

Constraint generation erases the subscripts, assigns an unknown in {S,D} to every occurrence and formal parameter, and reads the premises of the displayed rules as equations. For example, a static operator generates bi=S; a dynamic conditional generates b0=b1=b2=D; and a call equates each argument with the corresponding formal division. A boundary S<D is repaired only by inserting lift. Starting with every unknown at S, repeatedly changing a violated unknown to D computes the least solution because every generated constraint is monotone on the two-point lattice.

Lemma 127.13 — Binding-time consistency

Suppose τe:b, ξ maps every S-variable to a source value and every D-variable to residual syntax, and Pb;ξoffeq. Then q=val(v) for some v when b=S, and q=code(r) for some r when b=D. In particular, no static operator, test, or parameter receives residual syntax.

Proof of Lemma 127.13 — Binding-time consistency

Proof. Induct on the finite offline-specialization derivation, and invert the matching final binding-time rule. This choice is essential in Off-Call-S: its body specialization is a strict subderivation, whereas the callee’s binding-time derivation lives under the different division τf. The program check supplies that matching derivation under τf.

Rules Off-Const and Off-Var-S/D match BT-Const and BT-Var. The induction hypotheses give source values to Off-Op-S and Off-If-S, and residual syntax to Off-Op-D and Off-If-D. Each call rule’s matching binding-time rule checks its argument lists against the callee division. In Off-Call-S, apply the induction hypothesis to the body subderivation using the program check for efb. Rule Off-Call-D constructs residual syntax. Rule Off-Lift changes the source value supplied by its induction hypothesis into quoted residual syntax. These are all offline rule families. ◻

Lemma 127.14 — Completed-graph node simulation

Let (ν,E,r0) be a completed offline specialization graph for a checked program Pb, and let P=|Pb|. For every node (f,v)dom(ν), write E(ν(f,v))=(xd,rf,v). For every dynamic tuple d and value w, P;[xsv,xdd]|efb|wE;[xdd]rf,vw. The same equivalence holds simultaneously for the graph’s main source expression and r0.

Proof of Lemma 127.14 — Completed-graph node simulation

Proof. For kN, prove both implications simultaneously for every graph node and the main expression when the given finite evaluation derivation has height below k. Use strong induction on k, followed by induction on the fixed offline-specialization derivation supplied by definition 127.12. Constants, variables, operators, lifts, and both conditional forms use strict argument or selected-branch subderivations.

For an unfolded static call, inversion of source evaluation exposes the callee-body derivation strictly below the enclosing call; apply the outer height induction and rebuild the residual evaluation. For a residual dynamic call, graph closure supplies the unique target node and its equation. In the source-to-residual direction, inversion of the source call again exposes a strictly shorter body derivation, to which the outer induction applies. In the residual-to-source direction, inversion of the residual named call exposes the equation-body derivation strictly below that call; apply the same outer induction in the opposite direction and rebuild the source call. Crucially, a back edge is handled by smaller evaluation height, not by an induction hypothesis on the graph node or on its specialization body. These are both call forms and every offline rule family. Induction on k removes the bound. ◻

Theorem 127.15 — Well-annotated-program soundness

Let Pb pass the program check above, let P be its annotation erasure, and let offline specialization on static input s return a completed graph (ν,E,r0) in the sense of definition 127.12, with residual program Ps=(E,r0). For every dynamic input d and returned value v, run0(Ps,d)=vrun0(P,(s,d))=v. No conclusion is asserted when specialization does not return.

Proof of Theorem 127.15 — Well-annotated-program soundness

Proof. Apply lemma 127.14 to the main-expression clause of the completed graph. The definition of run0 supplies the displayed static and dynamic environments on the source side and the dynamic environment on the residual side. The two implications of the lemma give the equivalence for each returned value. The proof therefore depends on the completed, reachability-closed graph, rather than treating a back-edge body as a structural subderivation. ◻

Exercise 127.3

★☆☆ Let x be dynamic. Explain why ifs(x,0,1) has no binding-time derivation, display the failed premise, and give the least well-annotated repair, including every required lift. Derive the offline residual result of the repaired term under ηd(x)=x.

Monovariance can force a useless division. Fix the actual Scheme0 definition define (power k x)if(= k 0)1( x (call power ( k 1) x)). The primitive ( k 1) is defined on positive naturals; the conditional prevents its use at zero. Suppose one call has dynamic exponent and another has known exponent four. A single monovariant division marks k dynamic at both sites.

Here is the finite copying transformation used to repair that division. Let P be a finite Scheme0 program containing (127.1) and no definitions named powerd or powers. A power-site map χ assigns d or s to every nonrecursive syntactic call site of power in P. The program Splitχ(P) deletes the original definition, inserts two alpha-distinct copies powerd and powers, redirects the recursive call in each copy to that copy, and redirects every other call site to powerχ(). Every other syntax node and definition is copied homomorphically. The collapse map split renames either copy back to power and identifies their duplicate definitions. Therefore Splitχ(P)split=P, including bodies and call sites.

For offline specialization, the two copies receive different annotated definitions. Writing the static arguments before the semicolon, their bodies are powerd(;k,x)=ifd(opd(=)(k,lift(0)),lift(1),opd()(x,calldpowerd()(opd()(k,lift(1)),x))),powers(k;x)=ifs(ops(=)(k,0),lift(1),opd()(x,callspowers(ops()(k,1))(x))). The displayed lifts make every dynamic primitive operand dynamic. Both annotated definitions erase to their Scheme0 copies. A site may be assigned s only after its exponent premise checks at binding time S; all remaining sites use d, inserting a lift when a known actual flows to a dynamic formal. Thus the transformation and its admissible call-site annotations are separate, explicit data.

Proposition 127.16 — Binding-time split preservation

For every power-site map χ, Scheme0 environment ρ, expression e in P, and value v, P;ρevSplitχ(P);ρsplitχ(e)v. In particular, for all naturals k,x,v, a call to either copied function returns v exactly when the call to power(k,x) in (127.1) returns v. Under the displayed checked offline annotation, specialization of powers(4;x) returns x(x(x(x1))).

Proof of Proposition 127.16 — Binding-time split preservation

Proof. Prove the two implications simultaneously by induction on the finite evaluation derivation from the side supplying the implication. Constants, variables, primitives, and conditionals rebuild the same rule because splitχ is homomorphic there. A call to any function other than power uses the induction hypotheses for its argument and body derivations. At an external power call, the selected copy has the same formals and collapsed body as (127.1); apply the induction hypotheses to the arguments and then to the strictly smaller body derivation. At a recursive power call, the copy calls itself, while collapse calls power; the recursive body derivation is again strictly smaller. These are all call cases. The reverse induction collapses either copied name to power, so it has the same cases and establishes the converse.

For specialization, Off-If-S evaluates the equality tests at 4,3,2,1,0. At each positive exponent, Off-Op-S computes the decrement and Off-Call-S unfolds the next state. At zero, Off-Lift emits 1. The four enclosing Off-Op-D instances retain multiplication by the residual variable x, giving the displayed term. ◻

The same pair separates online state from an offline monovariant division. For f(k;x)=if0 k then x else (x+1), consider one call f(k;x) with unknown k and one call f(0;x). Monovariance joins the first call into the formal division and marks k dynamic, so the offline specializer residualizes the conditional at both call sites. The online specializer sees the second actual value 0 and returns only x. Function splitting recovers that result offline by assigning the second call its own all-static copy. Thus online is more precise on this fixed program and division; the calculation is not a system-independent ordering.

For the first projection, fix the represented arithmetic language q::=Lit(n)InputAdd(q,q) and the Scheme0 equations intExp(Lit(n),d)=n,intExp(Input,d)=d,intExp(Add(q1,q2),d)=intExp(q1,d)+intExp(q2,d). These equations use the card’s constructor tests, selectors, calls, and base addition. Specializing with respect to Add(Lit(2),Input) unfolds the first and third equations and yields d2+d. Hence the first-projection instance is eval(mix(intExpa,Add(Lit(2),Input)),d)=2+d. The general compiler equation below is the same calculation with q arbitrary.

Let p be the Scheme0 representation of p, and let pa=ann(p,δp) be its two-level annotation under checked division δp. Thus pa contains the division information rather than hiding it as an implicit argument. Fix the historical self-applicable offline program mix with full Scheme0 interface mix:AnnProg×StaticTupleProg and equation mix(pa,s)=reval(r,d)=veval(|pa|,(s,d))=v. The annotation pa, suppressed in informal projection slogans, is part of the executable interface. The equation is not asserted for definition 127.5.

Proposition 127.17 — Scheme0 self-application typing boundary

For mixa=ann(mix,δmix), and the distribution’s checked annotation powera of its Scheme0 power program, the following two applications are well-formed specializer inputs: mix(mixa,powera),mix(mixa,mixa). If either application returns, its result lies in Prog. The proposition asserts neither termination nor a structural fixed-point equation.

Proof of Proposition 127.17 — Scheme0 self-application typing boundary

Proof. The checked annotations satisfy mixa,poweraAnnProg, and the Scheme0 representation satisfies AnnProgStaticTuple. Therefore each displayed pair lies in AnnProg×StaticTuple, the domain sort of mix. Its codomain is Prog, so any returned value has that sort. Partiality of the signature prevents a termination conclusion. The cited construction supplies the annotation shapes but no general termination theorem for self-application . ◻

The following meta-sort ledger types every self-application. Let ProgStaticTupleData be the represented Scheme0 programs, AnnProgStaticTupleData their checked annotations, run:Prog×DataData, and let int,mixProg. The interpreter accepts [q,d]:Data, and the annotation encodings inta and mixa are members of StaticTuple. The specializer accepts [pa,s]:Data with pa:AnnProg and returns r:Prog. Hence, if the following three applications return, their outputs have the displayed meta-sorts; the equations introduce names for those returned programs rather than asserting termination: run(mix,[inta,q])=compile(q):Prog,run(mix,[mixa,inta])=compiler:Prog,run(mix,[mixa,mixa])=cogen:Prog. The displayed inclusions into StaticTuple and Data, realized by Scheme0’s quoted program and annotation representations, license the two self-applications when they terminate. The typing proposition above discharges none of the three termination antecedents. An arbitrary typed specializer need not have such a reflexive representation.

Theorem 127.18 — The three Futamura equations

Let int satisfy eval(int,(q,d))=eval(q,d) whenever either side returns. For the represented program q under consideration, assume that all three displayed specializations below terminate and return the named programs. Define compile(q)=mix(inta,q),compiler=mix(mixa,inta),cogen=mix(mixa,mixa). Then eval(compile(q),d)=eval(q,d),eval(compiler,q)=compile(q),eval(cogen,inta)=compiler. Each equality asserts a common returned program or value; it says nothing when the corresponding specialization diverges.

Proof of Theorem 127.18 — The three Futamura equations

Proof. Instantiate (127.2) with pa=inta and s=q. The interpreter equation proves the first line. For the second, use pa=mixa, s=inta, and dynamic input q. The right side is compile(q). For the third, use pa=mixa, s=mixa, and dynamic input inta. The right side is compiler. The stated termination hypotheses license precisely these applications. ◻

A typed self-interpreter does not discharge those hypotheses. In the Fω card of chapter 17, an evaluator at object type A has shape evalA:Rep(A)A. Its own syntax has type Rep(Rep(A)A), not Rep(A); the attempted application evalAevalA therefore fails at the argument type. Scheme0’s untyped program-as-data inclusion avoids that particular mismatch, but self-applying mix still requires the binding-time and termination facts in (127.2).

The binding-time analysis has a local abstract-interpretation map. Take the two-point lattice SD; map a concrete partial environment to S at exactly its known variables and to D elsewhere. Constants transfer to S, variables by lookup, base applications by join, a dynamic test forces the result and both branches to D, and calls join actual annotations into the callee’s formal division.

Lemma 127.19 — Finite binding-time analysis

For a finite Scheme0 program, the displayed transfer is monotone on the finite lattice {S,D}Vars. Kleene iteration from the all-static map terminates at its least post-fixed division, and every accepted static occurrence depends only on variables mapped to S.

Proof of Lemma 127.19 — Finite binding-time analysis

Proof. Each transfer clause is a projection, constant, or finite join in SD, hence monotone. The product lattice has finite height at most the number of program variables; every strict iteration changes at least one coordinate from S to D, so iteration terminates. At the post-fixed point, induct on expression syntax. The variable case is lookup; an operation can remain static only when every operand does; the conditional and call cases use their displayed propagation constraints. ◻

This is an explicit instance of the vocabulary in chapter 25. It does not import that chapter’s flow-sensitive domains, widening theorem, or verified analyzer: the lattice, transfer, and termination argument above are the entire connection.

Sources. The Scheme0 encodings, binding-time analysis, mix equation, and projection derivations follow Jones, Gomard, and Sestoft, especially Chapters 4–6 [JGS93]. The online specializer is the smaller calculus proved locally above. Other computational-metalanguage and pure-lambda binding-time theorems remain separate systems.

Suggested first pass.

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

Exercise 127.4

★★☆ Give the useless monovariant annotation of power in which both arguments are dynamic. Perform the function split above, solve both divisions, and calculate the residual program at exponent four. Prove equality of the erased calls by unfolding the two copied equations.

Exercise 127.5

★★★ Let int+ interpret numerals, variables, and addition. Supply its encoding and use (127.2) to derive the first projection for (x+2)+3. State the termination assumption where it is used.

Exercise 127.6

★★★ Practical project.scheme0-fuelled-specializer Implement in Kappa the expression core, dynamic conditional, recursive call patterns, memo hits, and safe fuel exhaustion. Maintain lemma 127.3. Print power-3: x(x(x1)),dynamic-if: if0 x then 5 else y,fuel-zero: frontier call plus retained equation,fuel-two: two equations plus retained closure,memo-hit: a reached self-recursive equation,closure-check: every frontier target is present. A mutation that returns the left operand of static addition must fail the dynamic-conditional test. The Kappa program does not prove self-applicability or a Futamura equation.

Exercise 127.7

★★☆ For E0=x and En+1=if0 yn then En else En, calculate the tree size of the residual term produced without let insertion. Then insert one fresh let at each level and prove the resulting dag has linear size using proposition 127.11 under its termination hypothesis.

Exercise 127.8

★★☆ Compare the polyvariant key (f,n) with the following monovariant policy on calls power(2;x) and power(3;y). The monovariant table has key f; when distinct static tuples collide, it promotes every differing static component to a dynamic formal, emits one copy of the erased source equation with that promoted formal, and redirects every colliding call to the copy. Write both residual programs and state which static distinction is lost. Prove preservation of the polyvariant program by the corresponding call cases of theorem 127.9. Prove preservation of the monovariant program directly by induction on the promoted exponent; it is not an instance of that theorem’s (f,n)-keyed algorithm.

Exercise 127.9

★★★ Using (127.2), derive the second projection for an arbitrary q and the third at inta. Type each meta-level application with the ledger above and state its termination premise.

Search the book

Type to search the local edition.