Lectures onType Theory
Chapter 137
Chapter 137Optional

Dependent Closure Conversion for the Calculus of Constructions

Prerequisites. Direct starred prerequisites: Chapter 133 and chapter 26. No later core chapter depends on this route.

Closure conversion in chapter 133 hid the environment behind an existential. The reason that worked is that a simply typed function’s type mentions nothing from its environment. In the calculus of constructions it does.

Take the polymorphic identity, written with the two abstractions separated: λA:. λx:A. x. Converting the inner abstraction produces code whose environment holds A, so the code’s argument type must say “the first component of the environment”: λ(n2:×1,x:fst n2).x, A,. Now hide the environment type behind an existential, as one must for two closures of the same type with different environments to be interchangeable. The code’s type becomes Π(n2:α2,x:fst n2).fst n2. Read that type: it projects the first component of something whose type is the abstract variable α2. Projection applies to pairs, and α2 is not a pair. The type is not well formed. Hiding the environment to recover type preservation has destroyed type preservation.

This chapter reconstructs the solution of Bowman and Ahmed (2018): abandon the existential, add primitive code and closure forms to the target, and give closures an equivalence principle strong enough that the translation of a Π type is the Π type of the translations.

The source calculus

Definition 137.1 — CC

Universes are U::=, with impredicative and predicative. Expressions are e,A,B::=xλx:A.ee1e2Πx:A.Blet x=e:A in eΣx:A.Be1,e2fst esnd e. Reduction Γe:e has the clauses xe when x=e:AΓ(δ),(λx:A.e1)e2e1[e2/x](β),let x=e:A in e1e1[e/x](ζ),fst e1,e2e1(π1), and snd e1,e2e2. Equivalence Γe1e2 holds when both reduce to a common expression, or by either η rule: if e1λx:A.e, e2e2 and Γ,x:Aee2x, then Γe1e2, and symmetrically.

Definition 137.2 — Typing

Γ
Γ:
Ax-★
Γx:AΓ or x=e:AΓ
Γx:A
Var
Γe:AΓ,x=e:Ae:B
Γlet x=e:A in e:B[e/x]
Let
Γ,x:AB:U
ΓΠx:A.B:U
Prod
Γ,x:Ae:B
Γλx:A.e:Πx:A.B
Lam
Γe:Πx:A.BΓe:A
Γee:B[e/x]
App
Γe:AΓABΓB:U
Γe:B
Conv
ΓA:Γ,x:AB:
ΓΣx:A.B:
Sig

Conv is what makes the rest of this chapter possible: a term keeps its type up to reduction, so a translation may produce a type that merely reduces to the intended one.

Two solutions that do not scale

Construction 137.3 — The existential encoding

The standard translation for simply typed and polymorphic languages sets (Πx:A.B)+=α:U.n:α. Code(n:α, y:n=n, x:A+).B+, quantifying over the environment’s type α and value n, and giving the code a proof y that it receives exactly n. Packing copies the witness into the type, so a closure packA,v,e type-checks when e has the type above with n replaced by v.

Proposition 137.4 — The existential encoding needs impredicativity

Suppose construction 137.3 is type preserving. Then for every A,B with ΓΠx:A.B:, the type α:U. also has type . Since U ranges over both universes, the existential quantifier must be impredicative.

Proof of Proposition 137.4 — The existential encoding needs impredicativity

Proof. Type preservation requires (Πx:A.B)+ to inhabit the translation of the universe of Πx:A.B, which is . The environment of a closure may contain a type, so α may be instantiated at ; a quantifier over whose result lies in is impredicative by definition. ◻

That is not an idle restriction. A computationally relevant universe is predicative in the default configuration of the proof assistant this calculus models, impredicativity at more than one relevant universe is unsound, and some dependently typed languages admit none at all. A second obstruction is independent of the first: an η principle for existentially packaged closures would have to be justified by a parametric relation on environments, and dependent type theories do not all admit parametricity.

Remark 137.5 — Why translucency does not rescue it

For System F the repair is to add singleton types, type-level pairs and kinds, so that the environment’s type can be described precisely enough to project from. Every one of those features is encodable in CC, so the repair transfers syntactically — and proposition 137.4 still applies, because the existential is still there. A different repair for System F erases types before run time, so code need not close over type variables; in CC types contain term variables, and there is nothing to erase.

The target: CC with primitive closures

Definition 137.6 — CC-CC

Extend the expressions of definition 137.1 with e,A,B::=1Code(x:A,x:A).Bλ(x:A,x:A).ee,e, with typing

Γ,x:A,x:AB:U
ΓCode(x:A,x:A).B:U
T-Code
,x:A,x:Ae:B
Γλ(x:A,x:A).e:Code(x:A,x:A).B
Code
Γe:Code(x:A,x:A).BΓe:A
Γe,e:Πx:A[e/x].B[e/x]
Clo

and the reduction λ(x:A,x:A).e1,eee1[e/x][e/x].

Three features of definition 137.6 carry the whole solution.

First, Code types its body in the empty context. That single premise is the entire content of typed closure conversion: it is a machine-checked guarantee that the translation produces closed code, and it is checked by the target’s own type checker rather than by a metatheorem.

Second, Clo substitutes the environment into the closure’s type. A closure e,e is therefore not a pair but a delayed partial application of e to e, and its type is computed exactly as dependent application computes the type of ee. Nothing is hidden, and nothing needs to be.

Third, the closure type is an ordinary Π type. There is no new type former for closures, so the translation of a source Π has somewhere to go.

Definition 137.7 — Closure equivalence

Two closures are equivalent when inlining their environments and reducing makes their bodies equivalent:

Γ,x:Ae1[e1/x]e2[e2/x]
Γλ(x:A,x:A).e1,e1λ(x:A,x:A).e2,e2
-Clo

The algorithmic form used in the metatheory is the pair of one-sided rules

Γe1λ(x:A,x:A).e1,eΓe2e2Γ,x:Ae1[e/x]e2x
Γe1e2
-Clo_1

and its mirror image.

Remark 137.8 — The problem the rule solves

Type checking in CC reduces terms. Two syntactically identical closures can become distinct when reduction inlines a term into one environment and not the other, and a naive syntactic comparison would then call them inequivalent. Definition 137.7 removes the possibility by never comparing environments at all: it inlines both and compares the resulting bodies with the argument left free. A consequence is a normal form for closures in which the environment is a tuple of variables.

The translation

Definition 137.9 — Closure conversion

Write FV(e,Γ)=x1:A1, for the free variables of e in Γ, ordered so that each Ai mentions only earlier ones. Then (Πx:A.B)+=Πx:A+.B+,(λx:A.e)+=λ(n:Σ(xi:Ai+), x:let xi=n in A+).let xi=n in e+, xi, where xi:Ai=FV(λx:A.e,Γ); every other form translates componentwise.

The first clause is the surprise. The translation of a dependent function type is the dependent function type of the translations — exactly as if nothing had happened. The next lemma says why that is consistent with definition 137.6.

Lemma 137.10 — The closure's type reduces to the translated type

Write Ax for let xi=n in A+, let ecode=λ(n:An,x:Ax).let xi=n in e+, and let v=xi. Then Clo gives the closure ecode,v the type Πx:(let xi=n in A+)[v/n].(let xi=n in B+)[v/n], and that type reduces by π1, π2 and ζ steps to Πx:A+.B+.

Proof of Lemma 137.10 — The closure's type reduces to the translated type

Proof. Substituting v=xi for n turns each let xi=n in C into let xi=xi in C. Destructuring a literal tuple is |i| projections followed by |i| ζ steps, each binding xi to itself, so the result is C with each xi replaced by xi: that is C. Applying this to A+ and to B+ gives the claim. ◻

So Conv closes the gap between the type Clo assigns and the type definition 137.9 declares, and the declared type mentions no environment. Two closures of the same source type have the same target type whatever their environments contain, which is what the existential was for.

Example 137.11 — The polymorphic identity, again

The inner closure of λA:.λx:A.x is λ(n:Σ(A:), x:let A=n in A).let A=n in x, A. Its code is closed and type-checks by Code. By Clo its type is Πx:(let A=A in A).(let A=A in A), which reduces to Πx:A.A by lemma 137.10. Compare the opening: there the type contained fst n2 with n2 of an abstract type, and no reduction was possible.

Exercise 137.1

★★☆ Translate the outer abstraction of example 137.11 and give the resulting nested closure in full, together with the type Clo assigns to it and the type it reduces to. Say which free variable each environment contains and why the outer one is empty.

Type preservation

Lemma 137.12 — Compositionality

(e1[e2/x])+=e1+[e2+/x].

Proof of Lemma 137.12 — Compositionality

Proof. Induction on e1. The case that is not immediate is the abstraction, and it is the reason the lemma is the technical heart of the proof: substituting before translating changes which variables are free, hence the shape of the environment. Let e1=λy:A.e with FV(e1,Γ)=xi:Ai. If x is not among the xi, both sides build the same environment and the induction hypothesis applies componentwise. If x=xj, then substituting first removes xj from the free variables and inserts FV(e2,Γ); substituting afterwards leaves the environment xi with xj replaced by e2+. The two results differ syntactically, and definition 137.7 identifies them: both closures have the same code up to inlining the environment, and inlining is exactly what the equivalence rule performs. The statement is therefore proved up to , which is all the later uses require. ◻

Lemma 137.13 — Preservation of reduction

If Γee then Γ+e+e+; and hence for reduction sequences.

Proof of Lemma 137.13 — Preservation of reduction

Proof. Most clauses of definition 137.1 are defined by substitution, so lemma 137.12 does the work. The β case is the one that is not. The translation of (λx:A.e1)e2 applies a closure to the translation of e2, and by definition 137.6 that reduces to (let xi=n in e1+)[xi/n][e2+/x]. By |i| ζ steps that is e1+[e2+/x], which by lemma 137.12 is (e1[e2/x])+. ◻

Lemma 137.14 — Coherence

If Γee then Γ+e+e+.

Proof of Lemma 137.14 — Coherence

Proof. Induction on the equivalence derivation. The common-reduct case is lemma 137.13. The η cases are where definition 137.7 is needed. Suppose eλx:A.e1, ee2 and Γ,x:Ae1e2x. By lemma 137.13, e+ reduces to something equivalent to (λx:A.e1)+ and e+ to something equivalent to e2+, so by transitivity it suffices to relate those two. Rule -Clo1 reduces that obligation to let xi=xi in e1+e2+x; the left side is e1+ after |i| ζ steps, and the induction hypothesis applied to e1e2x finishes. ◻

Theorem 137.15 — Type preservation

If Γ then Γ+; and if Γe:A then Γ+e+:A+.

Proof of Theorem 137.15 — Type preservation

Proof. Simultaneous induction on the derivations.

Lam. Let Γλx:A.e1:Πx:A.B with Γ,x:Ae1:B, and let xi:Ai be the free variables of e1, A and B. The induction hypothesis at ΓA:U, weakened, gives n:Σ(xi:Ai+)let xi=n in A+:U+. The induction hypothesis at Γ,x:Ae1:B, weakened, gives ,n,xlet xi=n in e1+:let xi=n in B+ — the context is empty because xi are all the free variables, which is the premise Code demands. So Code types the code, Clo types the closure, and lemma 137.10 together with Conv moves that type to Πx:A+.B+=(Πx:A.B)+.

App. Let Γe1e2:B[e2/x]. By lemma 137.12 it suffices to show Γ+e1+e2+:B+[e2+/x], which is App in the target applied to the two induction hypotheses, since e1+ has type Πx:A+.B+ by the hypothesis at e1.

Conv. Lemma 137.14 transports the equivalence, and Conv in the target reapplies. The remaining rules translate componentwise. ◻

Consistency of the target

Adding typing rules to a dependent type theory is adding axioms to a logic. Definition 137.6 must therefore be shown consistent, and shown not to lose any proof the source had.

Construction 137.16 — Back-translation

Define from CC-CC to CC by erasing the primitive forms: Code(x:A,x:A).B becomes Πx:A.Πx:A.B, the code abstraction becomes the corresponding double abstraction, and e,e becomes ee.

Lemma 137.17

False=False, where False=ΠA:.A; the two are syntactically identical.

Theorem 137.18 — Consistency and type safety of CC-CC

There is no closed e with e:False in CC-CC; and if e:A then ev with v irreducible.

Proof of Theorem 137.18 — Consistency and type safety of CC-CC

Proof. For consistency: the back-translation preserves typing — Clo becomes App because the closure’s type is the application’s type, and Code becomes two Lam steps — and preserves equivalence, including the closure rule of definition 137.7, which becomes the η rule of definition 137.1 after the two applications are performed. A closed CC-CC proof of False would therefore back-translate, by lemma 137.17, to a closed CC proof of False, and CC has none. Type safety follows from the same transport together with normalization for CC. ◻

Remark 137.19

Two transports are needed and they do different work. Theorem 137.15 goes forwards and says the compiler produces well-typed target code. Theorem 137.18 goes backwards and says the target’s new rules prove nothing the source could not. A translation could satisfy the first with an inconsistent target, in which case the target’s type checker would accept everything.

Separate compilation

Definition 137.20 — Components and linking

A component is a well-typed open term Γe:A. Linking is substitution by a closing substitution γ with Γγ when γ(x):A for every x:AΓ. The compiler is extended to substitutions pointwise. Fix a ground type with a relation vv on its values — for booleans, truetrue and falsefalse.

Theorem 137.21 — Correctness of separate compilation

Let Γe:A with A ground, Γγ, Γ+γ, γ(e)v, and γ+γ. Then γ(e+)v with v+v.

Proof of Theorem 137.21 — Correctness of separate compilation

Proof. The square (γ(e))+γ(e+)v+v commutes. The top edge is lemma 137.12 together with the hypothesis γ+γ and lemma 137.14. The left edge is lemma 137.13 applied to γ(e)v; the right edge is reduction of γ(e+), which terminates by theorem 137.18. Transitivity of gives v+v, and at a ground type equivalence implies . ◻

Corollary 137.22 — Whole-program correctness

If e:A with A ground and ev, then e+v with v+v.

Exercise 137.2

★★☆ Show that theorem 137.21 becomes false if “A ground” is dropped, by exhibiting a source component of function type whose translation is equivalent to, but not observationally identical to, the translation of its value. Then say which step of the proof used groundness.

Exercise 137.3

★☆☆ Delete the emptiness of the context from the premise of Code and give a target term that becomes well typed and whose code is not closed. Say which sentence of section 137.3 that premise was carrying.

Limits and seminar

The frozen source is Bowman and Ahmed (2018). Its definitions are definition 137.1, definition 137.2, definition 137.6, together with definition 137.7 and definition 137.9. Its theorems, retained at their exact statements, are lemma 137.12, lemma 137.13, lemma 137.14 and theorem 137.15, theorem 137.18, theorem 137.21, with corollary 137.22. Proposition 137.4 and lemma 137.10 are local reconstructions of arguments the paper gives informally, and the proofs written out above are local.

Five boundaries. The source is the calculus of constructions with Σ types and η; there are no inductive types, so this is not a translation for the calculus of inductive constructions and nothing here covers pattern matching, fixed points or universe polymorphism. Only one compiler pass is treated: there is no continuation-passing pass before it and no allocation pass after it, and theorem 137.21 is about this pass alone. Theorem 137.21 is stated at ground types, so it says nothing about components whose interface is a function type; a compositional statement would need a relation between source and target components chosen independently of the compiler. The equivalence of definition 137.7 is presented declaratively here and its algorithmic form is what a checker would implement; decidability of the target’s conversion is claimed by the source and not proved here. Finally, the target is a high-level dependently typed language, not assembly: theorem 137.18 guarantees safety of linking any two well-typed CC-CC components, and says nothing about machine code.

[4]

Suggested first pass.

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

Exercise 137.4

★★★ Carry construction 137.3 through on λA:.λx:A.x, writing every type in full, and identify the first judgment that is underivable. Then add singleton types and type-level pairs as in remark 137.5, redo the derivation, and locate the step that requires impredicativity.

Exercise 137.5

★★★ Give two closures with the same code but environments that reduction has inlined differently, show that they are not syntactically equal, and derive their equivalence with -Clo. Then show that deleting that rule makes lemma 137.14 false, by exhibiting a source equivalence whose translation is no longer derivable.

Exercise 137.6

★★★ Add a single inductive type with one recursive constructor and its eliminator to definition 137.1. Extend definition 137.9 to it, and state precisely which of lemma 137.12, lemma 137.13, theorem 137.15 you can still prove and where the argument stops.

Exercise 137.7 — Practical: closure conversion checker

★★★ Practical project.dcc-closure-checker Complete project dcc-closure-checker. Implement, for the fragment of CC with , variables, dependent functions, application and Σ types with projections, (i) a type checker for CC and one for CC-CC including Code, Clo and Conv, (ii) reduction to weak head normal form for both, and (iii) the translation of definition 137.9 together with the free-variable sequence. The invariant the implementation must maintain is theorem 137.15 on every accepted input: a term accepted by the CC checker at type A must translate to a term accepted by the CC-CC checker at A+. The named cases print

polymorphic-identity: accepted
closure-type-reduces: ok
open-code: rejected
existential-projection: rejected
type-preserved: ok

The checker is independent evidence on the named inputs; it does not prove theorem 137.15, implements neither definition 137.7 beyond reduction to a common form nor the back-translation of construction 137.16, and establishes nothing about theorem 137.18 or theorem 137.21. If the environment is built as a right-nested pair, record whether the implementation handles a component whose type mentions an earlier component; if it does not, include a source term with such an environment among the named inputs and check that the translation declines it, so that the restriction is visible rather than silent.

Search the book

Type to search the local edition.