Featherweight Java Chapter 19 Benjamin Pierce Types and Programming Languages Eiffel, 1989 Cook, W.R. (1989) - A Proposal for Making Eiffel Type-Safe, in Proceedings of ECOOP'89. S. Cook (ed.), pp. 57-70. Cambridge University Press. Betrand Meyer, on unsoundness of Eiffel: “Eiffel users universally report that they almost never run into such problems in real software development.” Ten years later: Java Interesting Aspects of Java • Object Oriented – (Almost) everything is an object – Single inheritance • Adding fields • Method override • Open recursion – Interfaces – Encapsulation • • • • Reflection Concurrency Libraries Type safety – Well typed programs have no undefined semantics Featherweight Java • (Minimal) Purely functional object oriented strict subset of Java • Supports – Everything is an object – Single inheritance • Adding fields • Method override • Open recursion • Simple – Operational Semantics – Type Checking – Proof of safety • Extensions – Interface – Inner classes – Polymorphism Featherweight Java CL ::= class declarations class c extends C { C f ; K M } K ::= constructor declarations C (C f) { super(f) ; this.f=f ;} M ::= method declarations C m(C x) { return t;} t ::= terms x variable t.f field access t.m(t) method invocation new C(t) object creation (C) t cast v::= values new C(v) A Simple Example class Bicycle extends object { class MountainBike extends Bicycle { int currentSpeed ; // field int LowerGear; int currentGear ; // field MountainBike(int s, int g, int l) { Biycle(int s, int g) { // constructor super(s, g) ; super() ; this.LowerGear= l ; } this.currentSpeed= s ; Bicycle UpShift () this.currentGear= g ; } Bicycle UpShift () { …} } { class Main extends object { return new Bicycle(this.currentSpeed, Bicycle b; this.currentGeer+1) ; } Main() { super() ; this.b = new MountainBike(3, 3, 5); } } Bicycle UpShift() { return this.b.UpShift() ; } } Running example class A extends Object { A() { supper(); } } class B extends Object { B() { supper(); } } class Pair extends Object { Object first; Object second; Pair(Object fst, Object snd) { supper(); this first=fst; this second = snd; } Pair SetFst(Object newfst) { return new Pair(newfst, this.snd); } } Nominal vs. Structural Type Systems • When are two types equal: • Structural equivalence – Two isomorphic types are identical – NatPair = {fst: Nat, snd: Nat} • Nominal (name equivalence) type systems – Compound types have name – The name caries significant information – Type name must match Nominal vs. Structural Type Systems Nominal • Type name is useful at runtime – “Generic” programming – Efficient runtime checks • Naturally supports recursive types • Efficient subtyping checks • Prevent “spurious” subsumption Structural • Type expressions are closed entities • Supports type abstractions – – – – Parametric polymorphism Abstract data types User defined type operators … The Class Table • Maps class names to their class definitions (excluding objects) Running example class A extends Object { A() { supper(); } } A class A extends Object {…} class B extends Object { B() { supper(); } } B class B extends Object {…} class Pair extends Object { Pair Object first; Object second; Pair(Object fst, Object snd) { supper(); this first=fst; this second = snd; } Pair SetFst(Object newfst) { return new Pair(newfst, this.snd); } } class Pair extends Object {…} Featherweight Java with subtyping CL ::= class declarations Subtyping class c extends C { C f ; K M } K ::= constructor declarations C (C f) { super(f) ; this.f=f ;} M ::= method declarations C m(C x) { return t;} t ::= v::= terms x variable t.f field access t.m(t) method invocation new C(t) object creation (C) t cast values new C(v) C <: D C <: C C <: D D <:E C <: E CT(C) = class C extends D {… } C <: D The Class Table • Maps class names to their class definitions (excluding objects) • A program is a class table and a term • Consistency requirements – – – – <: is acyclic (<: is a partial order) CT(C)= class C … for every C in dom(CT) Object dom(CT) For every class C appearing in CT except Object, c dom(CT) • fields(C) = C f are the fields declared in C • mbody(m, C) = (x, t) where x are the formal arguments and t is m’s body Plan • • • • A small step operational semantics Potential runtime errors Basic type system Corrections New Evaluation ti t’i new C(v, ti, t) new C(v, t’i, t) (E-New-Arg) Field Projection fields(C) = C f (E-ProjNew) new C(v).fi vi new Pair(new A(), new Pair(new A(), new B())).snd new Pair (new A(), new B()) t0 t’0 (E-Field) t0.f t’0.f new Pair(new A(), new Pair(new A(), new B())).snd.fst new A() Method Invocation • Use the (actual) class to determine the exact method • Bind actual parameters to formals • Benefit from absence of side effects Method Invocation mbody(m, C) =(x, t0) new C(v).m(u) [x (E-InvNew) u, this new C(v)] t0 new Pair(new A(), new B()).setfst(new B()) [newfst new B(), this new Pair(new A(), new B())] new Pair(newfst, this.snd) =new Pair(new B(), new Pair (new A(), new B()).snd) new Pair(new B(), new B()) (E-ProjNew) Method Invocation mbody(m, C) =(x, t0) new C(v).m(u) [x (E-InvNew) u, this t0 t’0 new C(v)] t0 (E-InvkRecv) t0. m(t) t’0.m(t) ti t’i v0. m(v, ti, t) v0.m(v, t’i, t) (E-InvArg) Cast Invocation • Assure that the casting is valid • Convert the type Cast Invocation C <: D (E-CastNew) (D) (new C(v)) new C(v) t0 t’0 (E-Cast) (C) t0 (C) t’0 ((Pair) new Pair(new Pair (new A(), new B()), new A()).fst).snd (E-ProjNew) ((Pair) new Pair(new A(), new B())).snd (E-CastNew) new Pair(new A(), new B()).snd new B() (E-ProjNew) FJ Semantics Summary fields(C) = C f t0 t’0 (E-ProjNew) new C(v).fi vi t0.f t’0.f mbody(m, C) =(x, t0) new C(v).m(u) [x (E-Field) (E-InvNew) u, this new C(v)] t0 t0 t’0 (E-InvkRecv) t0. m(t) t’0.m(t) ti t’i (E-InvArg) v0. m(v, ti, t) v0.m(v, t’i, t) C <: D (D) (new C(v)) new C(v) (E-CastNew) ti t’i (E-New-Arg) new C(v, ti, t) new C(v, t’i, t) t0 t’0 (C) t0 (C) t’0 (E-Cast) Potential Runtime Errors • Incompatible constructor invocation – new Pair(new A()) • Incompatible field selection – new Pair(new A(), new B()).thrd • Incompatible method invocation “message not understood” – new A().setfst(new B()) • Incompatible arguments of methods • Incompatible return value of methods • Incompatible downcasts The Class Table • Maps class names to their class definitions (excluding objects) • A program is a class table and a term • fields(C) = C f are the fields declared in C • mbody(m, C) = (x, t) where x are the formal arguments and t is the method body • mtype(m, C) = B B where B is the type of arguments and B is the type of the results • override(m, D, C C0) holds if the method m with arguments C is redefined in a subclass of D Featherweight Java Type Rules t : C x : C x : C (T-VAR) t0 : C0 fields(C0)=C f t0 .fi : Ci C <: D C != D t0: D (T-DCAST) (C) t0: C (T-FIELD) Method Typing M OK in C x: C, this: C t0 : E0 <: C0 E0 CT(C)=class C extends D {…} t0 : C0 mtype(m, C0)= D C t:C C <: D (T-INVK) override(m, D, C C0) t0 .m(t): C C0 m(C x) { return t0 ; } OK in C fields(C)= D f Class Typing C OK C <: D t:C (T-NEW) new C (t): C K= C(D g, C f) {super(g); this.f = f;} fields(D)=D g M ok in C D <: C t0: D (T-UCAST) (C) t0: C class C extends D { C f K M } OK Type Safety(19.5) • Well typed programs cannot go wrong – No undefined semantics – No runtime checks • If t is well typed then either t is a value or there exists an evaluation step t t’ [Progress] • If t is well typed and there exists an evaluation step t t’ then t’ is also well typed [Preservation] Type Preservation: Take 1 • If t : C and t t’ then t’ : C • Counterexample (Object) new B() new B() (E-CastNew) Type Preservation: Take 2 • If t : C and t t’ then there exists C’ such that C’ <: C and t’ : C’ • Counterexample – (Object) new B() new B() (E-CastNew) – (A) (Object) new B() (A) new B() (E-Cast) t : C x : C x : C Featherweight Java Type Rules t0: D C : D stupid warning (T-VAR) t0 : C0 fields(C0)=C f t0 .fi : Ci D :C (C) t0: C (T-SCAST) (T-FIELD) Method Typing M OK in C t0 : C0 mtype(m, C0)= D C x: C, this: C t0 : E0 <: C0 t:C C <: D (T-INVK) CT(C)=class E0 C extends D {…} t0 .m(t) : C override(m, D, C C0) fields(C)= D f C0 m(C x) { return t0 ; } OK in C C <: D t:C (T-NEW) Class Typing new C (t): C C OK D <: C t0: D (C) t0: C (T-UCAST) C <: D C != D t0: D (T-DCAST) (C) t0: C K= C(D g, C f) {super(g); this.f = f;} fields(D)=D g M ok in C class C extends D { C f K M } OK Progress Theorem • If a program is well typed then the only way to get stuck is if it reaches a point in which it cannot perform a downcast • If t is a well typed term – If t = new C0(t).f then fields(C0) = C f and f f – If t = new C0(t).m(s) then mbody(m, C0) = (x, t0) and |x| = |s| • if t is a closed well typed in a normal form then either t is a value or “t is a cast” Evaluation Contexts • Terms with a hole E ::= evaluation contexts [] hole E.f field access E.m(t) method invocation (receiver) v.m(v, E, t) method invocation (arg) new C(v, E, t) object creation(arg) (C) E cast • E[t] denotes the term obtained by replacing the hole with t • If t t’ then t= E(r) and t’= E(r’) where E, r, and r’ are unique and r r’ is one of the rules E-ProjNew, E-InvNew and E-CastNew • If t is closed well defined normalized term then either t is a value or for some context E we can express t as t = E[(C)(new D(v)] where D :C (Progress theorem) Encoding vs. Primitive Objects • Two approaches for semantics and typing OO programs – Typed lambda calculus with records, references and subtypes (Chapter 18) – Object and classes are primitive mechanisms Summary • Establishing type safety of real programming language can be useful • Mechanized proof systems (Isabele, Coq) can help – Especially useful in the design phase • But indentifying a core subset is also useful Quotes • “Inside every large language there is a small language struggling to come out” – Igarashi, Pierce, and Wadler (1999) • “Inside every large program there is a small program struggling to come out” – Sir Tony Hoare, Efficient Production of large programs (1970) • “I’m fat but I’m thin inside” – George Orwell, Coming Up from Air (1939)