Lectures onType Theory
Chapter 17
Chapter 17Optional

Typed Self-Representation in System F-omega

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

Typed self-representation without a paradox

Strong normalization appears to forbid a self-interpreter. An interpreter which can consume its own text seems to invite the same diagonal argument which proves that there is no total computable universal function for the total computable functions. The conclusion is too quick. The diagonal argument needs one particular self-application to be well typed, and a typed representation is indexed precisely so that this application need not be well typed.

The diagonal step that typing refuses

Let us recall the classical argument in the form that matters here. Suppose that a total computable function U:N×NN were universal for total computable unary functions: for every such function f there would be an index a with f(n)=U(a,n) for all n. Then d(n):=U(n,n)+1 would itself be total and computable. If a indexed d, then d(a)=U(a,a)+1=d(a)+1, a contradiction. The two occurrences of a in U(a,a) are the whole mechanism.

Here is the corresponding calculation for terms. A quotation is provisionally the result of an external operation on a chosen closed typing derivation D:e:A; write its result as D^, or temporarily as e^ when the derivation is fixed. Suppose that a closed internal term u unquotes every genuine quotation: ue^=βe. Suppressing annotations for the moment, form pu:=λx.λy.(ux)x. The vacuous binder λy plays the role of the classical +1: it makes the diagonal reduct differ from itself by exactly one abstraction node. If pu^ exists and the diagonal application pupu^ is typable, then pupu^βλy.(upu^)pu^=βλy.pupu^. The equation looks paradoxical, but only after the italicized typing assumption has been made.

The barrier argument needs unique normal forms for a relation that also normalizes constructor annotations. Let β be the compatible closure, through a term, of term beta contraction and constructor beta contraction in annotations and type arguments. Write βmix for its symmetric, transitive closure. To obtain unique normal forms, it remains to prove that two reductions from one term can be joined. Root and congruence reduction overlap at beta-redexes, so the joining relation must commute with both term and constructor substitution.

Lemma 17.1 — Mixed parallel substitution and diamond

Let c be parallel constructor beta reduction: it has reflexivity, congruence for every constructor former, and the root clause AcACcC(λu::κ.A)CcA[C/u]. Let m contain reflexivity, congruence for term abstraction, application, type abstraction, and type application, together with the ordinary term-beta root and the following type-beta root. Its annotations and type arguments reduce by c: emedmd(λx:A.e)dme[d/x],emeCcC(Λu::κ.e)[C]me[C/u]. After capture-avoiding freshening, the following simultaneous substitution laws hold: eme, dmde[d/x]me[d/x],eme, CcCe[C/u]me[C/u]. Both parallel relations have the diamond property.

Proof of Lemma 17.1 — Mixed parallel substitution and diamond

Proof. The first substitution law is the induction of lemma 5.37; its abstraction case additionally uses constructor congruence on the binder annotation. Prove the second by induction on eme. Constructor annotations and type arguments use constructor substitution. In the term-beta case, constructor substitution commutes with capture-avoiding term substitution. The only new root calculation is type beta. Alpha-rename its binder v so that v{u}FV(C)FV(C). The two orders are identified by e0[D/v][C/u]=e0[C/u][D[C/u]/v], and the induction hypotheses reduce both sides in parallel to

e0[D/v][C/u]=e0[C/u][D[C/u]/v].

For the diamonds, use the source-form complete development. Its complete term clauses are x:=x,(λx:A.e):=λx:A.e,(Λu::κ.e):=Λu::κ.e,((λx:A.b)d):=b[d/x],((Λu::κ.e)[C]):=e[C/u],(e1e2):=e1e2when e1 is not syntactically an abstraction,(e[C]):=e[C]when e is not syntactically a type abstraction. where the two application clauses and the two type-application clauses are selected by the source outer form. An annotation or type argument is developed by constructor complete development, whose complete clauses are u:=u,(AB):=AB,(u::κ.A):=u::κ.A,(λu::κ.A):=λu::κ.A,((λu::κ.A)C):=A[C/u],(AC):=AC. The last clause applies when A is not syntactically a constructor abstraction. These clauses are exhaustive for the displayed term and constructor grammars. In particular, constructor application contracts only a source of the displayed beta form and otherwise develops the two components without contracting their result. Thus development does not contract a redex created by developing an immediate component. Induction on a parallel derivation shows that every parallel reduct takes one further parallel step to this complete development. For application congruence, split on the source outer form of e1. If e1 is not an abstraction, from eimei the induction hypotheses give eimei, hence e1e2me1e2, the complete development. If e1=λx:A.b, then e1=λx:A.b for parallel reducts A,b. Apply the root clause to e1e2 and the substitution law to reach b[e2/x], the complete development of the source application. A constructor root–congruence overlap at (λu.A)C joins at A[C/u] by constructor substitution. A term root–congruence overlap at (λx:A.e)d joins at e[d/x] by the first displayed law. Finally, the new overlap at (Λu::κ.e)[C] joins at e[C/u] by the second law. These are the only root overlaps. Thus every parallel reduct of a takes one parallel step to a. Fix either of the two parallel relations. If a reduces in parallel to both b and c, then both b and c reduce in parallel to a. This proves the diamond property for each relation. ◻

Lemma 7.56 — Term normalization and confluence used here

For the pure Fω signature whose term forms are variables, term abstraction and application, and constructor abstraction and application, every well-typed term is strongly normalizing under both compatible term beta reduction and the mixed relation that also reduces constructor beta redexes in annotations and type arguments. Both relations are confluent on all terms of the displayed raw syntax, whether or not they are typable.

Proof of Lemma 7.56 — Term normalization and confluence used here

Proof. The source proves strong normalization of every well-typed term in the pure Fω signature under combined beta reduction [Bar92]. This relation contracts both term-level and constructor-level beta redexes; on the separated syntax printed here it is exactly β. The term-only relation is a subrelation. Expanding a named abbreviation adds no reduction rule.

By lemma 17.1, mixed parallel reduction is diamond. Every mixed one-step contraction is parallel, and every parallel step can be serialized into finitely many compatible mixed steps, by induction on its derivation. The path-length argument of theorem 5.38 therefore gives mixed confluence. Restricting the parallel clauses to term roots and holding annotations and type arguments fixed gives term-only confluence. ◻

Proposition 7.57 — The normalization barrier, exactly stated

Let u be a closed Fω term which unquotes the quotation of every closed, well-typed Fω term. Suppose annotations can be chosen so that pu:=λx.λy.(ux)x is closed and well typed, and its quotation pu^ exists. Then pupu^ is not a well-typed Fω term. The conclusion holds for either the term-beta equation ue^=βe or its mixed-beta analogue.

Proof of Proposition 7.57 — The normalization barrier, exactly stated

Proof. Fix either relation from the statement. Assume that the application is well typed. Its first term-beta reduct is well typed by subject reduction. The unquoting hypothesis relates upu^ to pu in the chosen relation. Compatible congruence under application and abstraction therefore relates λy.(upu^)pu^ to λy.pupu^; this is the second step of (7.6).

By lemma 7.56, let v be the normal form of pupu^ in the chosen relation. Confluence says that equivalent well-typed terms have alpha-equivalent normal forms. Compatible reduction under the outer abstraction gives λy.v as the normal form of the last term in (7.6). Hence v would be alpha-equivalent to λy.v. This is impossible: the latter finite syntax tree contains one more abstraction node. Therefore the diagonal application is not well typed. ◻

The proposition does not say that u is untypable. It does not even say that pu is untypable. It says that the represented type attached to pu^ does not permit the second use of the same argument required by the diagonal. This is the point at which the analogy with one untyped universal domain ends.

The proposition is conditional. Its antecedent is not a property of the deep indexed representation. Its unquoter has type unquote:α::U.ExpαOpIdα. Under α::U and x:Expα, the attempted body (unquote[α]x)x would require constructor conversion OpIdα=βExpαB for some B. Unfolding Op gives the two contractions OpIdα0Id(αId)0αId. The result has neutral constructor head α, so its constructor normal form cannot have arrow head. Strong normalization and unique constructor normal forms theorem 7.21, corollary 7.24 reject the application before any term reduction occurs. At a genuine closed index α=A^, the same result type reduces to A, which is exactly why ordinary unquotation remains typable. Here and until the formal construction below, A^ denotes a constructor of kind U; it is distinct from the term quotation e^.

Exercise 7.16 — *

★★☆ Work under α::U and x:Expα. Infer the type of unquote[α]x, then show that applying this result to x would require OpIdα=βExpαB. Normalize both constructor heads and explain why neutral α cannot be converted to an arrow. Then substitute A^ for α and use lemma 7.66 to show why unquoting a genuine quotation at A is nevertheless well typed. This is a constructor-typing calculation; do not appeal to term strong normalization.

The pure calculus

Quotation and unquotation use the pure Fω fragment generated by κ::=Tyκ1κ2,A,B::=uABu::κ.Aλu::κ.AAB,e::=xλx:A.eeeΛu::κ.ee[A] with the kinding and typing rules of chapter 9. Constructor conversion is type-level beta conversion. Compatible term reduction has the two root contractions (λx:A.e1)e2βe1[e2/x],(Λu::κ.e)[C]βe[C/u] and is closed under binders and both operands of an application. A term is normal when its term tree contains neither root; constructor redexes in type annotations and type arguments do not affect this predicate.

Write etye when e and e have the same term tree and corresponding constructor components are beta-convertible. For the term-only closure β and the mixed reduction β fixed before lemma 17.1, eβeeβe,etyeeβmixe. Deep unquotation reduces by β to the source term. Restricting the calculation to β instead yields some e with etye: constructor normalization may change annotations, but not the term tree or its normality.

Names such as Bool and Nat abbreviate the Church encodings BoolF and NatF of definition 5.15, definition 5.18, with explicit kind annotations. Every named declaration in this chapter expands to one term of the stated grammar. A constructor binder without an explicit kind in a program formula has kind Ty; term binders retain their type annotations.

A shallow representation

The quotation must be beta-normal while substituting a fixed identity must recover the source. Copying the source under a fresh binder does not meet the first requirement: for the redex (λx:A.x)y, the naive candidate λi:I.(λx:A.x)y still contains that redex. The repair is to record each application under an inert variable head, so that the application node becomes stuck until unquotation replaces the inert variable by the identity. Put I:=Z::Ty.ZZ,i:I. The letter i is a fresh, designated term variable. Quotation is defined on a typing derivation, rather than on an unannotated term, because an application node must record the function type found by that derivation.

Definition 7.58 — Shallow prequotation

For a derivation D of Δ;Γe:A, the shallow prequotation relation Dshq is determined by the following clauses. If the final rule of D is application, its premises derive e1:AB and e2:A; the clause therefore builds an application representation of type B. xshx,λx:A.eshλx:A.q,(eshq),e1e2shi[AB]q1q2,e1:ABshq1,e2:Ashq2,}Λu::κ.eshΛu::κ.q,(eshq),e[C]shi[u::κ.A]q[C],(e:u::κ.Ashq). A final use of conversion leaves q unchanged. For a closed derivation D of e:A, its shallow quotation is the term D^sh:=λi:I.q.

The application clauses are easy to read by calculation. Since i is a variable, i[AB]q1q2andi[u::κ.A]q[C] are stuck, hence normal, yet replacing i by the polymorphic identity erases the inserted node in either case.

Lemma 7.59 — Typing and normality of shallow quotation

If D derives Δ;Γe:A and Dshq, then Δ;Γ,i:Iq:A. If D is closed, then D^sh:IA and D^sh is term-beta-normal.

Proof of Lemma 7.59 — Typing and normality of shallow quotation

Proof. Induct on the typing derivation. The variable, term-abstraction, and type-abstraction cases use the same typing rule as the source derivation and the induction hypothesis for the immediate premise.

For term application the two induction hypotheses give q1:AB and q2:A. The variable i has the instances i[AB]:(AB)(AB),i[AB]q1:AB, so i[AB]q1q2:B. For type application the induction hypothesis gives q:u::κ.A, and i[u::κ.A]q:u::κ.A; applying this term to C gives A[C/u]. A final conversion changes only the claimed result type.

Now assume the derivation closed. Abstracting i gives type IA. For normality, induct once more over the same clauses. A copied abstraction cannot introduce a redex. At either application clause the head is the variable i, not an abstraction; the induction hypotheses say that all proper term subexpressions are normal. The conversion case changes no term. For a term abstraction Γ,x:Ae:B, the body induction hypothesis holds under x:A; copying the binder therefore gives type AB without introducing a redex. ◻

Define the closed polymorphic identity and the internal shallow unquoter by idI:=ΛZ::Ty.λz:Z.z:I,unquotesh:=ΛA::Ty.λr:IA.ridI:A::Ty.(IA)A. Unlike quotation, this is an ordinary term of Fω.

Theorem 7.60 — Strong shallow unquoting

If D is a closed derivation of e:A, then unquotesh[A]D^shβe.

Proof of Theorem 7.60 — Strong shallow unquoting

Proof. After the outer type contraction and two term contractions it is enough to prove the stronger assertion ()q[idI/i]βe for every shallow prequotation. We induct on its typing derivation. A variable is unchanged. Term and type abstractions follow by compatible reduction under the binder and the corresponding induction hypothesis.

For term application, the decisive part of the calculation is idI[AB]q1q2βq1q2βe1e2, where qj=qj[idI/i] and the last reductions are the two induction hypotheses. Type application calculates idI[u::κ.A]q[C]βq[C]βe[C]. A final conversion added no syntax, so it uses the induction hypothesis unchanged. This proves () and hence the theorem. ◻

Exercise 7.17

★★☆ Take A=I and a=idI. Write the complete shallow prequotation of (λx:I.x)idI, including the inserted type argument to i and its two term arguments. Then substitute idI and display every beta step to the original redex. Explain why the quotation itself is normal even though the represented term is not.

The shallow construction already breaks through the normalization barrier: the unquoter terminates and returns the represented term. It is too opaque for a size function, however. Its interface exposes no constructor tag or case operator: a consumer can only apply the stored polymorphic term at a chosen result type. A direct inspection of beta-eta normal forms therefore offers no branch that can count the source constructor. To inspect the four term constructors we must expose four cases in the representation itself.

Representing types only as far as the fold needs

Fix a fresh constructor variable F::TyTy. Marking every constructor production is too strong. For constructor application it would give preFall(AB)=F(preFall(A)preFall(B)), but the argument of the outer F must already have kind Ty, whereas A may have an arrow kind; the application case of the formation induction cannot be rebuilt. Constructor variables, abstractions, and applications must retain their kind, while the arrow and universal productions receive the marker. The F-pre-representation of a constructor is defined structurally: the constructor translation preF(A) is a syntactic constructor of representation kind. A semantic interpretation such as [[A]]ρ instead maps a type to a relation. They are different constructions, and no theorem identifying them is asserted here. preF(u)=u,preF(AB)=FpreF(A)FpreF(B),preF(u::κ.A)=u::κ.FpreF(A),preF(λu::κ.A)=λu::κ.preF(A),preF(AB)=preF(A)preF(B). The full representation of a closed constructor is A^:=λF::TyTy.preF(A). Here A^ is a constructor of kind U. By contrast, D^ is a term quotation determined by the typing derivation D, and e^ abbreviates that term only after its derivation has been fixed. The hat records representation in each case, while the sort of its operand determines whether the result is a constructor or a term. Notice the asymmetry in (7.7). An arrow or universal type classifies terms and is therefore marked by F; a constructor variable, constructor abstraction, or constructor application is represented by itself. This is enough information to calculate the result type of each operation; it is not an inductive syntax tree for types.

Lemma 7.61 — Formation and substitution of type pre-representations

Let F be fresh.

  1. If ΔA::κ, then Δ,F::TyTypreF(A)::κ.

  2. If Δ,u::κA::κ and ΔC::κ, then, up to renaming of bound variables, preF(A[C/u])=preF(A)[preF(C)/u].

  3. If A=βB, then preF(A)=βpreF(B).

Consequently, if A is closed of kind κ, then A^::(TyTy)κ. In particular, for a closed type A, A^::U,U:=(TyTy)Ty.

Proof of Lemma 7.61 — Formation and substitution of type pre-representations

Proof. For (1), induct on the kinding derivation. A variable is unchanged. In an arrow, the induction hypotheses give preF(A),preF(B)::Ty; applying F to each gives two types and hence their arrow is a type. In a universal, the body hypothesis gives preF(A)::Ty under u::κ, so FpreF(A)::Ty and the universal is formed. Constructor abstraction and application use their two kinding rules directly. Thus variables, arrows, universals, constructor abstractions, and constructor applications each preserve kind Ty under prequotation.

For (2), induct on A. For an arrow, preF((AB)[C/u])=FpreF(A[C/u])FpreF(B[C/u]), and the two induction hypotheses rewrite this to preF(AB)[preF(C)/u]. Variables and applications are homomorphic. Under either binder, first rename its bound variable away from u and the free variables of C, then apply the induction hypothesis to the body. This accounts for both constructor abstraction and universal quantification.

For (3), it suffices by congruence to inspect one constructor beta step. By (2), preF((λu::κ.A)C)=(λu::κ.preF(A))preF(C)βpreF(A)[preF(C)/u]=preF(A[C/u]). The conclusion about A^ is now one use of constructor abstraction. ◻

Type application creates a difficulty that did not occur in the shallow representation. If e:u::κ.A, its representation has to remember how to obtain the instance at C. We record that relationship extensionally by the ordinary term instP,Q:=λx:P.x[Q]:PB[Q/u],P=u::κ.B,Q::κ. The notation is defined only when P has the displayed universal form and Q has the displayed kind. It is not a kind-polymorphic Fω function; each occurrence in a quotation is a separately constructed term.

Type abstraction creates the dual difficulty. Its body has a redundant quantifier after the constant-result folds used by size and isNormal. Concretely, under a constant fold KX=λA.X, the recursive value has type x:u::κ.X, while the case branch must return X. It suffices to instantiate x at one fixed constructor of kind κ. The type Strip records this operation uniformly. Every kind has such a closed constructor: STy:=X::Ty.X,Sκ1κ2:=λu::κ1.Sκ2. The kinding judgment Sκ::κ follows by induction on κ. At the base kind the concrete calculation is one use of K-All: X::TyX::TyX::Ty.X::Ty. For the arrow step, the induction hypothesis gives Sκ2::κ2; weakening places it under u::κ1, and K-Abs derives λu::κ1.Sκ2::κ1κ2. This claims a closed constructor of kind Ty, not a closed term inhabiting that type. Put Strip:=λF::TyTy.λA::Ty.B::Ty.(C::Ty.FCB)AB. If T::κTy, define stripF,κ,T:=ΛB::Ty.λc:(C::Ty.FCB).λx:(u::κ.F(Tu)).c[TSκ](x[Sκ]). Then stripF,κ,T:StripF(u::κ.F(Tu)). Indeed, x[Sκ]:F(TSκ) and c[TSκ]:F(TSκ)B. This two-line derivation is the reason for the otherwise arbitrary-looking inhabitant Sκ.

For later calculations record what stripping does when F is the constant function KX:=λA::Ty.X and the combining map is the identity: stripKX,κ,T[X](ΛC::Ty.λz:X.z)(Λu::κ.q)βq[Sκ/u]. Thus stripping does not erase an arbitrary quantified value. It chooses one well-kinded instance. This is sufficient only when the operation’s answer is independent of type arguments, as size and normality are.

Exercise 7.18 — *

★★★ Work in the constructor context X::Ty,T::κTy, where KX:=λA::Ty.X, and take κ=(TyTy)Ty. Expand Sκ, derive its kind, and type every subterm of stripKX,κ,T. Finally verify (7.10) for this kind by an explicit reduction.

Hint. First derive STy::Ty, then use one constructor abstraction with a binder of kind TyTy to derive Sκ::κ. In the body, type x[Sκ] before instantiating c at TSκ.

The deep representation and its fold

The naive type-abstraction case would quantify over the represented binder’s kind: κ.(u::κ.F(Tu))F(u::κ.Tu). This is not a constructor of pure Fω: kinds are not first-class and there is no κ. The case type must instead work for each fixed metalevel kind κ, and receive the uniform stripping operation that extracts a representative body at that kind. The same obstruction determines the type-application case. The repair assigns one case type to each syntactic constructor, recording the represented source and target types in its indices: Op:=λF::TyTy.λα::U.F(αF),Abs:=λF::TyTy.P::Ty.Q::Ty.(FPFQ)F(FPFQ),App:=λF::TyTy.P::Ty.Q::Ty.F(FPFQ)FPFQ,TAbs:=λF::TyTy.P::Ty.StripFPPFP,TApp:=λF::TyTy.P::Ty.FPQ::Ty.(PFQ)FQ,Exp:=λα::U.F::TyTy.AbsFAppFTAbsFTAppFOpFα. The binders P,Q in these case types range over represented result constructors; source types are written A,B only in the prequotation clauses of definition 7.62. The fold consumes one algebra component for each representation constructor and returns the component indexed by the represented type. In particular, an application node consumes AppF, a represented function in F(FPFQ), and a represented argument in FP, producing FQ. For TAbs, the case consumes the stripping operation and the stripped body P, then returns FP. In a prequotation, F and the four term variables abs,app,tabs,tapp are fixed. Transform a source context Γ=x1:A1,,xn:An into ΓF=x1:FpreF(A1),,xn:FpreF(An). Abbreviate the complete case-variable context by ΞF:=abs:AbsF,app:AppF,tabs:TAbsF,tapp:TAppF.

Definition 7.62 — Deep prequotation and quotation

Write DFq for the external, derivation-directed deep prequotation: a relation that recursively encodes the final rule and premise encodings of D. Its clauses are: xFx,λx:A.e:BFabs[preF(A)][preF(B)](λx:FpreF(A).q),e1e2:BFapp[preF(A)][preF(B)]q1q2,(e1:ABFq1, e2:AFq2),Λu::κ.e:AFtabs[preF(u::κ.A)]stripF,κ,preF(λu::κ.A)(Λu::κ.q),e[C]:A[C/u]Ftapp[preF(u::κ.A)]q[preF(A[C/u])]instpreF(u::κ.A),preF(C). In the abstraction clause, the induction hypothesis derives q:FpreF(B) under x:FpreF(A) from Γ,x:Ae:B. In the application clause, the induction hypotheses derive q1:FpreF(AB) and q2:FpreF(A). Constructor abstraction retains its binder kind; constructor application records the prequotations of its operator, argument, and result indices. Conversion changes only the derivation’s result type, so the quoted term remains q.

If D is a closed derivation of e:A, its deep quotation is D^:=ΛF::TyTy.λabs:AbsF.λapp:AppF.λtabs:TAbsF.λtapp:TAppF.q.

For a source instance e[C], the recursive quotation has type FP, where P represents the universal type of e. The instantiation term has type PFR, where R represents the result type. Therefore the case must accept FP, then PFR, and return FR: tapp[P]q[R]g:FR(q:FP, g:PFR). Accordingly, the TApp component in (7.11) has type P.FPR.(PFR)FR; its quantifiers make one case term applicable at every source operator and result type.

Example 7.63 — A deep quotation, clause by clause

Quote the closed instance e0:=(ΛZ::Ty.λz:Z.z)[I]:II. For the fixed fold parameter F, abbreviate PF:=preF(I)=Z::Ty.F(FZFZ), and RF:=preF(II)=FPFFPF. The copied variable has type FZ. The abstraction clause therefore gives qabs:=abs[Z][Z](λz:FZ.z):F(FZFZ). The constructor-abstraction clause wraps this family: qtabs:=tabs[PF]stripF,Ty,preF(λZ::Ty.ZZ)(ΛZ::Ty.qabs):FPF. Finally, instPF,PF:PFFRF, so the type-application clause is q0:=tapp[PF]qtabs[RF]instPF,PF:FRF. Abstracting F and the four case variables as in (7.12) produces D0^:Exp(II)^. The term tree visibly contains one term-abstraction case, one constructor-abstraction case, and one constructor-application case; no general proof has yet been used.

Exercise 17.4

★★☆ Reconstruct the three displayed clauses for the quotation of (ΛZ::Ty.λz:Z.z)[I]. In particular, derive the types of qabs, qtabs, and q0 from the four case interfaces, and explain why the two constructor arguments supplied to tapp are PF and RF, in that order.

Lemma 7.64 — Fundamental typing lemma for deep quotation

If D derives Δ;Γe:A and DFq, then, in the context containing ΓF and the four case variables of (7.11), Δ,F::TyTy;ΓF,ΞFq:FpreF(A). For a closed derivation, D^:ExpA^, and D^ is term-beta-normal.

Proof of Lemma 7.64 — Fundamental typing lemma for deep quotation

Proof. Induct on D. The variable case is the definition of ΓF. For term abstraction the induction hypothesis gives q:FpreF(B) under x:FpreF(A). Hence the copied abstraction has type FpreF(A)FpreF(B), and the abs case returns F(FpreF(A)FpreF(B))=FpreF(AB). For term application, the two induction hypotheses have exactly the first two argument types required by app, which returns FpreF(B).

For type abstraction put P:=preF(u::κ.A)=u::κ.FpreF(A),T:=preF(λu::κ.A). The induction hypothesis gives Λu::κ.q:P, while (7.9) gives stripF,κ,T:StripF(u::κ.F(Tu)). Since TuβpreF(A), constructor conversion changes this type to StripFP. Thus tabs[P] returns FP, as required.

For type application put P=preF(u::κ.A) and R=preF(A[C/u]). The induction hypothesis gives q:FP. By (7.8), instP,preF(C) has domain P and codomain (FpreF(A))[preF(C)/u]=F(preF(A)[preF(C)/u])=lemma7.61.2FR. These are exactly the arguments expected by tapp[P]q[R], so the result has type FR. In the conversion case, lemma 7.61 converts the induction-hypothesis type to the required one.

For a closed derivation, abstracting the five designated variables yields F::TyTy.AbsFAppFTAbsFTAppFFpreF(A), which is definitionally ExpA^. Normality follows by the same induction. Every apparent application in q has one of the four case variables at its head. The inserted inst and strip terms are themselves normal and are passed as arguments, not applied there. Copied binders preserve normality. Type-level redexes in annotations are, by our convention, not term redexes. ◻

The representation is deep: eliminating a quoted expression requires handlers for term abstraction, term application, constructor abstraction, and constructor application. Applying a quotation to those four handlers is the eliminator foldExp:=ΛF::TyTy.λa:AbsF.λp:AppF.λta:TAbsF.λtp:TAppF.Λα::U.λr:Expα.r[F]aptatp of type F::TyTy.AbsFAppFTAbsFTAppFα::U.ExpαOpFα.

Lemma 7.65 — Fold calculation

If D is closed and DFq, then applying foldExp to a constructor G, four case terms, A^, and D^ reduces to q with F and the four designated case variables simultaneously replaced by those arguments.

Proof of Lemma 7.65 — Fold calculation

Proof. Writing the four case arguments as a,p,ta,tp, expansion gives the complete head calculation foldExp[G]aptatp[A^]D^βunfoldfoldExpD^[G]aptatpβunfoldD^q[G/F,a/abs,p/app,ta/tabs,tp/tapp]. No induction is needed: this is the beta law of the Church encoding, in the sense explained for Church naturals in definition 5.18. ◻

The internal unquoter

Take the identity type operator Id:=λA::Ty.A and the following four cases: unAbs:=ΛA::Ty.ΛB::Ty.λf:AB.f,unApp:=ΛA::Ty.ΛB::Ty.λf:AB.λx:A.fx,unTAbs:=ΛA::Ty.λs:StripIdA.λf:A.f,unTApp:=ΛA::Ty.λf:A.ΛB::Ty.λg:AB.gf. They have types AbsId, AppId, TAbsId, and TAppId respectively. Define the ordinary internal Fω term unquote:=foldExp[Id]unAbsunAppunTAbsunTApp,unquote:α::U.ExpαOpIdα.

Lemma 7.66 — Recovery of represented types

For every well-kinded constructor A, preF(A)[Id/F]βA. If A is a closed type, then OpIdA^=βA.

Proof of Lemma 7.66 — Recovery of represented types

Proof. Induct on A. A variable is unchanged. For an arrow, the induction hypotheses and the two contractions IdXβX give IdpreId(A)βA,IdpreId(B)βB. For a universal, contract its one occurrence of Id and reduce under the binder. Constructor abstraction reduces under its binder, and constructor application uses the two induction hypotheses in its operator and argument positions. Hence the variable, arrow, universal, constructor- abstraction, and constructor-application clauses all reduce to their original types under Id.

Finally, OpIdA^βunfoldOpId(A^Id)βunfoldA^preId(A)βinductionhypothesisA. ◻

Lemma 7.67 — Unquoting a prequotation

Let DFq. Substitute Id for F and the four terms in (7.14) for the four case variables. The resulting term reduces by β to the source term of D. By term-only reduction it reaches a term ty-equivalent to that source.

Proof of Lemma 7.67 — Unquoting a prequotation

Proof. Induct on D. A variable is unchanged. In the term-abstraction case, the unAbs case contracts to the copied abstraction, and the induction hypothesis reduces its body: unAbs[preId(A)][preId(B)](λx:IdpreId(A).q)βunfoldunAbs,thenbetaλx:A.e. By lemma 7.66, IdpreId(A)βA and IdpreId(B)βB, so constructor conversion validates the annotations in this calculation. In term application, unApp applies the two induction-hypothesis results and hence reduces to e1e2.

In type abstraction, unTAbs ignores its strip argument and returns the copied type abstraction; compatible reduction and the induction hypothesis give Λu::κ.e.

For type application, write P=preId(u::κ.A) and Q=preId(C). The decisive contractions are unTApp[P]q[preId(A[C/u])]instP,QβinstP,Qqβ(7.8)q[Q]βIHandtyperecoverye[C]. If the last rule of D is conversion from A to B, the induction hypothesis unquotes the unchanged term q at A, and constructor conversion assigns the same term type B. The variable, abstraction, application, constructor-abstraction, constructor-application, and conversion cases exhaust the derivation rules. ◻

Theorem 7.68 — Strong typed self-interpretation

If D is a closed derivation of e:A in the pure Fω signature of lemma 7.56, then unquote[A^]D^βe,unquote[A^]D^:A.

Proof of Theorem 7.68 — Strong typed self-interpretation

Proof. The typing judgment follows from lemma 7.64, lemma 7.66 and the type of (7.15). By lemma 7.65, the term reduces to the prequotation with F and the four cases replaced by the unquoting choices. By lemma 7.67, that term reduces to e. ◻

Exercise 17.5

★★☆ Work the constructor-application case of lemma 7.67 without abbreviating the unTApp and inst redexes. Starting from the prequotation of e[C], display every term-beta contraction that exposes the recursive quotation of e instantiated at the represented constructor C. Mark the two places where lemma 7.66 supplies constructor conversion.

The shallow and deep representations, their internal unquoters, and the fold interface follow Brown and Palsberg [BP16]; the typing and reduction obligations used here are stated and proved locally.

Corollary 7.69 — Separation of represented beta classes

Let D1 and D2 be closed derivations of e1:A and e2:A. If D1^βmixD2^, then e1βmixe2.

Proof of Corollary 7.69 — Separation of represented beta classes

Proof. Mixed beta equivalence is a congruence, so apply the same internal term unquote[A^] to both quotations. The two results remain equivalent. By theorem 7.68, they reduce respectively to e1 and e2. ◻

There is a genuine self-application calculation, but it contains a quotation, not the forbidden raw diagonal application. Let Tu:=α::U.ExpαOpIdα be the type of unquote, and let Du be its closed typing derivation. Applying the theorem to that derivation gives unquote[Tu^]Du^βunquote. Quotation remains a meta-level operation. Equation (7.16) therefore constructs no internal term of type AExpA^ and does not contradict the normalization barrier of proposition 7.57. Every term in this calculation is an ordinary well-typed term of pure Fω; adding no term former or reduction rule means that the strong normalization and syntactic-consistency results for that calculus remain unchanged.

Recognizing the outer constructor

Use the Church booleans Bool:=X::Ty.XXX,true:=ΛX::Ty.λt:X.λf:X.t,false:=ΛX::Ty.λt:X.λf:X.f,KBool:=λA::Ty.Bool. The four cases need not inspect their recursive arguments: isAbsAbs:=ΛA.ΛB.λf:BoolBool.true,isAbsApp:=ΛA.ΛB.λf:Bool.λx:Bool.false,isAbsTAbs:=ΛA.λs:StripKBoolA.λf:A.true,isAbsTApp:=ΛA.λf:Bool.ΛB.λg:ABool.false. Every constructor binder without an explicit kind in (7.17) has kind Ty. The four terms have the four case types at KBool. Since OpKBoolα=βBool, define isAbs:=foldExp[KBool]isAbsAbsisAbsAppisAbsTAbsisAbsTApp,isAbs:α::U.ExpαBool.

Theorem 7.70 — Correctness of the abstraction test

Let D be a closed derivation of e:A. If e is a term abstraction or a type abstraction, then isAbs[A^]D^βtrue. If e is a term application or a type application, the same expression reduces to false.

Proof of Theorem 7.70 — Correctness of the abstraction test

Proof. The fold calculation exposes the outermost prequotation clause. In the two abstraction clauses, the corresponding case in (7.17) discards its recursive argument and returns true. In the two application clauses it returns false. A closed term cannot have a free variable at its root, and conversion adds no term constructor. These exhaust the possibilities. ◻

Counting term nodes

Define Church natural numbers inside pure Fω by Nat:=X::Ty.X(XX)X,zero:=ΛX.λz:X.λs:XX.z,succ:=λn:Nat.ΛX.λz:X.λs:XX.s(n[X]zs),plus:=λm:Nat.λn:Nat.m[Nat]nsucc,one:=succzero,KNat:=λA.Nat. Let n denote the Church numeral obtained by applying succ n times to zero. Then succnβn+1,plusmnβm+n. The successor equation is definitional; direct beta calculation gives the addition equation.

The size of a term counts term nodes and does not count constructors in an annotation or type argument: |x|=1,|λx:A.e|=1+|e|,|e1e2|=1+|e1|+|e2|,|Λu::κ.e|=1+|e|,|e[C]|=1+|e|. The cases implementing these five equations are sizeAbs:=ΛA.ΛB.λf:NatNat.succ(fone),sizeApp:=ΛA.ΛB.λm:Nat.λn:Nat.succ(plusmn),sizeTAbs:=ΛA.λs:StripKNatA.λf:A.succ(s[Nat](ΛC.λn:Nat.n)f),sizeTApp:=ΛA.λm:Nat.ΛB.λg:ANat.succm. Their types are the four case types at KNat. Therefore size:=foldExp[KNat]sizeAbssizeAppsizeTAbssizeTApp,size:α::U.ExpαNat.

Theorem 7.71 — Correctness of size

If D is a closed derivation of e:A, then size[A^]D^β|e|.

Proof of Theorem 7.71 — Correctness of size

Proof. The induction must be slightly stronger than the closed statement, because a term abstraction exposes a variable in its body. Let DFq be an open prequotation. For every kind-respecting closing constructor substitution θ for the open constructor context, first apply θ to q. After substituting KNat and the four size cases, also substitute one for every free term variable. We prove that the result reduces to |e|. Constructor substitution changes annotations and type arguments but not the term-constructor count.

For a variable, the extra substitution gives one=1=|x|. For term abstraction, the case term calculates sizeAbs[](λx:Nat.q)βsucc(q[one/x]). The induction hypothesis gives q[one/x]β|e|, so (7.18) gives 1+|e|. Term application gives succ(plus|e1||e2|)β1+|e1|+|e2|.

In type abstraction, the recursive value is Λu::κ.q. Calculation (7.10), with X=Nat, reduces the stripping expression to q[Sκ/u]. Apply the body induction hypothesis to the closing substitution θ[uSκ]. It is kind respecting because Sκ::κ, and it gives the required size of q[Sκ/u]. Thus term size ignores the substituted type and remains |e|. The outer successor therefore gives 1+|e|. In type application, sizeTApp ignores the instantiation function and returns the successor of the operator size, namely 1+|e|. Conversion changes neither prequotation nor size. This proves the strengthened assertion in every case. With an empty term context, the fold calculation yields the theorem. ◻

Testing beta-normality

A Boolean does not carry enough information through an application. To decide whether e1e2 is normal, one must know not only that e1 is normal, but also that it is neutral and therefore cannot become a lambda redex at the root. We carry two booleans.

Define and:=λb1:Bool.λb2:Bool.ΛX.λt:X.λf:X.b1[X](b2[X]tf)f,Bools:=X::Ty.(BoolBoolX)X,bools:=λb:Bool.λn:Bool.ΛX.λk:BoolBoolX.kbn,fst:=λp:Bools.p[Bool](λb:Bool.λn:Bool.b),snd:=λp:Bools.p[Bool](λb:Bool.λn:Bool.n),KBools:=λA.Bools. Define the three pairs TT:=boolstruetrue,TF:=boolstruefalse,FF:=boolsfalsefalse.

Definition 7.72 — Neutral and normal terms

The neutral terms n are variables and their iterated eliminations; the beta-normal terms v are neutral terms or abstractions. They are described mutually by n::=xnvn[C],v::=nλx:A.vΛu::κ.v. Thus every neutral term is normal. A normal term which is not neutral is an abstraction. These grammars speak only about term redexes; a constructor C need not be constructor-normal.

Lemma 17.19 — Normal applications have neutral operators

For a well-typed application this grammar yields the two exact tests e1e2 is normale1 is normal and neutral, and e2 is normal,e[C] is normale is normal and neutral.

Proof of Lemma 17.19 — Normal applications have neutral operators

Proof. For completeness, if a normal operator is not neutral, the grammar says it is a term abstraction or a type abstraction. Typing rules out the wrong one: a type abstraction has universal type, not arrow type, and a term abstraction has arrow type, not universal type. A final conversion cannot identify these heads, because constructor normalization gives distinct normal-form heads for and . Hence an arrow-typed nonneutral normal operator is a term abstraction, producing a term beta-redex, and a universal-typed one is a type abstraction, producing a type-application redex. This proves both directions of (7.20). ◻

Now define the four cases: nfAbs:=ΛA.ΛB.λf:BoolsBools.bools(fst(fTT))false,nfApp:=ΛA.ΛB.λp:Bools.λq:Bools.bools(and(sndp)(fstq))(and(sndp)(fstq)),nfTAbs:=ΛA.λs:StripKBoolsA.λf:A.bools(fst(s[Bools](ΛC.λp:Bools.p)f))false,nfTApp:=ΛA.λp:Bools.ΛB.λg:ABools.bools(sndp)(sndp). They inhabit the four case types at KBools. The public test returns the first component: isNormal:=Λα::U.λr:Expα.fst(foldExp[KBools]nfAbsnfAppnfTAbsnfTApp[α]r) with type α::U.ExpαBool.

Lemma 7.73 — The three-state invariant

Let DFq be an open prequotation. For every kind-respecting closing constructor substitution θ for the open constructor context, first apply θ to q. Then substitute KBools, the four cases in (7.21), and TT for each free term variable. The resulting closed term reduces by β to the pair in the second column of the following table: state of the source term eresulte normal and neutralTTe normal and not neutralTFe not normalFF.

Proof of Lemma 7.73 — The three-state invariant

Proof. Induct on the source typing derivation. A variable is replaced by TT, as required.

For a term abstraction, nfAbs applies the recursive function to TT, exactly the value assigned to the newly bound variable. By the induction hypothesis the first projection is true precisely when the body is normal. The second result component is false. The result is therefore TF for a normal abstraction and FF for a nonnormal one.

For term application, let p and q be the two recursive result pairs. The case puts the same Boolean (sndp)and(fstq) in both components. By the induction hypotheses this Boolean is true exactly when the operator is normal and neutral and the operand is normal. By (7.20), this is exactly when the whole application is normal; in that event the application is neutral. Thus the result is TT in the normal case and FF otherwise.

For type abstraction, the stripping calculation (7.10) with X=Bools selects the body at Sκ. Apply the body induction hypothesis to the closing substitution θ[uSκ], which is kind respecting because Sκ::κ. The case copies its first component and sets the second to false, giving TF exactly when the body, and hence the abstraction, is normal; otherwise it gives FF.

For type application, the case copies the operator’s second component into both result positions. It therefore returns TT exactly when the operator is normal and neutral, and FF otherwise. This is exactly the second equivalence of (7.20); a normal type application is itself neutral. Conversion changes no term and hence no state. The application equation in (7.20) holds exactly when the operator is neutral-normal and the operand is normal. ◻

Theorem 7.74 — Correctness of the normal-form test

For a closed derivation D of e:A, isNormal[A^]D^β{true,e is beta-normal,false,e is not beta-normal.

Proof of Theorem 7.74 — Correctness of the normal-form test

Proof. By the fold calculation and lemma 7.73, the intermediate pair is TT or TF precisely when e is normal, and is FF otherwise. Its first projection is respectively true or false. ◻

Exercise 7.19

★☆☆ Let e=(ΛA::Ty.λx:A.x)[X::Ty.XX]. Determine |e|, decide whether e is normal, and compute the results of isAbs, size, and isNormal on its deep quotation. In the normality calculation, name the three-state pair obtained for the operator before the final type-application case is used.

Typed CPS consumers

Continuation-passing style (CPS) replaces a computation returning A by a computation that receives a continuation AB and sends its result to that continuation. The common answer operator and result family for the call-by-name and call-by-value folds are Ct:=λA::Ty.B::Ty.(AB)B,CPS:=OpCt. The call-by-name case operators are the following pure Fω terms: cpsAbsn:=ΛA.ΛB.λf:CtACtB.ΛV.λk:(CtACtB)V.kf,cpsAppn:=ΛA.ΛB.λf:Ct(CtACtB).λx:CtA.ΛV.λk:BV.f[V](λg:CtACtB.gx[V]k),cpsTAbsn:=ΛA.λs:StripCtA.λf:A.ΛV.λk:AV.kf,cpsTAppn:=ΛA.λf:CtA.ΛB.λg:ACtB.ΛV.λk:BV.f[V](λx:A.gx[V]k). They have the four case types at Ct, so their fold has type cpsn:α::U.ExpαCPSα. The call-by-name abstraction case performs the continuation transfer. For f:CtACtB and k:(CtACtB)V, cpsAbsn[A][B]f[V]kβtypebetaandtermbetakf. The application kf has type V, the codomain of the continuation. Thus this calculation proves the declared case typing; it does not assert an operational simulation of source evaluation. The call-by-value artifact uses the same three application and type cases. It replaces the abstraction case by cpsAbsv:=ΛA.ΛB.λf:CtACtB.ΛV.λk:(CtACtB)V.k(λx:CtA.x[CtB](λa:A.f(ΛW.λh:AW.ha))), and its fold has the same public type α::U.ExpαCPSα.

Construction boundaries

For every derivation D of e:A, the external map constructs D^:ExpA^. The internal term unquote:α::U.ExpαOpIdα sends this representation back to a term of A. Each CPS fold instead has type α::U.ExpαCPSα for its fixed continuation operator. The first map is derivation-directed; the latter two are Fω terms. These types neither make every inhabitant of ExpA^ a quotation nor assert that the CPS term simulates source evaluation.

The classical diagonal obstruction also still applies internally at the numeric interface. There is no closed V:NatNatNat such that every closed f:NatNat has a numeral a with Van=βfn for every n. Otherwise d:=λn.succ(Vnn) would have an index a, and the instance at a would equate a Church numeral with its successor. Strong normalization and confluence distinguish those normal forms. Typed self-representation evades the raw diagonal only through its type index; it does not enumerate all total numeric functions.

Example 7.75 — A representation-type inhabitant which is not a quotation

The failure of surjectivity is already visible in the shallow construction. The closed term junksh:=λi:I.i:II is not the shallow quotation of any closed derivation of a term of type I. Indeed, such a quotation has the form λi:I.q. If the source ends in an abstraction, then q begins with the copied abstraction. If it ends in an application, then q is headed by i applied to at least one type and one term argument. A closed source cannot end in a variable, and conversion does not change q. A source constructor abstraction makes q begin with the copied constructor abstraction; a source constructor application makes q headed by i applied to a constructor argument. None gives the bare body i. Both junksh and every shallow quotation are normal, so confluence also rules out beta-equivalence between them.

Example 17.23 — A deep representation inhabitant which is not a quotation

Fix a closed type B. For each fold parameter F, abbreviate AF:=preF(B),PF:=FAFFAF,qF:=abs[AF][AF](λx:FAF.x):FPF. Then the closed term junkB:=ΛF::TyTy.λabs:AbsF.λapp:AppF.λtabs:TAbsF.λtapp:TAppF.tapp[PF]qF[PF](λz:PF.qF) has type Exp(BB)^, because preF(BB)=PF. It is not a quotation. A genuine outer type-application quotation clause has as its first child a quotation of a term with universal type, so its first type parameter has the form preF(u::κ.C). Here that parameter is the arrow-normal constructor PF, and its child qF is headed by the term-abstraction case. Constructor normalization and outer-form injectivity rule out their equality. The case interface is intentionally large enough to admit this well-typed, non-syntactic inhabitant.

Suggested first pass.

Begin with exercise 17.7, reconstruct the open quotation argument in exercise 17.8, and test the boundary in exercise 17.9 before implementing the finite oracle.

Exercise 17.7

★☆☆ For the quotation of (λx:I.x)idI, calculate the size fold and the outer-constructor test clause by clause. Compare the result with the source syntax tree, and identify the case or copied-variable contribution responsible for each unit in the returned Church numeral.

Exercise 17.8

★★☆ Generalize the deep quotation theorem to the one-variable judgment x:Ae:B. State the type of the prequotation under the copied context, formulate the corresponding unquotation claim, and reconstruct the variable, both abstraction, both application, and conversion cases. Say precisely which closure step from theorem 7.68 is no longer available.

Exercise 17.9

★★☆ Use example 17.23 to prove that typing at Exp(BB)^ does not characterize the quotation image, without using term strong normalization. Next suppose hypothetically that, for every closed type A, there is a total closed term quoteA:AExpA^ whose result is stipulated to be the genuine quotation of each closed input. Determine whether these fixed-index quoters suffice to reinstate the diagonal obstruction from the opening section. Reconstruct the constructor equalities that the raw self-application would still require, and keep this typing calculation separate from the non-surjectivity argument and the normalization contradiction.

Exercise 17.10

★★★ Practical project.selfrepr-fold-checker Implement the finite four-constructor quotation and fold fragment in artifacts/ch17-fomega-selfrepr/corpus.kp. Stage 1 represents term and type abstraction and application as distinct data constructors. Stage 2 implements structural quotation/unquotation. Stage 3 implements the size and normality folds. Stage 4 rejects a distinguished inhabitant outside the quotation image. Maintain the invariant that unquoting a produced quotation recovers the original tree and that fold size agrees with direct size.

Run kappa check, kappa test, kappa run, and kappa audit. Acceptance is the six printed PASS lines and the final line All 6 typed-self- representation corpus cases passed., with an empty audit. Then replay separately the three README mutations: swap the two application children, omit the argument from the size fold, and accept a lambda-headed application as normal. Each mutant must still check but make kappa test fail. This finite oracle checks the displayed fold equations; it is not a proof of full Fω kinding or normalization.

Search the book

Type to search the local edition.