Lectures onType Theory
ch:object-calculi: ch:object-calculi
appendix sectionsolutions

ch:object-calculi: ch:object-calculi

exercise 15.1.

Twice applying T-Override to p0:P gives p2:P. Put o2=[x=ς(s:P)2,get=ς(s:P)s.x],o3=[x=ς(s:P)3,get=ς(s:P)s.x]. Compatible reduction first contracts the inner override and installs 2: p2o2.xς(s:P)3o3. The outer override therefore installs 3, after which the two invocation roots give p2.geto3.geto3.x3. Separately p0.getp0.x0. The unchanged old receiver still returning 0 after p2 is built is the exact functional, rather than imperative, observation.

exercise 15.2.

The receiver and replacement premises are p0:P and s:Ps.get:Nat, the latter by T-Invoke. For the reduct, its new x-body has exactly that derivation. Its retained body is typed by the other invocation s:Ps.x:Nat. These are the two premises of T-Object, so the reduct has P, the type of the redex.

exercise 15.3.

Rule M-Object gives q minimum type Q; minimum invocation gives q.x:Nat. Put A=[x:Nat]. Since Q<:A, the receiver and constant-body premises of M-Override give the last term minimum type A. Declaratively, subsume q:Q to q:A, type 2 under s:A, and apply T-Override; this also yields A.

exercise 15.4.

Use the chapter’s types S=[tag:Unit]<:T=[], Q=[m:S,n:S], and P=[m:T,n:S]. The proposed covariant rule gives Q<:P, hence q:P. Rule T-Override types r=q.mς(s:P)t0:P; invocation gives r.n:S, and a second invocation gives (r.n).tag:Unit. The very first root, the override contraction, produces a runtime-Q literal whose new m body is t0:T, although T-Object requires S. That is the first missing preservation premise. Continuing makes the defect observable: r.nr.mt0, so tag selection is stuck.

For extraction, width is used exactly in a:Q<:P, before T-Extract. It changes the claimed self parameter of the extracted method from Q, which contains y, to P, which does not. Consequently the alleged PNat function can be applied to p:P, but reduces to plus(p.x,p.y) and is stuck at p.y.

exercise 15.5.

Unfolding Point gives UPoint, whose move component is Point. Hence origin:Point,(unfoldorigin).move:Point,p2:Point by two uses each of T-Unfold and T-Invoke. Let o0 be the runtime receiver inside origin. Abbreviate the retained move body by M(s)=foldPoint(s.xς(t:UPoint)succ(s.x)). The two distinct updated literals are o1=[x=ς(t:UPoint)succ(o0.x),get=ς(s:UPoint)s.x,move=ς(s:UPoint)M(s)],o2=[x=ς(t:UPoint)succ(o1.x),get=ς(s:UPoint)s.x,move=ς(s:UPoint)M(s)]. The first unfold and move invocation gives foldPointo1; unfolding that fold and invoking the retained move gives foldPointo2. Therefore (unfoldp2).geto2.geto2.xsucc(o1.x)succ(succ(o0.x))succ(succ(0))2. The occurrences o0,o1,o2 make the two successive late-self substitutions visible.

exercise 15.6.

Write R(f,h)=rDP(f,h). Then tr(p0)=R(fx,fget). In the translated override, the letrec root for rDP and its parameter beta roots expose the folded package. The unfold root removes the fold; the generalized open root substitutes the actual witness and record; projection selects xupd=λk:PNat.R(k,fget). Its beta root yields R(g,fget), literally tr(p1).

Target congruence performs those roots inside the receiver of the translated get-invocation. A second letrec/beta unfolding, followed by unfold, open, and the two projections, gives fget(R(g,fget)). Since fget=λs:P.trs:P(s.x), beta gives the translated x-invocation on that same R-term. Its letrec/beta, unfold, open, selection, self, and beta roots give g(R(g,fget))1. No reverse step or frozen record identity is used.

exercise 15.7.

One unfold exposes equal x and get components, but the shared move components are respectively Point and ColorPoint. Invariance blocks both directions there (and width also blocks Point-to-ColorPoint). Changing the colored result to Point recovers the width judgment ColorPoint-to-Point, so point clients may invoke move, but the result has type Point and color cannot be invoked afterward.

exercise 15.8.

Under s:T, the three bodies have the judgments 0:Nat,s.a:Nat,s.b:Nat. Thus T-Object gives t:T. Also s:T4:Nat, so T-Override and then T-Invoke give (t.aς(s:T)4).c:Nat. The concrete updated receiver is t4=[a=ς(s:T)4,b=ς(s:T)s.a,c=ς(s:T)s.b]. Compatible reduction first contracts the override in receiver position. Every remaining arrow is an invocation root whose displayed receiver is substituted for the method’s self binder: (t.aς(s:T)4).ct4.ct4.bt4.a4. The two retained bodies therefore consult b and then a through t4, not through the original t.

exercise 15.9.

The three minimum types are B, Unit, and A. The last uses B<:A in M-Override. The minimum-typing theorem says any declarative conclusion is a supertype of the corresponding result; explicitly these are object-width supertypes of B, only Unit or Top for the projection, and object-width supertypes of A.

exercise 15.10.

From an arbitrary typing of v.j, minimum typing gives the closed receiver a least object type A0<:A. Canonical forms makes v a literal built at A0=[i:Ci]iI. Width invariance recovers Cj=Bj. Formation inversion gives sj:A0bj:Cj, and source substitution with v:A0 gives bj[v/sj]:Bj. If the requested invocation conclusion arose by subsumption, restore that final supertype by T-Sub.

exercise 15.11.

Put Q=[m:S,n:S,k:S] and P=[m:T,n:S,k:S]. Extend q by k=ς(s:Q)s.n, and define r=q.mς(s:P)t0. Covariance claims Q<:P, so r:P, r.k:S, and (r.k).tag:Unit. After the override root, let v name the updated literal. The complete invocation schedule is (r.k).tag(v.k).tag(v.n).tag(v.m).tagt0.tag, which is stuck. The added kn call only delays the use of the illegally weakened m-result; the covariant S<:T component remains the first failed premise.

exercise 15.12.

For A=[:B], CA(X) has sel:XB, upd:(XB)X, and self:X. At witness X=A, these become AB, (AB)A, and A; there is no A/X mismatch. The record is packed at X<:A.CA(X) and folded at A.

Put o=[=ς(s:A)c]. The source schedule is uo,u.o.c[o/s]. Let fb=λs:A.trΓ,s:A(b), gc=λs:A.trΓ,s:A(c), and R(f)=rDA(f). The letrec/beta, unfold, open, update projection, and application beta roots give trΓ(u)R(gc)=trΓ(o). Target congruence therefore gives trΓ(u.)trΓ(o.). The next letrec/beta unfolding, unfold, open, selection and self projections, and application beta roots give gc(R(gc))trΓ,s:A(c)[trΓ(o)/s]=αtrΓ(c[o/s]) by translation substitution. This is the translation of each source reduct, not the incorrect term c[u/s].

exercise 15.13.

For the ordinary object use the chapter’s o0:UPoint, origin=foldPointo0, and next=(unfoldorigin).move. The object premise types x,get at Nat; its move body overrides x in UPoint and folds the result, so it has Point. Hence origin and next both have Point.

Put UColorPoint=[x:Nat,get:Nat,move:ColorPoint,color:Nat],N(s)=foldColorPoint(s.xς(t:UColorPoint)succ(s.x)),c0=[x=ς(s:UColorPoint)0,get=ς(s:UColorPoint)s.x,move=ς(s:UColorPoint)N(s),color=ς(s:UColorPoint)7]. Invocation gives s.x:Nat. Override types the payload of N at UColorPoint, and fold gives ColorPoint. The other bodies have their displayed ground types. Thus corigin=foldColorPointc0,cnext=(unfoldcorigin).move:ColorPoint. After one type unfold, width comparison reaches the shared move fields ColorPoint and Point. Invariance rejects them, although each calculus derivation is independently safe.

exercise 15.14.

Write i:=[=ς(t)[]],a:=[=ς(s)i]. The empty literal has []:[]. Choosing A=[:[]] as the self type for the inner one-method literal gives the explicit formation t:A[]:[]i:ATObject. Width gives A<:[], so weakening and the explicit subsumption s:Ai:AA<:[]s:Ai:[]TSub let outer object formation conclude a:A. For the second typing, weaken the same inner formation to s:Ai:A, where A=[:A], and use it directly as the outer body premise; outer formation concludes a:A.

If some C were below both A and A, width-invariant subtyping would require the -component of C to equal both [] and A. These distinct types cannot both be that component. Erasing the self annotation therefore removes the syntax that fixed the conclusions of object formation and override, precisely the two annotated cases used by the minimum-type uniqueness proof.

exercise 15.15.

Here is the incorrect target fragment explicitly. Put R:=μY.{xsel:YNat,getsel:YNat,self:Y}, and define fx=λs:R.0,g=λs:R.1,fget=λs:R.(unfolds).xsel((unfolds).self). Let the deliberately bad recursive declaration and its old receiver be Dmake(u:Unit):R=foldR{xsel=fx,getsel=fget,self=make(unit)},r0:=rD(unit). The letrec and beta roots expose the old record: r0foldR{xsel=fx,getsel=fget,self=r0}. Functional record update of only the selection field is the target term r1:=foldR{xsel=g,getsel=(unfoldr0).getsel,self=(unfoldr0).self}. Thus the frozen translation of p1.get has the complete reduction (unfoldr1).getsel((unfoldr1).self)(unfoldr0).getsel((unfoldr0).self)fgetr0(unfoldr0).xsel((unfoldr0).self)fxr00. Beside it, the source uses the newly constructed receiver p1=[x=ς(s:P)1,get=ς(s:P)s.x]: p1.getp1.getp1.x1. The defective term is (unfoldr0).self=r0, retained inside r1. Recursive creation instead rebuilds the suite as rDP(g,fget) and ties its self field to that same updated application.

exercise 15.16.

Induct on b, freshening binders. In an object body both substitutions pass under each fresh self binder, and the induction hypothesis proves equality; in a fold payload they commute homomorphically and the type annotation obeys ordinary type-substitution composition. The variable cases are immediate, including b=s. No freshness condition on X in a is needed because the left side explicitly uses a[C/X]. Type terms contain no term variables, so C cannot contain s.

Search the book

Type to search the local edition.