A record can contain functions, but selecting a function does not pass it the record that was selected. Passing the record when the function is built is also insufficient. After one method has been replaced, every other method must invoke the replacement through the new receiver. A receiver binder is the variable that invocation replaces by the current object, rather than an ordinary stored argument. Equivalently, the object is a finite system of mutually recursive equations 𝑥𝑖=𝑏𝑖(¯𝑥). Replacing one equation should make every other equation refer to the replacement when the system is solved again; an ordinary record of already-closed functions solves the system once and freezes those references. The receiver binder is the syntax that keeps the equations open until invocation.
A receiver that survives replacement
Ground terms are variables, natural numerals, 𝗎𝗇𝗂𝗍, and 𝗌𝗎𝖼𝖼(𝑎), with 𝑛:𝖭𝖺𝗍,𝗎𝗇𝗂𝗍:𝖴𝗇𝗂𝗍,𝑎:𝖭𝖺𝗍𝗌𝗎𝖼𝖼(𝑎):𝖭𝖺𝗍. Numerals and unit are values, 𝗌𝗎𝖼𝖼(𝑛)⟼𝑛+1, and 𝗌𝗎𝖼𝖼(𝐸) is an evaluation context. These forms let the running methods return observable numerals.
The word object here means a runtime value: one receiver together with its methods. There are no class declarations, constructors, or nominal class types in this calculus. A class, if a source language had one, would instead be a separate device for producing such objects.
Methods return ground or object types and take no ordinary parameters. The source grammar has neither arrow nor polymorphic method types. The new types and terms are 𝐴,𝐵::=𝖭𝖺𝗍∣𝖴𝗇𝗂𝗍∣[ℓ𝑖:𝐵𝑖]𝑖∈𝐼,𝑎,𝑏::=⋯∣[ℓ𝑖=𝜍(𝑠𝑖:𝐴)𝑏𝑖]𝑖∈𝐼∣𝑎.ℓ∣𝑎.ℓ⇐𝜍(𝑠:𝐴)𝑏. The labels in an object or object type are distinct, their order is irrelevant, and 𝐼 may be empty. In 𝜍(𝑠:𝐴)𝑏, the variable 𝑠 is bound in 𝑏. Terms are identified up to alpha-renaming, and substitution is capture avoiding.
Each method has its own self binder 𝑠𝑖; equal spellings in two fields do not make those binders the same variable. A field is therefore a method taking no argument but the receiver, whose body is 𝑏𝑖; it is not a stored record component. Override is the single syntactic form displayed above: the receiver and replacement method are both part of one term. Its infix ⇐ is that term former, not the checking judgment of chapter 10.
The binder 𝜍(𝑠:𝐴)𝑏 plays the semantic role of a method’s this: invocation substitutes the receiver object for 𝑠 in 𝑏. At the metalanguage level, the family 𝑃pt:=[𝑥:𝖭𝖺𝗍,get:𝖭𝖺𝗍],Point(𝑛):=[𝑥=𝜍(𝑠:𝑃pt)𝑛,get=𝜍(𝑠:𝑃pt)𝑠.𝑥]. is a small object-producing schema: each numeral 𝑛 determines an object term Point(𝑛), whereas 𝖯𝗈𝗂𝗇𝗍 denotes one recursive type. Reduction of Override constructs a new object literal with the selected method body replaced and leaves the receiver object unchanged. The term grammar contains no store update, prototype relation, or nominal class name.
The object rules are
𝐴=[ℓ𝑖:𝐵𝑖]𝑖∈𝐼Γ,𝑠𝑖:𝐴⊢𝑏𝑖:𝐵𝑖(𝑖∈𝐼)
Γ⊢[ℓ𝑖=𝜍(𝑠𝑖:𝐴)𝑏𝑖]𝑖∈𝐼:𝐴
T-Object
Γ⊢𝑎:[ℓ𝑖:𝐵𝑖]𝑖∈𝐼𝑗∈𝐼
Γ⊢𝑎.ℓ𝑗:𝐵𝑗
T-Invoke
Γ⊢𝑎:𝐴𝐴=[ℓ𝑖:𝐵𝑖]𝑖∈𝐼Γ,𝑠:𝐴⊢𝑏:𝐵𝑗𝑗∈𝐼
Γ⊢𝑎.ℓ𝑗⇐𝜍(𝑠:𝐴)𝑏:𝐴
T-Override
An override is defined only for an existing label and preserves its result type. The object annotation is part of the term. It shows the type that every occurrence of self is expected to have.
An eliminator whose receiver is a variable is neutral rather than a redex: 𝑠:𝑃⊢𝑠.𝑥:𝖭𝖺𝗍,𝑠.𝑥⧸⟼. Only substitution of an object value for 𝑠 can expose an invocation root.
For the remainder of this section, abbreviate the running point interface by 𝑃:=[𝑥:𝖭𝖺𝗍,get:𝖭𝖺𝗍] and define 𝑝0:=[𝑥=𝜍(𝑠:𝑃)0,get=𝜍(𝑠:𝑃)𝑠.𝑥]. The two premises of T-Object are 𝑠:𝑃⊢0:𝖭𝖺𝗍,𝑠:𝑃⊢𝑠:𝑃𝑠:𝑃⊢𝑠.𝑥:𝖭𝖺𝗍T−Invoke. Thus ⋅⊢𝑝0:𝑃. Notice that the body of get does not contain 𝑝0; it invokes 𝑥 through the receiver supplied when get is called.
Weak object reduction is call-by-value reduction that never enters a 𝜍-body. Object literals and ground constants are values. Besides the ground contexts, evaluation contexts contain 𝐸::=⋯∣𝐸.ℓ∣𝐸.ℓ⇐𝜍(𝑠:𝐴)𝑏. One-step reduction is compatible closure under these contexts of [ℓ𝑖=𝜍(𝑠𝑖:𝐴)𝑏𝑖]𝑖∈𝐼.ℓ𝑗⟼𝑏𝑗[[ℓ𝑖=𝜍(𝑠𝑖:𝐴)𝑏𝑖]𝑖∈𝐼/𝑠𝑗][ℓ𝑖=𝜍(𝑠𝑖:𝐴)𝑏𝑖]𝑖∈𝐼.ℓ𝑗⇐𝜍(𝑠:𝐴)𝑐⟼[ℓ𝑗=𝜍(𝑠:𝐴)𝑐,ℓ𝑖=𝜍(𝑠𝑖:𝐴)𝑏𝑖]𝑖∈𝐼∖{𝑗}(𝐼𝑛𝑣𝑜𝑘𝑒)(𝑂𝑣𝑒𝑟𝑟𝑖𝑑𝑒) Both contractions require 𝑗∈𝐼. The right side of (Override) is a new value. The original object is unchanged.
Put 𝑝1:=𝑝0.𝑥⇐𝜍(𝑠:𝑃)1. The override body has type 𝖭𝖺𝗍 under 𝑠:𝑃, so 𝑝1:𝑃. Reduction exposes late self invocation: 𝑝1.get𝑜𝑣𝑒𝑟𝑟𝑖𝑑𝑒𝑒𝑥𝑝𝑜𝑠𝑢𝑟𝑒⟼[𝑥=𝜍(𝑠:𝑃)1,get=𝜍(𝑠:𝑃)𝑠.𝑥].get𝑖𝑛𝑣𝑜𝑘𝑒get⟼[𝑥=𝜍(𝑠:𝑃)1,get=𝜍(𝑠:𝑃)𝑠.𝑥].𝑥𝑖𝑛𝑣𝑜𝑘𝑒𝑥⟼1. The second line is decisive. The old get body receives the new object and therefore finds the replacement for 𝑥.
★☆☆ Let 𝑝2:=(𝑝0.𝑥⇐𝜍(𝑠:𝑃)2).𝑥⇐𝜍(𝑠:𝑃)3. Derive ⋅⊢𝑝2:𝑃, reduce 𝑝2.get, and also reduce 𝑝0.get. Identify the calculation that shows that override is functional rather than imperative.
Two facts do all the work. Substitution reconnects an invoked body with its receiver, while canonical forms guarantee that a closed receiver has the method promised by its type.
Proof of Lemma 15.3 — Structural properties of Ob_1
Proof. Weakening is induction on the typing derivation. In the object case, first alpha-rename every 𝑠𝑖 away from the added declarations. The induction hypothesis changes Γ,𝑠𝑖:𝐶⊢𝑏𝑖:𝐵𝑖 into Γ′,𝑠𝑖:𝐶⊢𝑏𝑖:𝐵𝑖, and T-Object restores the conclusion. Invocation and override restore their last rules; the latter treats its receiver premise and its self-body premise separately.
For substitution, induct on the derivation of Γ,𝑥:𝐴⊢𝑏:𝐵. At the variable 𝑥, use the assumed derivation Γ⊢𝑎:𝐴; every other variable retains its declaration. For an object, alpha-rename each 𝑠𝑖 away from 𝑥 and from the free variables of 𝑎. The induction hypothesis gives Γ,𝑠𝑖:𝐶⊢𝑏𝑖[𝑎/𝑥]:𝐵𝑖 for every method, so T-Object applies. Invocation follows by applying the induction hypothesis to its receiver. In an override, apply it once to the receiver and once under the fresh declaration 𝑠:𝐶 to the new body, then restore T-Override. The ground constructors have no new binder and follow directly.
For exchange, induct on the typing derivation. A variable lookup is preserved by the adjacent permutation because source types contain no term variables. Every nonbinding rule applies the induction hypotheses to its premises. For object formation and override, alpha-rename the self binder away from 𝑥 and 𝑦, exchange the two outer declarations in each body premise, and restore the rule. Thus the permutation cannot capture a free occurrence. ◻
Proof. Numerals and 𝗎𝗇𝗂𝗍 have ground types. The only remaining value form is an object literal. Inverting T-Object equates its annotation with [ℓ𝑖:𝐵𝑖]𝑖∈𝐼, its label set with 𝐼, and each component result type with the corresponding 𝐵𝑖. There is no subsumption rule that could hide methods or change the outer type. ◻
Proof of Theorem 15.5 — Safety of the functional object calculus
Proof. For preservation, induct on the reduction derivation. Compatible steps use the induction hypothesis and restore the typing rule for the surrounding context. There are two new root cases.
For (Invoke), inversion of the receiver’s T-Object derivation gives 𝑠𝑗:𝐴⊢𝑏𝑗:𝐵𝑗,⋅⊢[ℓ𝑖=𝜍(𝑠𝑖:𝐴)𝑏𝑖]𝑖∈𝐼:𝐴. Substitution therefore types the reduct 𝑏𝑗[[ℓ𝑖=𝜍(𝑠𝑖:𝐴)𝑏𝑖]𝑖∈𝐼/𝑠𝑗] at 𝐵𝑗, the type assigned by T-Invoke.
For (Override), inversion gives 𝑠:𝐴⊢𝑐:𝐵𝑗 and, for the retained methods, 𝑠𝑖:𝐴⊢𝑏𝑖:𝐵𝑖. These are exactly the premises of T-Object for the replacement literal, so the reduct has type 𝐴. Ground contraction uses its ground typing premise.
For progress, induct on typing. Object literals are values. In an invocation, the receiver either steps, and the invocation context lifts that step, or is a value. In the latter case it is a literal containing the selected label by lemma 15.4, so (Invoke) applies. Override is identical up to its root rule. Ground terms use their usual progress cases. The final safety claim follows by applying preservation after every step and progress at the last term of any finite reduction. ◻
The safety theorem permits infinite reduction. For 𝐷=[loop:𝖭𝖺𝗍],𝑑=[loop=𝜍(𝑠:𝐷)𝑠.loop], the closed well-typed term 𝑑.loop reduces to itself. Self invocation therefore defines a general-recursive loop even though the calculus has no store.
★★☆ Write the complete preservation derivation for 𝑝0.𝑥⇐𝜍(𝑠:𝑃)𝑠.get⟼[𝑥=𝜍(𝑠:𝑃)𝑠.get,get=𝜍(𝑠:𝑃)𝑠.𝑥]. Your derivation must include both invocations used to type the method bodies. Also observe that the resulting object’s 𝑥 invocation alternates forever with its get invocation; this divergence is not a typing error.
An object with extra methods can answer every invocation understood by a smaller interface. This justifies width subtyping. It does not yet justify changing the result types of shared methods: every method body was checked under assumptions about every other method of self.
Add 𝖳𝗈𝗉 to the types and subsumption to typing. Width-invariant object subtyping is the least preorder that forgets methods while requiring equal result types for retained methods; it satisfies
𝐴<:𝖳𝗈𝗉
S-Top
𝐽⊆𝐼𝐵𝑗=𝐶𝑗(𝑗∈𝐽)
[ℓ𝑖:𝐵𝑖]𝑖∈𝐼<:[ℓ𝑗:𝐶𝑗]𝑗∈𝐽
S-Object
Ground types have only reflexive subtyping. The typing rule is
Γ⊢𝑎:𝐴𝐴<:𝐵
Γ⊢𝑎:𝐵
T-Sub
Shared object components are invariant: they must be the same type, not merely related by subtyping. The resulting calculus is 𝖮𝖻<:1. The type 𝖳𝗈𝗉 has no eliminator: a term can be forgotten to 𝖳𝗈𝗉, but no operation can recover its hidden object interface.
Proof of Lemma 25.8 — Top is maximal in object subtyping
Proof. Induct on the derivation in the least preorder. Reflexivity and S-Top have the stated target. Neither ground reflexivity nor S-Object can have source 𝖳𝗈𝗉. In a transitive derivation 𝖳𝗈𝗉<:𝐵<:𝐴, the induction hypothesis for the first part makes 𝐵=𝖳𝗈𝗉, and the hypothesis for the second then makes 𝐴=𝖳𝗈𝗉. ◻
Proof of Lemma 25.9 — Inversion of width-invariant object subtyping
Proof. Use rule induction for the least preorder, together with the shape claim that a type below an object type is itself an object type. Reflexivity is immediate, and S-Object gives both the label inclusion and the component equalities. Neither S-Top nor a ground-type reflexivity judgment can end at an object type.
For transitivity, write 𝐴′<:𝑀<:[ℓ𝑗:𝐶𝑗]𝑗∈𝐽. Inverting the second derivation gives 𝑀=[ℓ𝑘:𝐷𝑘]𝑘∈𝐾, with 𝐽⊆𝐾 and 𝐷𝑗=𝐶𝑗 on 𝐽. Inverting the first gives 𝐴′=[ℓ𝑖:𝐵𝑖]𝑖∈𝐼, with 𝐾⊆𝐼 and 𝐵𝑘=𝐷𝑘 on 𝐾. Composing the inclusions and equalities proves the conclusion. This argument also proves the shape claim. An intermediate 𝖳𝗈𝗉 is excluded by lemma 25.8. ◻
For example, [𝑥:𝖭𝖺𝗍,get:𝖭𝖺𝗍]<:[get:𝖭𝖺𝗍], so 𝑝0 may be passed to code that invokes only get. But [𝑚:𝑆]≮:[𝑚:𝑇] when 𝑆<:𝑇 and 𝑆≠𝑇.
Subsumption destroys unique typing: 𝑝0 has both displayed object types. The annotations in object formation and override nevertheless determine a minimum type for every typable term.
Write Γ⊢min𝑎:𝐴. Remove T-Sub, retain the variable, ground, and invocation rules with ⊢min, and replace object formation and override by
𝐴=[ℓ𝑖:𝐵𝑖]𝑖∈𝐼Γ,𝑠𝑖:𝐴⊢min𝑏𝑖:𝐵′𝑖𝐵′𝑖<:𝐵𝑖(𝑖∈𝐼)
Γ⊢min[ℓ𝑖=𝜍(𝑠𝑖:𝐴)𝑏𝑖]𝑖∈𝐼:𝐴
M-Object
𝐴=[ℓ𝑖:𝐵𝑖]𝑖∈𝐼Γ⊢min𝑎:𝐴′𝐴′<:𝐴Γ,𝑠:𝐴⊢min𝑏:𝐵′𝐵′<:𝐵𝑗𝑗∈𝐼
Γ⊢min𝑎.ℓ𝑗⇐𝜍(𝑠:𝐴)𝑏:𝐴
M-Override
Invocation is the explicit rule
Γ⊢min𝑎:[ℓ𝑖:𝐵𝑖]𝑖∈𝐼𝑗∈𝐼
Γ⊢min𝑎.ℓ𝑗:𝐵𝑗
M-Invoke
Thus, if the minimum receiver type is [ℓ𝑖:𝐵𝑖]𝑖∈𝐼, the result is the component 𝐵𝑗, and every conclusion is determined by the term, the context, and the conclusions of its immediate premises.
If an object with private methods is first viewed at a shorter interface 𝐴, an override annotated by 𝐴 has minimum type 𝐴, not the hidden longer type. The operation still creates a runtime object containing the private methods; the type checker does not recover an interface that the program chose to hide.
Proof. Induct on the typing derivation. In the distinguished variable case, 𝑠:𝐴′ follows by the variable rule and then T-Sub; other variables are unchanged. For object formation, alpha-rename each inner self binder away from 𝑠, apply the induction hypothesis to every body, and restore T-Object. Invocation and override apply the induction hypothesis to the receiver and to every premise in which the outer 𝑠 remains free; their own self binders are first made fresh. A final T-Sub is restored unchanged. Ground terms have no binding case. ◻
Proof of Lemma 15.9 — Source substitution with subtyping
Proof. Induct on the derivation of Γ,𝑥:𝐴⊢𝑏:𝐵. At 𝑥, use the assumed derivation Γ⊢𝑎:𝐴; every other variable retains its declaration. For T-Object, alpha-rename each self binder away from 𝑥 and 𝖥𝖵(𝑎), apply the induction hypothesis to every body, and restore the same annotated object type. Invocation applies the induction hypothesis to its receiver. Override applies it once to the receiver and once to the replacement body under a freshly renamed self binder, then restores T-Override. Numerals and 𝗎𝗇𝗂𝗍 contain no variables, and a successor uses the induction hypothesis on its argument. If the last rule is T-Sub, apply the induction hypothesis to its term premise and restore the unchanged subtype judgment. Thus subsumption introduces no new substitution case. ◻
Proof. For item 1, induct on the term. A variable’s type is fixed by Γ, and the ground forms have fixed types. An object or override carries its result annotation 𝐴 in its syntax. For invocation, the induction hypothesis fixes the minimum receiver type; distinctness of labels fixes its component type. Thus no term form admits two different conclusions.
For items 2 and 3, use simultaneous induction on the minimum and declarative derivations. In M-Object, each minimum body type 𝐵′𝑖 can be raised to 𝐵𝑖 by T-Sub; T-Object then gives the annotated type. In M-Override, raise the receiver from 𝐴′ to 𝐴 and the new body from 𝐵′ to 𝐵𝑗, then use T-Override. Invocation and ground cases are unchanged. This proves item 2.
Conversely, consider the last rule of a declarative derivation. If it is T-Sub, apply the induction hypothesis to its premise. If that premise has minimum type 𝐴, then 𝐴<:𝐵′ for the premise type 𝐵′, and transitivity with 𝐵′<:𝐵 gives the required comparison.
Suppose the last rule is T-Object. The induction hypothesis gives a minimum type 𝐵′𝑖<:𝐵𝑖 for each body under the annotated self context; M-Object gives the same annotated object type. Suppose it is T-Invoke. Its receiver has a minimum type 𝐴′ below the displayed object type 𝐴. Lemma 25.9 says that 𝐴′ contains every label of 𝐴 with exactly the same component type. The minimum invocation rule therefore returns the displayed 𝐵𝑗.
Finally suppose the last rule is T-Override, whose annotation is 𝐴=[ℓ𝑖:𝐵𝑖]𝑖∈𝐼. The receiver’s minimum type 𝐴′ satisfies 𝐴′<:𝐴, and the body’s minimum type 𝐵′ satisfies 𝐵′<:𝐵𝑗. Rule M-Override therefore gives minimum type 𝐴. These cases exhaust the syntax. By item 1, any two types constructed this way are equal, so the minimum type is independent of the declarative derivation. ◻
★★☆ Let 𝑄=[𝑥:𝖭𝖺𝗍,𝑦:𝖭𝖺𝗍],𝑞=[𝑥=𝜍(𝑠:𝑄)0,𝑦=𝜍(𝑠:𝑄)1]. Find the minimum type of 𝑞, of 𝑞.𝑥, and of 𝑞.𝑥⇐𝜍(𝑠:[𝑥:𝖭𝖺𝗍])2. For the last term, show both the minimum derivation and a declarative typing that first subsumes 𝑞.
Subtyping changes the override contraction in one small but essential way. If the runtime literal was constructed at 𝐴′, the replacement body must be reannotated by 𝐴′: [ℓ𝑖=𝜍(𝑠𝑖:𝐴′)𝑏𝑖]𝑖∈𝐼.ℓ𝑗⇐𝜍(𝑠:𝐴)𝑐⟼[ℓ𝑗=𝜍(𝑠:𝐴′)𝑐,ℓ𝑖=𝜍(𝑠𝑖:𝐴′)𝑏𝑖]𝑖∈𝐼∖{𝑗}.(𝑂𝑣𝑒𝑟𝑟𝑖𝑑𝑒−𝑆𝑢𝑏) From this point on, (Override-Sub) supersedes the earlier (Override) root whenever subsumption is present. The premise that typed the receiver implies 𝐴′<:𝐴. By lemma 15.8, a body checked with 𝑠:𝐴 remains well typed with 𝑠:𝐴′. Leaving the annotation 𝐴 on only the new method would not form a legal object literal.
Proof of Theorem 15.11 — Safety with width-invariant subtyping
Proof. Use minimum typing to remove arbitrary final subsumption before analyzing an eliminator. For invocation, a value whose minimum type is below [ℓ𝑖:𝐵𝑖]𝑖∈𝐼 is an object literal with at least those labels and the same result types. Applying lemma 15.9 to the invoked body and the actual receiver types the source reduct at 𝐵𝑗; a final subsumption restores any requested supertype.
For override, let the runtime receiver have construction type 𝐴′, while the source override is annotated by 𝐴. Minimum typing gives 𝐴′<:𝐴. If its new body was checked at 𝐵𝑗 under 𝑠:𝐴, receiver narrowing gives the same judgment under 𝑠:𝐴′. Lemma 25.9 says that the ℓ𝑗 component of 𝐴′ is exactly 𝐵𝑗. Together with the retained method premises, T-Object types the reduct of (Override-Sub) at 𝐴′, and T-Sub raises it to 𝐴. Compatible reduction preserves typing by the induction hypothesis. These are the only additions to the preservation proof of theorem 15.5.
For progress, use minimum typing and width inversion in place of the earlier canonical-forms lemma. A closed value usable at a nonempty object type must be a literal containing every visible label. Invocation and override therefore have a root contraction after their receivers become values. The ground cases are unchanged. ◻
Two tempting record rules fail
Width subtyping is safe because a hidden method remains present and every visible method keeps the result type assumed by all bodies. Covariance would break the second fact even though override is functional.
Let 𝑆:=[tag:𝖴𝗇𝗂𝗍],𝑇:=[],𝑆<:𝑇. For the covariance counterexample, define fresh local interfaces 𝑄𝑐:=[𝑚:𝑆,𝑛:𝑆],𝑃𝑐:=[𝑚:𝑇,𝑛:𝑆]. Suppose, contrary to definition 15.6, that object components were covariant. Then 𝑄𝑐<:𝑃𝑐. Define 𝑠0=[tag=𝜍(𝑠:𝑆)𝗎𝗇𝗂𝗍]:𝑆,𝑡0=[]:𝑇, and 𝑞=[𝑚=𝜍(𝑠:𝑄𝑐)𝑠0,𝑛=𝜍(𝑠:𝑄𝑐)𝑠.𝑚]:𝑄𝑐. By the proposed covariance, 𝑞:𝑃𝑐, so the term 𝑟:=𝑞.𝑚⇐𝜍(𝑠:𝑃𝑐)𝑡0 would have type 𝑃𝑐, and 𝑟.𝑛 would have type 𝑆. Operationally, however, 𝑟.𝑛⟼[𝑚=𝜍(𝑠:𝑄𝑐)𝑡0,𝑛=𝜍(𝑠:𝑄𝑐)𝑠.𝑚].𝑛⟼[𝑚=𝜍(𝑠:𝑄𝑐)𝑡0,𝑛=𝜍(𝑠:𝑄𝑐)𝑠.𝑚].𝑚⟼𝑡0. The alleged 𝑆-term has reduced to the empty object, and (𝑟.𝑛).tag is stuck. The old body of 𝑛 relied on 𝑚:𝑆; the new object retained that body while the replacement’s self parameter had type 𝑇, not the 𝑆 assumed by the retained body. Functional update prevented mutation of 𝑞, but it did not protect the other methods of 𝑟.
Proof of Proposition 15.12 — Covariant method components destroy preservation
Proof. The constructed term 𝑟.𝑛 is typed at 𝑆. Its three-step reduct is 𝑡0=[], which is typed directly at 𝑇=[] by T-Object. It cannot be typed at 𝑆=[tag:𝖴𝗇𝗂𝗍]: even the proposed covariant rule only forgets labels, so an induction on a subtype derivation shows that an object type below 𝑆 must contain tag, whereas 𝑇 contains none. Hence the reduct has no type 𝑆. Thus preservation fails on this closed term. ◻
A record field can be pulled out and used on its own. A self-bound method cannot, at least not at a shortened interface: its body may invoke a method that subsumption has hidden. For this failed-rule analysis only, make the conservative addition of arrows, lambda abstraction, and application with
Γ,𝑥:𝐴⊢𝑏:𝐵
Γ⊢𝜆𝑥:𝐴.𝑏:𝐴→𝐵
T-Lam
Γ⊢𝑓:𝐴→𝐵Γ⊢𝑎:𝐴
Γ⊢𝑓𝑎:𝐵
T-App
and the contraction (𝜆𝑥:𝐴.𝑏)𝑣⟼𝑏[𝑣/𝑥]. Also add the total ground operation 𝗉𝗅𝗎𝗌(𝑚,𝑛)⟼𝑚+𝑛 on numerals, with left-to-right contexts 𝗉𝗅𝗎𝗌(𝐸,𝑎)and𝗉𝗅𝗎𝗌(𝑛,𝐸). None of these additions mentions objects, and none belongs to the source fragment translated in section 15.5.
Suppose there were an interface-annotated term 𝑎†𝐴ℓ with rules Γ⊢𝑎:𝐴𝐴=[ℓ𝑖:𝐵𝑖]𝑖∈𝐼𝑗∈𝐼Γ⊢𝑎†𝐴ℓ𝑗:𝐴→𝐵𝑗T−Extract and, for an actual object built at 𝐴′, the unambiguous contraction [ℓ𝑖=𝜍(𝑠𝑖:𝐴′)𝑏𝑖]𝑖∈𝐼†𝐴ℓ𝑗⟼𝜆𝑠:𝐴.𝑏𝑗. The annotation records the interface used by the typing derivation; without it, the 𝐴 in the reduct would not be determined by the redex. For the extraction counterexample, define fresh local interfaces 𝑃𝑥=[𝑥:𝖭𝖺𝗍,𝑓:𝖭𝖺𝗍],𝑄𝑥=[𝑥:𝖭𝖺𝗍,𝑦:𝖭𝖺𝗍,𝑓:𝖭𝖺𝗍]<:𝑃𝑥.𝑝=[𝑥=𝜍(𝑠:𝑃𝑥)1,𝑓=𝜍(𝑠:𝑃𝑥)1]:𝑃𝑥 and 𝑎=[𝑥=𝜍(𝑠:𝑄𝑥)1,𝑦=𝜍(𝑠:𝑄𝑥)1,𝑓=𝜍(𝑠:𝑄𝑥)𝗉𝗅𝗎𝗌(𝑠.𝑥,𝑠.𝑦)]:𝑄𝑥. Subsumption gives 𝑎:𝑃𝑥, so extraction claims 𝑎†𝑃𝑥𝑓:𝑃𝑥→𝖭𝖺𝗍. But (𝑎†𝑃𝑥𝑓)𝑝⟼(𝜆𝑠:𝑃𝑥.𝗉𝗅𝗎𝗌(𝑠.𝑥,𝑠.𝑦))𝑝⟼𝗉𝗅𝗎𝗌(𝑝.𝑥,𝑝.𝑦), which is stuck at 𝑝.𝑦. Invocation avoids this failure: it substitutes the actual object 𝑎:𝑄𝑥, not an arbitrary argument of the shortened type 𝑃𝑥.
★★☆ For the covariance counterexample, draw the proposed derivation of ⋅⊢(𝑟.𝑛).tag:𝖴𝗇𝗂𝗍 and mark the first reduction at which its preservation proof lacks a premise. For the extraction counterexample, identify the precise use of width subsumption that changes the type of the extracted self parameter.
The point 𝑝1 can be updated from outside, but it cannot yet offer a move method that returns the updated point. If the point type is 𝑅, that method requires the equation 𝑅=[𝑥:𝖭𝖺𝗍,get:𝖭𝖺𝗍,move:𝑅]. No finite expansion closes this equation. After replacing the rightmost 𝑅 once, the result contains another occurrence requiring another replacement.
We use the iso-recursive rules of definition 24.1. In particular, 𝜇𝑋.𝐴 is not judgmentally equal to its unfolding, and the only ways to cross between them are 𝖿𝗈𝗅𝖽 and 𝗎𝗇𝖿𝗈𝗅𝖽. The formation rule for 𝜇𝑋.𝐴 does not require positivity. The point equation below is in addition contractive under the definition of chapter 12, extended here by treating an object-type constructor as a guarding constructor: its occurrence of 𝑋 lies beneath that constructor. No algorithmic equality between recursive types is being added.
Define 𝖯𝗈𝗂𝗇𝗍:=𝜇𝑋.[𝑥:𝖭𝖺𝗍,get:𝖭𝖺𝗍,move:𝑋] and its unfolding 𝖴𝖯𝗈𝗂𝗇𝗍:=[𝑥:𝖭𝖺𝗍,get:𝖭𝖺𝗍,move:𝖯𝗈𝗂𝗇𝗍]. The type variable 𝑋 is bound in a type. It is unrelated to the term variables bound by 𝜍.
Let 𝑜0:𝖴𝖯𝗈𝗂𝗇𝗍 be [𝑥=𝜍(𝑠:𝖴𝖯𝗈𝗂𝗇𝗍)0,get=𝜍(𝑠:𝖴𝖯𝗈𝗂𝗇𝗍)𝑠.𝑥,move=𝜍(𝑠:𝖴𝖯𝗈𝗂𝗇𝗍)𝖿𝗈𝗅𝖽𝖯𝗈𝗂𝗇𝗍(𝑠.𝑥⇐𝜍(𝑡:𝖴𝖯𝗈𝗂𝗇𝗍)𝗌𝗎𝖼𝖼(𝑠.𝑥))]. Then origin:=𝖿𝗈𝗅𝖽𝖯𝗈𝗂𝗇𝗍𝑜0:𝖯𝗈𝗂𝗇𝗍.
The move body is the only non-immediate typing premise. Under 𝑠:𝖴𝖯𝗈𝗂𝗇𝗍, invocation gives 𝑠.𝑥:𝖭𝖺𝗍, so under the additional fresh binder 𝑡:𝖴𝖯𝗈𝗂𝗇𝗍, 𝗌𝗎𝖼𝖼(𝑠.𝑥):𝖭𝖺𝗍. Override gives 𝑠:𝖴𝖯𝗈𝗂𝗇𝗍⊢𝑠.𝑥⇐𝜍(𝑡:𝖴𝖯𝗈𝗂𝗇𝗍)𝗌𝗎𝖼𝖼(𝑠.𝑥):𝖴𝖯𝗈𝗂𝗇𝗍. Folding gives the required method result 𝖯𝗈𝗂𝗇𝗍. The term receiver 𝑠 is substituted when move is invoked; the type variable 𝑋 was already replaced by 𝖯𝗈𝗂𝗇𝗍 when the recursive type was unfolded.
Define next:=(𝗎𝗇𝖿𝗈𝗅𝖽origin).move. Write the unfolded object created by this invocation as 𝑜1:=[𝑥=𝜍(𝑡:𝖴𝖯𝗈𝗂𝗇𝗍)𝗌𝗎𝖼𝖼(𝑜0.𝑥),get=𝜍(𝑠:𝖴𝖯𝗈𝗂𝗇𝗍)𝑠.𝑥,move=𝜍(𝑠:𝖴𝖯𝗈𝗂𝗇𝗍)⋯]. Then next:𝖯𝗈𝗂𝗇𝗍, and the complete observable calculation is (𝗎𝗇𝖿𝗈𝗅𝖽next).get𝑖𝑛𝑣𝑜𝑘𝑒𝑎𝑛𝑑𝑢𝑛𝑓𝑜𝑙𝑑⟼∗𝑜1.get𝑖𝑛𝑣𝑜𝑘𝑒⟼𝑜1.𝑥𝑜𝑣𝑒𝑟𝑟𝑖𝑑𝑒⟼𝗌𝗎𝖼𝖼(𝑜0.𝑥)𝑝𝑟𝑜𝑗𝑒𝑐𝑡𝑖𝑜𝑛⟼𝗌𝗎𝖼𝖼(0)𝑎𝑟𝑖𝑡ℎ𝑚𝑒𝑡𝑖𝑐⟼1. The retained get body sees the replacement for 𝑥, exactly as in the finite point. Fold and unfold solve only the type equation. Invocation still substitutes the updated receiver 𝑜1 for self in every selected method body: invoking get contracts its body 𝑠.𝑥 to 𝑜1.𝑥.
For this extension, Δ is the type-variable context and Γ is the term-variable context; a judgment has the form Δ;Γ⊢𝑎:𝐴. A declaration 𝑋 extends only Δ, whereas a self declaration 𝑠:𝐴 extends only Γ. In contrast, chapter 8 places type bounds and term declarations in one context. Here type substitution changes Δ and the type annotations in Γ, whereas term substitution changes only terms under the fixed pair Δ;Γ.
if Δ,𝑋;Γ⊢𝑎:𝐴 and Δ⊢𝐶𝗍𝗒𝗉𝖾, then Δ;Γ[𝐶/𝑋]⊢𝑎[𝐶/𝑋]:𝐴[𝐶/𝑋];
if Δ;Γ,𝑠:𝐴⊢𝑏:𝐵 and Δ;Γ⊢𝑎:𝐴, then Δ;Γ⊢𝑏[𝑎/𝑠]:𝐵.
Type substitution passes beneath a self binder after substituting in its annotation. Term substitution passes beneath 𝜇𝑋 because 𝑋 binds types, not terms.
Proof. For item 1, induct on typing and type formation. In an object premise Δ,𝑋;Γ,𝑠:𝐷⊢𝑏:𝐵, the induction hypothesis gives Δ;Γ[𝐶/𝑋],𝑠:𝐷[𝐶/𝑋]⊢𝑏[𝐶/𝑋]:𝐵[𝐶/𝑋], which is precisely the premise for the substituted object. Fold and unfold use the type-substitution composition calculation from lemma 24.3. Invocation and override restore their last rules.
For item 2, induct on typing. Alpha-rename a method’s bound self variable away from the substituted 𝑠, apply the induction hypothesis to its body, and restore the object or override rule. Fold and unfold bind no term variable, so their cases apply the induction hypothesis to the payload and restore the same rule. All other cases are those of lemma 15.3. ◻
Proof of Proposition 15.15 — Safety of recursive objects
Proof. The object roots still use item 2 of lemma 15.14. The new preservation root is 𝗎𝗇𝖿𝗈𝗅𝖽(𝖿𝗈𝗅𝖽𝜇𝑋.𝐴𝑣)⟼𝑣, whose typing derivation has 𝑣:𝐴[𝜇𝑋.𝐴/𝑋] as the premise of fold and the result type of unfold. For progress, the only value form at a recursive type is 𝖿𝗈𝗅𝖽𝜇𝑋.𝐴𝑣. Therefore, if the argument of 𝗎𝗇𝖿𝗈𝗅𝖽 is already a value of recursive type, it exposes exactly the root contraction displayed above; otherwise the evaluation-context rule steps its argument. The fold evaluation-context case and all object cases are unchanged. ◻
★★☆ Let 𝑝2:=(𝗎𝗇𝖿𝗈𝗅𝖽((𝗎𝗇𝖿𝗈𝗅𝖽origin).move)).move. Derive 𝑝2:𝖯𝗈𝗂𝗇𝗍 and reduce (𝗎𝗇𝖿𝗈𝗅𝖽𝑝2).get to 2. Keep the two successive runtime receivers distinct in the calculation.
The direct safety proof leaves a structural question. Can invocation, functional override, width subtyping, and late self all be reconstructed from functions, records, existentials, and recursive types? The first attempt is 𝐴∘=𝜇𝑋.{ℓ𝑖:𝑋→𝐵∗𝑖}𝑖∈𝐼. For width 𝐴<:𝐵, an Amber-style comparison of the bodies assumes 𝑋𝐴<:𝑋𝐵, but arrow contravariance asks for the reverse premise 𝑋𝐵<:𝑋𝐴. Thus the naive record of methods cannot validate width. A second attempt stores a fixed self field 𝗌𝖾𝗅𝖿=𝑟 and implements override by changing only one method field. After overriding a coordinate method from 0 to 1, selection through the retained method still applies to the old 𝑟 and returns 0, whereas source late self returns 1. In the one-coordinate fragment, the failure is already visible in the candidate target records 𝑟0=𝗅𝖾𝗍𝗋𝖾𝖼𝑟={𝗌𝖾𝗅𝖿=𝑟,𝑥=0,get=𝜆𝑢:𝖴𝗇𝗂𝗍.𝑟.𝗌𝖾𝗅𝖿.𝑥}𝗂𝗇𝑟,𝑟1=𝑟0{𝑥:=1}. Functional update retains the closed body of get, so 𝑟1.get𝗎𝗇𝗂𝗍𝑝𝑟𝑜𝑗𝑒𝑐𝑡𝑖𝑜𝑛𝑎𝑛𝑑𝑏𝑒𝑡𝑎⟶𝑟0.𝗌𝖾𝗅𝖿.𝑥𝑝𝑟𝑜𝑗𝑒𝑐𝑡𝑖𝑜𝑛⟶𝑟0.𝑥𝑝𝑟𝑜𝑗𝑒𝑐𝑡𝑖𝑜𝑛⟶0, whereas source late self gives 𝑝1.get⟼∗1. The record-update notation here belongs only to the failed candidate, not to 𝐹<:𝜇. The translation must therefore rebuild the suite on update and expose separate selection and update capabilities.
Rebuilding must hide the current receiver representation while retaining its public upper bound. The target calculus therefore combines a bounded existential package with an iso-recursive public interface.
A type context Δ contains distinct bounds 𝑋<:𝑆; a term context Γ contains distinct declarations 𝑥:𝑆. Target types and the terms needed by the translation are 𝑆,𝑇::=𝖳𝗈𝗉∣𝖭𝖺𝗍∣𝖴𝗇𝗂𝗍∣𝑋∣𝑆→𝑇∣{𝑘𝑖:𝑆𝑖}𝑖∈𝐼∣∃𝑋<:𝑆.𝑇∣𝜇𝑋.𝑇,𝑡,𝑢::=𝑥∣𝑛∣𝗎𝗇𝗂𝗍∣𝗌𝗎𝖼𝖼(𝑡)∣𝜆𝑥:𝑆.𝑡∣𝑡𝑢∣{𝑘𝑖=𝑡𝑖}𝑖∈𝐼∣𝑡.𝑘∣𝗉𝖺𝖼𝗄𝑋<:𝑆=𝑅𝗐𝗂𝗍𝗁𝑡𝖺𝗌∃𝑋<:𝑆.𝑇∣𝗈𝗉𝖾𝗇𝑡𝖺𝗌𝑋<:𝑆,𝑥:𝑇𝗂𝗇𝑢∣𝖿𝗈𝗅𝖽𝜇𝑋.𝑇𝑡∣𝗎𝗇𝖿𝗈𝗅𝖽𝑡∣𝗅𝖾𝗍𝗋𝖾𝖼𝑓(𝑥𝑖:𝑆𝑖)𝑛𝑖=1:𝑇=𝑡𝗂𝗇𝑢. All binders are modulo alpha-equivalence. Records have distinct labels and are covariant. The empty type context is well formed; if Δ is well formed, Δ⊢𝑆𝗍𝗒𝗉𝖾, and 𝑋 is fresh, then Δ,𝑋<:𝑆 is well formed. Type formation is generated by
Δ⊢𝑆𝗍𝗒𝗉𝖾Δ⊢𝑇𝗍𝗒𝗉𝖾
Δ⊢𝑆→𝑇𝗍𝗒𝗉𝖾
FT-Arr
Δ⊢𝑆𝑖𝗍𝗒𝗉𝖾(𝑖∈𝐼)
Δ⊢{𝑘𝑖:𝑆𝑖}𝑖∈𝐼𝗍𝗒𝗉𝖾
FT-Record
Δ⊢𝑆𝗍𝗒𝗉𝖾Δ,𝑋<:𝑆⊢𝑇𝗍𝗒𝗉𝖾
Δ⊢∃𝑋<:𝑆.𝑇𝗍𝗒𝗉𝖾
FT-Exists
Δ,𝑋<:𝖳𝗈𝗉⊢𝑇𝗍𝗒𝗉𝖾
Δ⊢𝜇𝑋.𝑇𝗍𝗒𝗉𝖾
FT-Mu
Variables declared in Δ, 𝖭𝖺𝗍, 𝖴𝗇𝗂𝗍, and 𝖳𝗈𝗉 are well formed. The basic subtype rules are
Δ⊢𝑆𝗍𝗒𝗉𝖾
Δ⊢𝑆<:𝑆
S-Refl
Δ⊢𝑅<:𝑆Δ⊢𝑆<:𝑇
Δ⊢𝑅<:𝑇
S-Trans
𝑋<:𝑆∈Δ
Δ⊢𝑋<:𝑆
S-Bound
Δ⊢𝑆𝗍𝗒𝗉𝖾
Δ⊢𝑆<:𝖳𝗈𝗉
S-Top
The remaining subtyping rules are
Δ⊢𝑆′<:𝑆Δ⊢𝑇<:𝑇′
Δ⊢𝑆→𝑇<:𝑆′→𝑇′
S-Arr
𝐽⊆𝐼Δ⊢𝑆𝑗<:𝑇𝑗(𝑗∈𝐽)
Δ⊢{𝑘𝑖:𝑆𝑖}𝑖∈𝐼<:{𝑘𝑗:𝑇𝑗}𝑗∈𝐽
S-Rec
Δ⊢𝑆<:𝑆′Δ,𝑋<:𝑆⊢𝑇<:𝑇′
Δ⊢∃𝑋<:𝑆.𝑇<:∃𝑋<:𝑆′.𝑇′
S-Exists
The recursive rule is traditionally called the Amber rule, after the recursive-type calculus in which this comparison principle was isolated:
Δ,𝑌<:𝖳𝗈𝗉,𝑋<:𝑌⊢𝑆<:𝑇
Δ⊢𝜇𝑋.𝑆<:𝜇𝑌.𝑇
S-Amber
In its premise, alpha-renaming makes 𝑋 and 𝑌 fresh, and the body 𝑆 is formed under 𝑋 while 𝑇 is formed under 𝑌. Thus the assumption 𝑋<:𝑌 is precisely the recursive simulation hypothesis. It says that one unfolding of the left body simulates one unfolding of the right while their recursive occurrences are related; the conclusion closes that hypothesis at the two recursive types. The chapter proves the structural lemmas and preservation cases needed by the translation, including generalized bounded-existential opening and 𝗅𝖾𝗍𝗋𝖾𝖼, but does not claim a standalone progress or normalization theorem for every target term. Formation deliberately admits noncontractive target types such as 𝜇𝑋.𝑋. The structural and translation proofs never expose a recursive head by unbounded equality, and S-Amber is used only as the displayed syntactic simulation rule. Every recursive type generated by the source translation is contractive: its recursive occurrence lies beneath the record constructor in 𝐶𝐴(𝑋). Thus no target progress claim for 𝜇𝑋.𝑋 is needed or implied.
The empty term context is well formed over Δ, and it may be extended by a fresh 𝑥:𝑆 when 𝑆 is well formed. Term typing includes
𝑥:𝑆∈Γ
Δ;Γ⊢𝑥:𝑆
F-Var
Δ;Γ⊢𝑡:𝑆Δ⊢𝑆<:𝑇
Δ;Γ⊢𝑡:𝑇
F-Sub
and
Δ;Γ,𝑥:𝑆⊢𝑡:𝑇
Δ;Γ⊢𝜆𝑥:𝑆.𝑡:𝑆→𝑇
F-Lam
Δ;Γ⊢𝑡:𝑆→𝑇Δ;Γ⊢𝑢:𝑆
Δ;Γ⊢𝑡𝑢:𝑇
F-App
Δ;Γ⊢𝑡𝑖:𝑆𝑖(𝑖∈𝐼)
Δ;Γ⊢{𝑘𝑖=𝑡𝑖}𝑖∈𝐼:{𝑘𝑖:𝑆𝑖}𝑖∈𝐼
F-Record
Δ;Γ⊢𝑡:{𝑘𝑖:𝑆𝑖}𝑖∈𝐼𝑗∈𝐼
Δ;Γ⊢𝑡.𝑘𝑗:𝑆𝑗
F-Proj
Numerals have type 𝖭𝖺𝗍, and 𝗎𝗇𝗂𝗍 has type 𝖴𝗇𝗂𝗍; if 𝑡:𝖭𝖺𝗍, then 𝗌𝗎𝖼𝖼(𝑡):𝖭𝖺𝗍. Bounded packages and opening are typed by
Δ⊢𝑅<:𝑆Δ;Γ⊢𝑡:𝑇[𝑅/𝑋]
Δ;Γ⊢𝗉𝖺𝖼𝗄𝑋<:𝑆=𝑅𝗐𝗂𝗍𝗁𝑡𝖺𝗌∃𝑋<:𝑆.𝑇:∃𝑋<:𝑆.𝑇
F-Pack
Δ;Γ⊢𝑝:∃𝑋<:𝑆.𝑇Δ,𝑋<:𝑆;Γ,𝑥:𝑇⊢𝑢:𝑈𝑋∉𝖥𝖵(𝑈)
Δ;Γ⊢𝗈𝗉𝖾𝗇𝑝𝖺𝗌𝑋<:𝑆,𝑥:𝑇𝗂𝗇𝑢:𝑈
F-Open
The opened type variable is fresh for Δ,Γ. The displayed escape condition prevents the hidden representation from occurring in the result. Iso-recursive typing is
Δ;Γ⊢𝑡:𝑇[𝜇𝑋.𝑇/𝑋]
Δ;Γ⊢𝖿𝗈𝗅𝖽𝜇𝑋.𝑇𝑡:𝜇𝑋.𝑇
F-Fold
Δ;Γ⊢𝑡:𝜇𝑋.𝑇
Δ;Γ⊢𝗎𝗇𝖿𝗈𝗅𝖽𝑡:𝑇[𝜇𝑋.𝑇/𝑋]
F-Unfold
Finally, if 𝐹=𝑆1→⋯→𝑆𝑛→𝑇, recursive creation has rule
Δ;Γ,𝑓:𝐹,𝑥1:𝑆1,…,𝑥𝑛:𝑆𝑛⊢𝑡:𝑇Δ;Γ,𝑓:𝐹⊢𝑢:𝑈
Δ;Γ⊢𝗅𝖾𝗍𝗋𝖾𝖼𝑓(𝑥𝑖:𝑆𝑖)𝑛𝑖=1:𝑇=𝑡𝗂𝗇𝑢:𝑈
F-Letrec
This is a functional recursive binding. It allocates neither cells nor mutable record fields.
Proof of Lemma 25.20 — Top is maximal in target subtyping
Proof. Induct on the subtype derivation. Rules S-Refl and S-Top have target 𝖳𝗈𝗉. The bound, arrow, record, existential, and Amber rules cannot have source 𝖳𝗈𝗉. In the transitive case, apply the induction hypothesis first to 𝖳𝗈𝗉<:𝑆, obtaining 𝑆=𝖳𝗈𝗉, and then to 𝑆<:𝑇, obtaining 𝑇=𝖳𝗈𝗉. ◻
The ordinary target rules already compute a complete small derivation. Put 𝑅={𝑛:𝖭𝖺𝗍}. Under 𝑥:𝖭𝖺𝗍, its lines are ⋅;𝑥:𝖭𝖺𝗍⊢𝑥:𝖭𝖺𝗍byF-Var,⋅;𝑥:𝖭𝖺𝗍⊢{𝑛=𝑥}:𝑅byF-Record,⋅;𝑥:𝖭𝖺𝗍,𝑟:𝑅⊢𝑟:𝑅byF-Var,⋅;𝑥:𝖭𝖺𝗍,𝑟:𝑅⊢𝑟.𝑛:𝖭𝖺𝗍byF-Proj,⋅;𝑥:𝖭𝖺𝗍⊢𝜆𝑟:𝑅.𝑟.𝑛:𝑅→𝖭𝖺𝗍byF-Lam,⋅;𝑥:𝖭𝖺𝗍⊢(𝜆𝑟:𝑅.𝑟.𝑛){𝑛=𝑥}:𝖭𝖺𝗍byF-App. Target translation uses F-Pack to hide each self witness, F-Fold to form its recursive public interface, and F-Letrec to define the recursive record builder; source terms have no corresponding constructors.
The bounded package/open mechanism has a complete smaller instance. With 𝑅={𝑛:𝖭𝖺𝗍}, put 𝑝=𝗉𝖺𝖼𝗄𝑋<:𝑅=𝑅𝗐𝗂𝗍𝗁{𝑛=𝑥}𝖺𝗌∃𝑋<:𝑅.𝑋. The record derivation above and reflexivity of 𝑅<:𝑅 give ⋅;𝑥:𝖭𝖺𝗍⊢𝑝:∃𝑋<:𝑅.𝑋. Inside 𝗈𝗉𝖾𝗇𝑝𝖺𝗌𝑋<:𝑅,𝑟:𝑋𝗂𝗇𝑟.𝑛, rule S-Bound and F-Sub type 𝑟:𝑅, so F-Proj types 𝑟.𝑛:𝖭𝖺𝗍. The result type contains no 𝑋; F-Open therefore gives the whole term type 𝖭𝖺𝗍. This is the same hidden-witness/public-bound invariant used by the object translation.
The translation is scoped to the nonrecursive source calculus 𝖮𝖻<:1. Its target 𝐹<:𝜇 has arrows, covariant records, bounded existentials, iso-recursive types, and an unrestricted functional 𝗅𝖾𝗍𝗋𝖾𝖼. Unlike the full 𝐹<: of chapter 8, it omits bounded universals and 𝖡𝗈𝗍. Unlike the kinded existentials of chapter 10, a hidden witness is known to lie below a public interface. The operation is called 𝗈𝗉𝖾𝗇, and its result may not mention the fresh representation variable. Fold and unfold have the iso-recursive meaning fixed in chapter 12.
Root contraction is generated by 𝗌𝗎𝖼𝖼(𝑛)⟼𝑛+1,(𝜆𝑥:𝑆.𝑡)𝑢⟼𝑡[𝑢/𝑥],{𝑘𝑖=𝑡𝑖}𝑖∈𝐼.𝑘𝑗⟼𝑡𝑗,𝑗∈𝐼,𝗎𝗇𝖿𝗈𝗅𝖽(𝖿𝗈𝗅𝖽𝜇𝑋.𝑇𝑡)⟼𝑡. The existential root is 𝗈𝗉𝖾𝗇(𝗉𝖺𝖼𝗄𝑌<:𝑆0=𝑅𝗐𝗂𝗍𝗁𝑡𝖺𝗌∃𝑌<:𝑆0.𝑇0)𝖺𝗌𝑋<:𝑆,𝑥:𝑇𝗂𝗇𝑢⟼𝑢[𝑅/𝑋][𝑡/𝑥]. Here 𝑌 and 𝑋 are chosen fresh and independently. In particular, the actual package annotation ∃𝑌<:𝑆0.𝑇0 need not be syntactically equal to the existential type expected by the open; a well-typed redex may have reached the latter type by package subsumption. The contraction uses the actual witness 𝑅 and actual payload 𝑡, not the opening annotations. For a recursive declaration 𝐷≡𝑓(𝑥𝑖:𝑆𝑖)𝑛𝑖=1:𝑇=𝑡, where 𝐹=𝑆1→⋯→𝑆𝑛→𝑇, put 𝑟𝐷:=𝗅𝖾𝗍𝗋𝖾𝖼𝐷𝗂𝗇𝑓,ℎ𝐷:=𝜆𝑥1:𝑆1.⋯𝜆𝑥𝑛:𝑆𝑛.𝑡[𝑟𝐷/𝑓]. The recursive root is 𝗅𝖾𝗍𝗋𝖾𝖼𝐷𝗂𝗇𝑢⟼𝑢[ℎ𝐷/𝑓]. Taking 𝑢=𝑓 shows 𝑟𝐷⟼ℎ𝐷, so a recursive call can unfold again. The relation ⟼ is the full compatible closure of these roots: a step is permitted in every subterm—the argument of a successor; the function or argument of an application; any record field; the receiver of a projection; the payload of a package; the package or body of an open; the payload of a fold or unfold; and the defining term or body of a letrec. It is also closed beneath lambda, open, and letrec binders after alpha-renaming away from captured variables. This congruence relation is not a deterministic evaluation strategy. Although ⟼ and the source’s ⟼ currently print the same base arrow, their roles are never interchangeable: the former is the nondeterministic full compatible target closure, while the latter is weak deterministic source evaluation. Translation statements name both relations explicitly.
The target rules are used immediately. For example, 𝑥:𝖭𝖺𝗍∈ΓΔ;Γ⊢𝑥:𝖭𝖺𝗍F−VarΔ;Γ⊢𝗎𝗇𝗂𝗍:𝖴𝗇𝗂𝗍Δ;Γ⊢{𝑎=𝑥,𝑏=𝗎𝗇𝗂𝗍}:{𝑎:𝖭𝖺𝗍,𝑏:𝖴𝗇𝗂𝗍}F−RecordΔ;Γ⊢{𝑎=𝑥,𝑏=𝗎𝗇𝗂𝗍}.𝑎:𝖭𝖺𝗍F−Proj. At the closed instance 𝑥=0, F-Proj contracts to 0.
If a formation, subtyping, or typing judgment is derivable under Δ0,𝑋<:𝑆,Δ1, and Δ0⊢𝑅<:𝑆, it remains derivable after replacing the bound of 𝑋 by 𝑅.
If Δ;Γ0,𝑥:𝑆,Γ1⊢𝑡:𝑇 and Δ⊢𝑅<:𝑆, then Δ;Γ0,𝑥:𝑅,Γ1⊢𝑡:𝑇.
If a target judgment is derivable under Δ0,𝑋<:𝑆,Δ1 and Δ0⊢𝑅<:𝑆, then substituting 𝑅 for 𝑋 throughout the judgment and the trailing contexts yields a derivable judgment under Δ0,Δ1[𝑅/𝑋].
Proof of Lemma 15.18 — Target narrowing and substitution
Proof. The four proofs are simultaneous inductions on derivation height, with the judgment form as a secondary case split. For item 1, the distinguished bound-variable case changes 𝑋<:𝑆 into 𝑋<:𝑅<:𝑆 by transitivity. Other variables keep their bounds. Arrow and record subtyping apply the induction hypotheses component by component. In S-Exists, alpha-rename the existential variable, narrow the outer bound in both premises, and restore the rule. In S-Amber, make its two recursive variables fresh for 𝑋, narrow the body comparison, and restore the same simulation hypothesis. The typing rules merely propagate the narrowed type context.
For item 2, the distinguished term variable is typed at 𝑅 and then raised to 𝑆 by subsumption. Other variables are unchanged. Lambda, open, and letrec binders are first alpha-renamed away from 𝑥; the induction hypothesis applies to each premise in which the outer 𝑥 remains free. Pack, fold, unfold, records, projection, and application restore their last rules. This proves item 2 of lemma 15.18.
For item 3, the variable-bound case uses the premise 𝑅<:𝑆. The arrow and record cases are componentwise. For bounded existentials, choose the bound variable fresh for 𝑅, apply the induction hypothesis to its bound and body, and restore S-Exists, F-Pack, or F-Open. The open escape condition is preserved because its freshly bound variable is not substituted. For S-Amber, first freshen both recursive variables; substitution then commutes with their binding, and the substituted premise restores Amber. Fold and unfold use the composition equality (𝑇[𝜇𝑌.𝑇/𝑌])[𝑅/𝑋]=𝛼𝑇[𝑅/𝑋][𝜇𝑌.𝑇[𝑅/𝑋]/𝑌]. The letrec case substitutes in every parameter type, result type, defining term, and body before restoring F-Letrec.
For item 4, the distinguished variable case is exactly the assumed typing of 𝑢; every other variable retains its declaration. Alpha-renaming handles lambda, open, and letrec binders. In a package, substitute in its payload and retain the same witness and existential family; in an open term, substitute independently in the package and body and restore the escape condition. Successor, records, projection, application, fold, and unfold are homomorphic. Subsumption restores its unchanged target subtype judgment. These cases exhaust the target syntax. ◻
After alpha-renaming the bound variables to 𝑋, if Δ⊢∃𝑋<:𝑆0.𝑇0<:∃𝑋<:𝑆.𝑇, then Δ⊢𝑆0<:𝑆,Δ,𝑋<:𝑆0⊢𝑇0<:𝑇. Moreover, every instance of F-Open whose package is the package value in definition 15.17 gives its reduct the same result type.
Proof of Lemma 25.23 — Existential inversion and the generalized open root
Proof. Prove inversion together with this shape claim: if an existential is below 𝐶, then 𝐶 is an existential or 𝖳𝗈𝗉. Rule induction proves the shape claim, with transitivity using it at both subderivations. Reflexivity gives 𝑆0=𝑆 and 𝑇0=𝑇. If the last rule is S-Exists, its premises are exactly 𝑆0<:𝑆 and Δ,𝑋<:𝑆0⊢𝑇0<:𝑇. In the transitive case the intermediate type cannot be 𝖳𝗈𝗉: no derivation can conclude Δ⊢𝖳𝗈𝗉<:𝐶 with 𝐶≠𝖳𝗈𝗉, by lemma 25.20. Write it as ∃𝑋<:𝑆1.𝑇1. The two induction hypotheses give 𝑆0<:𝑆1<:𝑆,𝑋<:𝑆0⊢𝑇0<:𝑇1,𝑋<:𝑆1⊢𝑇1<:𝑇. Narrow the last judgment from 𝑆1 to 𝑆0 by lemma 15.18 item 1, then compose both pairs by transitivity.
Now type a generalized open redex at 𝑈. Inversion of the package typing, including any final chain of F-Sub, gives Δ⊢𝑅<:𝑆0,Δ;Γ⊢𝑡:𝑇0[𝑅/𝑋],Δ⊢∃𝑋<:𝑆0.𝑇0<:∃𝑋<:𝑆.𝑇. This package inversion is an induction on typing: F-Pack gives the first two judgments, and every F-Sub composes one more subtype step. The outer-constructor inversion just proved ensures that each intermediate type on a path ending in an existential is again an existential.
Existential inversion gives 𝑆0<:𝑆 and 𝑋<:𝑆0⊢𝑇0<:𝑇. Substitute 𝑅 for 𝑋 in the latter judgment by lemma 15.18 item 3 and use subsumption to obtain Δ;Γ⊢𝑡:𝑇[𝑅/𝑋]. The other premise of F-Open is Δ,𝑋<:𝑆;Γ,𝑥:𝑇⊢𝑢:𝑈,𝑋∉𝖥𝖵(𝑈). Since 𝑅<:𝑆0<:𝑆, type substitution gives Δ;Γ,𝑥:𝑇[𝑅/𝑋]⊢𝑢[𝑅/𝑋]:𝑈; the escape condition removes 𝑋 from the result type. Term substitution with the payload now gives Δ;Γ⊢𝑢[𝑅/𝑋][𝑡/𝑥]:𝑈, which is precisely the generalized open reduct. ◻
The recursive root is well typed. Rule F-Letrec gives 𝑟𝐷:𝐹. By substitution (item 4 of lemma 15.18), the defining judgment gives 𝑡[𝑟𝐷/𝑓]:𝑇, so the lambdas give ℎ𝐷:𝐹. A second use of item 4 in the body judgment gives 𝑢[ℎ𝐷/𝑓]:𝑈, exactly the type of the reduct.
For 𝐴=[ℓ𝑖:𝐵𝑖]𝑖∈𝐼, define 𝐶𝐴(𝑋):={ℓ𝗌𝖾𝗅𝑖:𝑋→𝐵∗𝑖,ℓ𝗎𝗉𝖽𝑖:(𝑋→𝐵∗𝑖)→𝑋(𝑖∈𝐼),𝗌𝖾𝗅𝖿:𝑋}, and 𝐴∗:=𝜇𝑌.∃𝑋<:𝑌.𝐶𝐴(𝑋). Ground types and 𝖳𝗈𝗉 translate to their target counterparts. Selection makes 𝐵∗𝑖 occur covariantly; update makes it occur contravariantly. Their combination explains why the source component is invariant. The bound 𝑋<:𝑌 hides the object’s full interface while recording that it implements the public one.
Proof of Lemma 15.19 — Width is preserved by the type translation
Proof. Reflexivity, transitivity, and 𝖳𝗈𝗉 are preserved by the corresponding target rules. Consider width. Write 𝐴=[ℓ𝑖:𝐵𝑖]𝑖∈𝐼,𝐵=[ℓ𝑗:𝐵𝑗]𝑗∈𝐽,𝐽⊆𝐼. The shared result types are identical. For any representation variable 𝑋, S-Rec gives 𝐶𝐴(𝑋)<:𝐶𝐵(𝑋): it drops both ℓ𝗌𝖾𝗅 and ℓ𝗎𝗉𝖽 for every hidden label, while retaining 𝗌𝖾𝗅𝖿:𝑋. To apply Amber, work under 𝑌𝐵<:𝖳𝗈𝗉,𝑌𝐴<:𝑌𝐵. Rule S-Exists uses the latter bound and, under 𝑋<:𝑌𝐴, the record comparison just proved. Hence ∃𝑋<:𝑌𝐴.𝐶𝐴(𝑋)<:∃𝑋<:𝑌𝐵.𝐶𝐵(𝑋). Rule S-Amber now yields 𝜇𝑌𝐴.∃𝑋<:𝑌𝐴.𝐶𝐴(𝑋)<:𝜇𝑌𝐵.∃𝑋<:𝑌𝐵.𝐶𝐵(𝑋). No component variance is used: the common selection and update field types are literally the same. ◻
For a label ℓ and a result type 𝐵, put 𝐿ℓ,𝐵:=𝜇𝑌.∃𝑋<:𝑌.{ℓ𝗌𝖾𝗅:𝑋→𝐵∗,𝗌𝖾𝗅𝖿:𝑋}.
Proof of Lemma 15.20 — The visible-method target bound
Proof. Use S-Amber with recursive variables 𝑌0,𝑌𝐿. Work in the type context 𝑌𝐿<:𝖳𝗈𝗉,𝑌0<:𝑌𝐿. There S-Exists reduces the desired body comparison to 𝐶𝐴0(𝑋)<:{ℓ𝗌𝖾𝗅:𝑋→𝐵∗,𝗌𝖾𝗅𝖿:𝑋}(𝑋<:𝑌0). This is one application of covariant record width: retain the selection and self fields, and discard every update field and every field for a different label. The retained selection type is literally 𝑋→𝐵∗ because source object components are invariant. Restoring S-Exists and then S-Amber proves the displayed subtype. ◻
For a fixed object type 𝐴=[ℓ𝑖:𝐵𝑖]𝑖∈𝐼, take 𝐼={1,…,𝑛} and write 𝑜=[ℓ𝑖=𝜍(𝑠𝑖:𝐴)𝑏𝑖]𝑖∈𝐼. First fix the recursive declaration 𝐷𝐴≡create(𝑓𝑖:𝐴∗→𝐵∗𝑖)𝑖∈𝐼:𝐴∗=𝖿𝗈𝗅𝖽𝐴∗(𝗉𝖺𝖼𝗄𝑋<:𝐴∗=𝐴∗𝗐𝗂𝗍𝗁{ℓ𝗌𝖾𝗅𝑖=𝑓𝑖,ℓ𝗎𝗉𝖽𝑖=𝜆𝑔:𝐴∗→𝐵∗𝑖.create(𝑓1,…,𝑓𝑖−1,𝑔,𝑓𝑖+1,…,𝑓𝑛),𝗌𝖾𝗅𝖿=create(𝑓1,…,𝑓𝑛)}𝖺𝗌∃𝑋<:𝐴∗.𝐶𝐴(𝑋)). Recall 𝑟𝐷𝐴=𝗅𝖾𝗍𝗋𝖾𝖼𝐷𝐴𝗂𝗇create. The object translation is the application of that recursive function: trΓ(𝑜):=𝑟𝐷𝐴(𝜆𝑠𝑖:𝐴∗.trΓ,𝑠𝑖:𝐴(𝑏𝑖))𝑖∈𝐼. If ¯𝑓=(𝑓𝑖)𝑖∈𝐼, the letrec root followed by beta roots gives 𝑟𝐷𝐴(¯𝑓)⟼∗𝖿𝗈𝗅𝖽𝐴∗(𝗉𝖺𝖼𝗄𝑋<:𝐴∗=𝐴∗𝗐𝗂𝗍𝗁{ℓ𝗌𝖾𝗅𝑖=𝑓𝑖,ℓ𝗎𝗉𝖽𝑖=𝜆𝑔.𝑟𝐷𝐴(¯𝑓[𝑖:=𝑔]),𝗌𝖾𝗅𝖿=𝑟𝐷𝐴(¯𝑓)}𝖺𝗌∃𝑋<:𝐴∗.𝐶𝐴(𝑋)), with the bound and existential annotation exactly as in equation 25.2. Thus the stored self and the separately translated object are literally the same term 𝑟𝐷𝐴(¯𝑓), not two letrec expressions that merely unfold to similar records. Each update calls the same recursive function with one replacement, so its stored self is the new object rather than a frozen predecessor. The occurrence of create in the 𝗌𝖾𝗅𝖿 field is not evaluated eagerly. The target relation is a compatible congruence, not a strategy; one letrec unfolding produces the finite term displayed above, and later reductions unfold another copy only when that occurrence is used.
For a label ℓ, minimum typing and lemma 25.9 make its component type 𝐵 independent of which declarative object supertype was used to type the receiver. Put 𝑢:=𝑎.ℓ⇐𝜍(𝑠:𝐴)𝑏. Invocation and override translate as trΓ(𝑎.ℓ):=𝗈𝗉𝖾𝗇𝗎𝗇𝖿𝗈𝗅𝖽(trΓ(𝑎))𝖺𝗌𝑋<:𝐿ℓ,𝐵,𝑧:{ℓ𝗌𝖾𝗅:𝑋→𝐵∗,𝗌𝖾𝗅𝖿:𝑋}𝗂𝗇𝑧.ℓ𝗌𝖾𝗅𝑧.𝗌𝖾𝗅𝖿,trΓ(𝑢):=𝗈𝗉𝖾𝗇𝗎𝗇𝖿𝗈𝗅𝖽(trΓ(𝑎))𝖺𝗌𝑋<:𝐴∗,𝑧:𝐶𝐴(𝑋)𝗂𝗇𝑧.ℓ𝗎𝗉𝖽(𝜆𝑠:𝑋.trΓ,𝑠:𝐴(𝑏)).(𝑇𝐼𝑛𝑣)(𝑇𝑈𝑝𝑑) Variables translate to themselves. The environment translation replaces every 𝑥:𝐴 by 𝑥:𝐴∗. In (TUpd), the opened representation 𝑋 cannot escape: the update field first returns 𝑋, but target subsumption uses 𝑋<:𝐴∗ before the 𝗈𝗉𝖾𝗇 closes, so the whole term has the public type 𝐴∗.
If Γ,𝑠:𝐴⊢𝑎:𝐵 and 𝐴0<:𝐴, then, after choosing all generated binders fresh, trΓ,𝑠:𝐴(𝑎)=𝛼trΓ,𝑠:𝐴0(𝑎). The right-hand translation is defined because lemma 15.8 gives Γ,𝑠:𝐴0⊢𝑎:𝐵.
Proof of Lemma 25.26 — Translation is invariant under receiver narrowing
Proof. Induct on the source term. Variables and ground forms translate identically. For an object, its self annotations, declaration 𝐷𝐴, and generated create-parameter types are fixed by its syntax. Alpha-rename each method’s self binder away from the distinguished 𝑠, use item 3 of lemma 15.3 to put the outer 𝑠 last, and apply the induction hypothesis to the body. Thus every translated method function, and hence the application of 𝑟𝐷𝐴, is alpha-equal in the two environments.
For an invocation 𝑟.ℓ, let 𝑃 and 𝑃0 be the minimum types of 𝑟 before and after narrowing. The old minimum derivation gives Γ,𝑠:𝐴⊢𝑟:𝑃; source narrowing gives Γ,𝑠:𝐴0⊢𝑟:𝑃. Minimum typing in the narrowed context therefore gives 𝑃0<:𝑃. If the invoked component of 𝑃 is ℓ:𝐶, lemma 25.9 says that the same component of 𝑃0 is exactly 𝐶. Consequently both translations use 𝐿ℓ,𝐶, while their receiver translations are alpha-equal by induction.
For an override, the public object annotation and component type used in (TUpd) are written in the source term and hence unchanged. Apply the induction hypothesis to the receiver and, after freshening its bound self variable and using item 3 of lemma 15.3, to the replacement body. The generated open and lambda binders may then be alpha-renamed to agree. These cases exhaust the nonrecursive source syntax. ◻
Proof of Lemma 15.21 — Translation commutes with source substitution
Proof. Induct on 𝑏. The distinguished variable gives the displayed substituend; another variable is unchanged. For an object, alpha-rename every self binder and every generated create parameter away from 𝑥 and the free variables of 𝑎. Apply the induction hypothesis to each method body. The declaration 𝐷𝐴 and surrounding application of 𝑟𝐷𝐴 are syntactically identical on both sides, so the translated method functions are equal up to the chosen alpha-renaming.
For invocation, suppose the receiver 𝑟 has minimum type 𝑃 in Γ,𝑥:𝐴, and its ℓ-component is 𝐵ℓ. Minimum typing first gives Γ,𝑥:𝐴⊢𝑟:𝑃. Source substitution then gives Γ⊢𝑟[𝑎/𝑥]:𝑃. If 𝑃0 is the minimum type of the substituted receiver, the minimum-typing theorem yields 𝑃0<:𝑃. Lemma 25.9 therefore says that the ℓ-component of 𝑃0 is the same 𝐵ℓ, even though substitution may have refined the receiver minimum. The receiver translations agree by the induction hypothesis, and both sides consequently open the identical bound 𝐿ℓ,𝐵ℓ.
For override, apply the induction hypothesis to the receiver and, after making its self binder fresh, to the replacement body. The public annotation and its selected component are fixed in the source syntax, so term substitution changes neither. Variables, object literals, invocation, override, and ground constructors commute with substitution by the cases just given, exhausting the nonrecursive source term grammar. ◻
Minimum typing chooses the source shape from which the target package is built. In the invocation and override cases, target narrowing changes the opened self assumption from the public interface to the hidden witness type. For each source root, the simulation proof first performs the target administrative reductions that expose the translated method body and then uses translation-substitution on that body.
Proof of Theorem 25.28 — Scoped typing of the object translation
Proof. Let 𝐴0 be the unique minimum type of 𝑎. Induct on its minimum-typing derivation to obtain Γ∗⊢trΓ(𝑎):𝐴∗0. Since 𝐴0<:𝐴, lemma 15.19 and target subsumption then give the stated type 𝐴∗. Variables use the translated context.
Objects. For an object, M-Object gives minimum body types 𝐵′𝑖<:𝐵𝑖. The induction hypotheses and target subsumption therefore type 𝜆𝑠𝑖:𝐴∗.tr(𝑏𝑖):𝐴∗→𝐵∗𝑖. Assume these functions as the parameters 𝑓𝑖 of create. With existential witness 𝑋=𝐴∗, the selection field ℓ𝗌𝖾𝗅𝑖=𝑓𝑖 has type 𝐴∗→𝐵∗𝑖, and the update field ℓ𝗎𝗉𝖽𝑖=𝜆𝑔.create(¯𝑓[𝑖:=𝑔]) has type (𝐴∗→𝐵∗𝑖)→𝐴∗. The 𝗌𝖾𝗅𝖿 field create(¯𝑓) also has type 𝐴∗. Hence the record has type 𝐶𝐴(𝐴∗). Rule F-Pack, with witness 𝐴∗<:𝐴∗ and existential family 𝐶𝐴(𝑋), gives ∃𝑋<:𝐴∗.𝐶𝐴(𝑋); F-Fold then returns 𝐴∗. Rule F-Letrec therefore types 𝑟𝐷𝐴 at (𝐴∗→𝐵∗1)→⋯→(𝐴∗→𝐵∗𝑛)→𝐴∗. Applying it to the translated method functions proves the object case.
Invocation. For invocation, let 𝐴0 be the minimum type of the receiver. It contains ℓ:𝐵, so lemma 15.20 gives 𝐴∗0<:𝐿ℓ,𝐵. Target subsumption followed by F-Unfold therefore gives an existential package whose hidden representation 𝑋 is below 𝐿ℓ,𝐵. Opening it yields 𝑧.ℓ𝗌𝖾𝗅:𝑋→𝐵∗,𝑧.𝗌𝖾𝗅𝖿:𝑋, so their application has type 𝐵∗. The result does not contain 𝑋, which discharges the existential escape condition.
Override. For override, let 𝐴0 be the minimum receiver type. Rule M-Override gives 𝐴0<:𝐴, so lemma 15.19 permits target subsumption from 𝐴∗0 to 𝐴∗ before unfold. Opening gives 𝑧.ℓ𝗎𝗉𝖽:(𝑋→𝐵∗)→𝑋,𝑋<:𝐴∗. The same source rule gives a minimum body type 𝐵′<:𝐵. The induction hypothesis followed by target subsumption types its translation at 𝐵∗ under 𝑠:𝐴∗. In the opened type context, 𝑋<:𝐴∗; target term-context narrowing, lemma 15.18 item 2, changes that assumption to 𝑠:𝑋. Thus the lambda has type 𝑋→𝐵∗. The update field returns 𝑋, and target subsumption returns the public result type 𝐴∗, again free of the opened 𝑋. ◻
If Γ⊢𝑎:𝐴 and 𝑎⟼𝑎′, then trΓ(𝑎)⟼∗trΓ(𝑎′) in the target. The theorem claims neither reflection of every target reduction nor full abstraction for contextual equivalence.
Proof of Theorem 15.22 — Scoped typing and simulation
Proof.Simulation contexts. For simulation, induct on the source reduction. The compatible-context cases require one check because the translation is type directed. Suppose a receiver step 𝑎⟼𝑎′ occurs under invocation by ℓ. Inversion of the enclosing typing yields an interface 𝐾 containing ℓ:𝐵, together with the derivation Γ⊢𝑎:𝐾. Source preservation gives Γ⊢𝑎′:𝐾. If 𝑃 and 𝑃′ are the respective minimum receiver types, minimum typing gives 𝑃<:𝐾 and 𝑃′<:𝐾. Lemma 25.9 recovers the identical component 𝐵 in both minima. Thus both outer translations use exactly 𝐿ℓ,𝐵, and target congruence lifts the induction-hypothesis sequence from tr(𝑎) to tr(𝑎′).
For a receiver step under override, the public annotation, selected label, and selected component are fixed syntactically by the override term. The two outer target contexts are therefore identical, and target congruence again lifts the induction hypothesis. Ground evaluation contexts, such as 𝗌𝗎𝖼𝖼(−), translate homomorphically. Weak evaluation has no context beneath a method binder. This exhausts the compatible contexts.
Invocation root. At an invocation root, write the receiver translation as 𝑟𝐷𝐴(¯𝑓). The letrec and beta roots in equation 25.4, followed by unfold and the generalized open root justified by lemma 25.23, expose its record. Projection selects 𝑓𝑗, while 𝗌𝖾𝗅𝖿 selects the literal term 𝑟𝐷𝐴(¯𝑓). Beta reduction therefore gives (𝜆𝑠𝑗:𝐴∗.tr(𝑏𝑗))𝑟𝐷𝐴(¯𝑓). The argument is definitionally tr([ℓ𝑖=𝜍(𝑠𝑖:𝐴)𝑏𝑖]𝑖∈𝐼), so this reduces by target substitution to the translation of the source substitution in (Invoke), by lemma 15.21.
Override root. At an override root, let the runtime receiver have construction type 𝐴0 and let the override carry the public annotation 𝐴. As in (Override-Sub), minimum typing gives 𝐴0<:𝐴. The receiver translation therefore contains an actual package annotated ∃𝑋<:𝐴∗0.𝐶𝐴0(𝑋), whereas (TUpd) opens it at ∃𝑋<:𝐴∗.𝐶𝐴(𝑋). The generalized open root is applicable: its typing is exactly the package-subsumption case of lemma 25.23, and it substitutes the actual witness 𝐴∗0 and actual record payload.
Projection selects the actual ℓ𝗎𝗉𝖽𝑗 field. Its application and beta steps reduce to 𝑟𝐷𝐴0(𝑓1,…,𝑓𝑗−1,𝑔0,𝑓𝑗+1,…,𝑓𝑛),𝑔0=𝜆𝑠:𝐴∗0.trΓ,𝑠:𝐴(𝑏). Source receiver narrowing reannotates the replacement method by 𝐴0 in the reduct. By lemma 25.26, trΓ,𝑠:𝐴(𝑏)=𝛼trΓ,𝑠:𝐴0(𝑏), so 𝑔0 is alpha-equal to the method function in the translation of that reannotated source object. Thus the displayed 𝑟𝐷𝐴0-application is literally the translation of the source reduct. This proves the direct directional step tr(𝑎)⟼∗tr(𝑎′); no reverse target reduction is used. Its stored 𝗌𝖾𝗅𝖿 is that same application, so subsequent invocations see the replacement. Ground contractions translate homomorphically. These cases prove item 2. ◻
★★★ In equation 15.2, abbreviate the two original method functions of 𝑝0 by 𝑓𝑥,𝑓get, and put 𝑔:=𝜆𝑠:𝑃∗.1. Translate 𝑝1.get. Using only the root contractions of definition 15.17, first reduce the override to 𝑟𝐷𝑃(𝑔,𝑓get). Then reduce the invocation until 𝑓get is applied to the 𝗌𝖾𝗅𝖿 field 𝑟𝐷𝑃(𝑔,𝑓get) of that same unfolding, and continue to 1. Name every letrec, beta, unfold, open, and projection root.
The translation gives a semantic explanation, not a proposal for an efficient object layout. Its bounded existential hides private methods; its recursive record connects the hidden representation to the public interface; its paired selection and update components enforce invariance; and recursive creation re-ties self after every functional override. These four jobs cannot be performed by the naive recursive record alone.
The extension obstruction
The recursive point returns exactly 𝖯𝗈𝗂𝗇𝗍 from move. Add a color method and one expects the moved colored point to remain colored. Define the two candidate equations 𝖯𝗈𝗂𝗇𝗍=𝜇𝑋.[𝑥:𝖭𝖺𝗍,get:𝖭𝖺𝗍,move:𝑋],𝖢𝗈𝗅𝗈𝗋𝖯𝗈𝗂𝗇𝗍=𝜇𝑋.[𝑥:𝖭𝖺𝗍,get:𝖭𝖺𝗍,move:𝑋,color:𝖭𝖺𝗍]. After unfolding, one would like width subtyping to forget color. Invariance blocks the comparison because the common move components are 𝖢𝗈𝗅𝗈𝗋𝖯𝗈𝗂𝗇𝗍 and 𝖯𝗈𝗂𝗇𝗍, not the same type. Replacing invariance by covariance is not a remedy: proposition 15.12 shows that another retained method can rely on the more precise result and be invalidated by override through the shorter view. This is the precise contrast with covariant immutable records in chapter 8: record projection only reads a component, whereas an object method can be replaced and every retained method may invoke that replacement through self. The read/write combination forces invariance.
One may instead declare the colored point’s move method to return 𝖯𝗈𝗂𝗇𝗍. That permits forgetting color after a move, but it does not express the desired fluent interface. The missing phrase is “the exact type of the present receiver, as refined by extension.” A recursively bound type variable fixes one equation; it does not vary with later extension. This is the Point/ColorPoint obstruction. No solution to it is assumed in this chapter.
The obstruction is a fixed recursive equation where extension requires a type that varies with the current receiver. F-bounded quantification and matching provide that varying type for immutable records. The reusable ingredients are late self and the shape 𝜇𝑌.∃𝑋<:𝑌.𝐶(𝑋); covariance becomes safe once functional update is removed.
★★☆ Unfold 𝖯𝗈𝗂𝗇𝗍 and 𝖢𝗈𝗅𝗈𝗋𝖯𝗈𝗂𝗇𝗍 once. Attempt the width-invariant subtyping derivation in both directions and identify the first unequal shared component. Then replace the result of the colored move method by 𝖯𝗈𝗂𝗇𝗍 and state exactly what useful typing is recovered and what fluent typing is lost.
The primitive syntax, self substitution, functional invocation and override, first-order typing, width-invariant subtyping, minimum typing, the covariance and extraction failures, and explicit recursive-object examples follow Abadi and Cardelli, A Theory of Primitive Objects: Untyped and First-Order Systems, author full version, Sections 2.1, 3.1–3.2.2, 4.1.1–4.1.2, 4.5.1–4.5.2, and 4.6–4.8. The two typed safety developments are at PDF pp. 15–17 and 21–23; the failed covariance/extraction rules and recursive objects are at PDF pp. 24–29 [AC96b]. The package/open typing and generalized contraction are in Table 1 of Abadi, Cardelli, and Viswanathan, An Interpretation of Objects and Object Types. Its target type 𝜇𝑌.∃𝑋<:𝑌.𝐶𝐴(𝑋), separate selection, update, and self fields, recursive create, and exact translation scope are in Sections 3.2–3.3, Table 2, and Theorems 3.1–3.3, PDF pp. 3–6 [ACV96]. That source proves computational adequacy but explicitly does not claim full abstraction; this chapter makes no stronger claim.
★★☆ Let 𝑇=[𝑎:𝖭𝖺𝗍,𝑏:𝖭𝖺𝗍,𝑐:𝖭𝖺𝗍] and 𝑡=[𝑎=𝜍(𝑠:𝑇)0,𝑏=𝜍(𝑠:𝑇)𝑠.𝑎,𝑐=𝜍(𝑠:𝑇)𝑠.𝑏]. Override 𝑎 with the constant method returning 4, then reduce 𝑐 on the new object. Give the typing derivation and every invocation step, keeping the runtime receiver visible.
★★☆ For 𝐴=[𝑝:𝖴𝗇𝗂𝗍],𝐵=[𝑝:𝖴𝗇𝗂𝗍,𝑞:𝖴𝗇𝗂𝗍],𝑏=[𝑝=𝜍(𝑠:𝐵)𝗎𝗇𝗂𝗍,𝑞=𝜍(𝑠:𝐵)𝑠.𝑝], compute the minimum types of 𝑏, 𝑏.𝑝, and 𝑏.𝑝⇐𝜍(𝑠:𝐴)𝗎𝗇𝗂𝗍. Prove that every other declarative type of each term is a supertype of the one you give.
★★☆ Reprove preservation for invocation in 𝖮𝖻<:1 without saying “by inversion” as a single step. Start from an arbitrary declarative derivation, extract its minimum receiver type, use width invariance to recover the selected component, display the body-substitution judgment, and restore the requested result supertype.
★★☆ Modify the covariance counterexample so that 𝑄𝑐 has a third method 𝑘:𝑆 whose body invokes 𝑛, and the final stuck term invokes 𝑘 before selecting tag. Give all types and show the full reduction. Explain why the extra indirection does not change the source of the failure.
★★★ Let 𝐴=[ℓ:𝐵], Γ,𝑠:𝐴⊢𝑏:𝐵, and Γ,𝑠:𝐴⊢𝑐:𝐵. Put 𝑜=[ℓ=𝜍(𝑠:𝐴)𝑏] and 𝑢=𝑜.ℓ⇐𝜍(𝑠:𝐴)𝑐. Type every field of 𝐶𝐴(𝐴∗), its package as ∃𝑋<:𝐴∗.𝐶𝐴(𝑋), and the enclosing fold. Put 𝑜′=[ℓ=𝜍(𝑠:𝐴)𝑐]. First show the source roots 𝑢⟼𝑜′ and 𝑜′.ℓ⟼𝑐[𝑜′/𝑠]. Then use the target roots and the translation theorem to simulate trΓ(𝑢.ℓ)⟼∗trΓ(𝑜′.ℓ)⟼∗trΓ(𝑐[𝑜′/𝑠]). Each arrow is a nonempty many-step target reduction; name every letrec, beta, unfold, open, and projection root it contains. Display the 𝑟𝐷𝐴-application containing the translated replacement method and use lemma 15.21 only for the final invocation beta step.
★★☆ Construct origin, next, and a colored analogue whose move method returns 𝖢𝗈𝗅𝗈𝗋𝖯𝗈𝗂𝗇𝗍. Type each object at its own recursive type. Then show that width-invariant subtyping cannot pass the colored object to a context requiring 𝖯𝗈𝗂𝗇𝗍, although both objects separately satisfy preservation and progress.
★★☆ Erase the self annotations from object literals and let 𝐴=[ℓ:[]], 𝐴′=[ℓ:𝐴], and 𝑎=[ℓ=𝜍(𝑠)[ℓ=𝜍(𝑡)[]]]. Work in 𝖮𝖻<:1 with subsumption and the erased formation rule 𝐴=[ℓ𝑖:𝐵𝑖]𝑖∈𝐼Γ,𝑠𝑖:𝐴⊢𝑏𝑖:𝐵𝑖(𝑖∈𝐼)Γ⊢[ℓ𝑖=𝜍(𝑠𝑖)𝑏𝑖]𝑖∈𝐼:𝐴T−Object−Erased. For the typing at 𝐴, show explicitly how width subtyping and T-Sub type the inner one-method object at the empty object result type. Give another typing of 𝑎 at 𝐴′. Prove that 𝐴 and 𝐴′ have no common subtype, so the unannotated calculus has no minimum type for 𝑎. Identify the two points at which the proof of theorem 15.10 can no longer recover a type from syntax.
★★☆ Consider the incorrect target scheme that recursively defines one record 𝑟 with selection fields ℓ𝗌𝖾𝗅𝑖, sets 𝑟.𝗌𝖾𝗅𝖿=𝑟, and implements source override by functional record update of one selection field. Translate 𝑝1.get with this scheme and reduce it to 0. Then place beside it the source reduction to 1 and locate the frozen occurrence of 𝑟 responsible for the discrepancy.
★★☆ Under Δ,𝑋;Γ,𝑠:𝐴⊢𝑏:𝐵, Δ⊢𝐶𝗍𝗒𝗉𝖾, and Δ,𝑋;Γ⊢𝑎:𝐴, freshen every binder away from the substituends and prove 𝑏[𝐶/𝑋][𝑎[𝐶/𝑋]/𝑠]=𝛼𝑏[𝑎/𝑠][𝐶/𝑋]. Work out the object-body and fold-payload cases. Explain why no condition 𝑋∉𝖥𝖵(𝑎) is needed and why a type 𝐶 cannot contain the term variable 𝑠.
★★★Practical project.object-override-simulator Implement a finite object checker and evaluator for the point, override, fluent-self, and frozen-self cases. Preserve the invariant that override restores the runtime minimum annotation and every method body sees the rebuilt receiver. Include seven named observations that isolate late self, minimum typing, override reconstruction, and rejection of the frozen candidate. Then change creation to retain the old receiver, retain only the apparent annotation, and make components covariant in three independent variants; each must falsify its corresponding observation. The finite machine checks these traces, not the translation or safety theorems. Appendix E records the commands, and appendix F develops the implementation.