Semantic Casts: Contracts and Structural Subtyping in a Nominal World Robby Findler University of Chicago Matthew Flatt University of Utah Matthias Felleisen Northeastern University 1
Component marketplace - McIlroy's vision (1969) - Independent developers produce pieces of programs (components) - 3rd parties compose the components - Economic benefits: division of labor and competition - Software construction: merely plug & play 2
3
4
5
6
7
8
9
10
What is a contract? - Agreement between two components - Only allows certain patterns of interactions 11
Why check contracts? - Find faulty components - Accountability supports component economy 12
Contracts [Beugnard et al. 1999] - Syntactic: types int f(int[] x, int k) - Semantic level 1: behavioral int f(int[] x, int k) // 0 <= k < x.length() - Semantic level 2: sequencing, concurrency finalize is called for all objects - Quality of service: space, time web server handles at least 1000 GET/sec 13
Behavioral contract desiderata - Simplicity (dynamic enforcement) - Precise enforcement (no false pos/neg) - Blame 14
Behavioral contract history - Parnas: 1972 - Luckham: ANNA for Ada - Meyer: Eiffel - ... 15
Queues: an example 16
Queue class Q implements IQueue { void enq(int X) {...} int deq() {...} boolean empty() {...} } 17
Queue class Q implements IQueue { void enq(int X) {...} // @post !this.empty() int deq() {...} // @pre !this.empty() boolean empty() {...} } 18
Queue class Q implements IQueue { void enq(int X) {...} // @post !this.empty() int deq() {...} // @pre !this.empty() boolean empty() {...} } Good client IQueue q = new Q(); q.enq(1); q.deq(); 19
Queue class Q implements IQueue { void enq(int X) {...} // @post !this.empty() int deq() {...} // @pre !this.empty() boolean empty() {...} } Good client Bad client IQueue q = new Q(); IQueue q = new Q(); q.enq(1); q.deq(); q.deq(); q.enq(1); 20
Queue class Q implements IQueue { void enq(int X) {...} // @post !this.empty() int deq() {...} // @pre !this.empty() boolean empty() {...} } Good client Bad client IQueue q = new Q(); IQueue q = new Q(); q.enq(1); q.deq(); q.deq(); q.enq(1); Blame q.deq(); in Bad Client 21
Queue with observer class Q implements IQueue { Obs o; void enq(int X) {...} // @post !this.empty() // effect: o.onEnq(this) int deq() {...} // @pre !this.empty() // effect: o.onDeq(this) void register(Obs _o) {o = _o;} // please: a "good" Observer } 22
Good observer class GoodO implements Obs { void init() {...} void onEnq(IQueue q) {...} // @post !q.empty() void onDeq(IQueue q) {...} } 23
Good observer Bad Observer class GoodO class BadO implements Obs { implements Obs { void init() {...} void init() {...} void onEnq(IQueue q) void onEnq(IQueue q) {...} { q.deq() } // @post !q.empty() void onDeq(IQueue q) void onDeq(IQueue q) {...} {...} } } 24
Client Queue BadO 25
Client links BadO and Queue Client Queue BadO 26
Queue post-condition failure Client Queue BadO 27
Who to blame? Client Queue BadO 28
Who to blame? Client Queue BadO - Client combines mis-matched components - BadO violates informal contract - Queue is blamed 29
Queue with observer class Q implements IQueue { Obs o; void enq(int X) {...} // @post !this.empty() // effect: o.onEnq(this) int deq() {...} // @pre !this.empty() // effect: o.onDeq(this) void register(Obs _o) {o = _o;} // please: a "good" Observer } 30
Queue with observer class Q implements IQueue { Obs o; void enq(int X) {...} // @post !this.empty() // effect: o.onEnq(this) int deq() {...} // @pre !this.empty() // effect: o.onDeq(this) void register(Obs _o) {o = _o;} @pre _o.onEnq(...) // } 31
Contracts in interfaces? 32
Observer contracts interface Obs { void init(); void onEnq(IQueue q); // @post !q.empty() void onDeq(IQueue q); // @pre !q.empty() } Force observers to meet pre- and post-conditions that Queue needs 33
Controlling BadO class BadO implements Obs { void init() {...} void onEnq(IQueue q) { q.deq() } void onDeq(IQueue q) {...} } 34
Controlling BadO class BadO implements Obs { void init() {...} void onEnq(IQueue q) { q.deq() } // @post !q.empty() void onDeq(IQueue q) {...} } 35
A A' 36
A A' 37
A A' A - A' 38
A A' A - A' 39
Queue Class class Q implements IQueue { ... } Positive Queue interface IPosQ { void enq(int X) {...} // @pre X >= 0 // @post !this.empty() int deq() {...} // @pre !this.empty() // @post @ret >= 0 } 40
Queue Class class Q implements IQueue { ... } Positive Queue interface IPosQ { void enq(int X) {...} // @pre X >= 0 // @post !this.empty() int deq() {...} // @pre !this.empty() // @post @ret >= 0 } 41
Nominal subtyping Structural subtyping - Hierarchy explicit - Hierarchy implicit - Conventional OO PLs: - Research OO PLs: C++, C#, Eiffel, Java Moby, OML, OCaml, LOOM, PolyTOIL 42
Nominal subtyping Structural subtyping - Simple to implement - Harder to implement - Simple type-error messages - Complex type-error messages - Inhibits re-use - Permits flexible re-use 43
QClass class Q implements IQueue { ... } IQueue IPosQ interface IQueue { interface IPosQ { void enq(int X) {...} void enq(int X) {...} // @post !this.empty() // @pre X >= 0 // @post !this.empty() int deq() {...} int deq() {...} // @pre !this.empty() // @pre !this.empty() // @post @ret >= 0 } } 44
Structural Subtyping in an Nominal World 45
semanticCast (obj, I, <fromStr>, <toStr>) A structural subtype "cast" 46
semanticCast (obj, I, <fromStr>, <toStr>) The object that gets casted, now has additional contracts 47
semanticCast (obj, I, <fromStr>, <toStr>) The interface that describes the additional contracts 48
semanticCast (obj, I, <fromStr>, <toStr>) The name of the component where the object is from; Responsible for post-conds 49
semanticCast (obj, I, <fromStr>, <toStr>) The name of the component where the object is sent; Responsible for pre-conds 50
semanticCast (obj, I, <fromStr>, <toStr>) Result ensures I's contracts but otherwise identical to obj Has type I, even if obj doesn't 51
A A' A - A' 52
A A' semanticCast (o,A, , ) A - A' 53
A A' semanticCast semanticCast (o,A, , ) (o,A', , ) A - A' 54
<Queue> <Client> IQueue q = new Q(); q.enq(1); q.deq(); q.enq(-1); <PosQueue> q 55
<Queue> <Client> IQueue q = new Q(); q.enq(1); q.deq(); q.enq(-1); <PosQueue> q 56
<Queue> <Client> IQueue q = new Q(); q.enq(1); q.deq(); q.enq(-1); semanticCast (q, semanticCast (q, IQueue, IPosQ, <Queue>, <PosQueue>, <PosQueue>) <Client>) <PosQueue> q 57
<Queue> <Client> IQueue q = new Q(); q.enq(1); q.deq(); q.enq(-1); semanticCast (q, semanticCast (q, IQueue, IPosQ, <Queue>, <PosQueue>, <PosQueue>) <Client>) <PosQueue> q 58
<Queue> <Client> IQueue q = new Q(); q.enq(1); q.deq(); q.enq(-1); semanticCast (q, semanticCast (q, IQueue, IPosQ, <Queue>, <PosQueue>, <PosQueue>) <Client>) <PosQueue> q 59
<Queue> <Client> IQueue q = new Q(); q.enq(1); q.deq(); q.enq(-1); semanticCast (q, semanticCast (q, IQueue, IPosQ, <Queue>, <PosQueue>, <PosQueue>) <Client>) <PosQueue> q 60
Semantics of semanticCast 61
Semantics of semanticCast 62
Semantics of semanticCast 63
Semantics of semanticCast 64
Semantics of semanticCast 65
Semantics of semanticCast 66
Semantics of semanticCast 67
Semantics of semanticCast 68
semanticCast (o, I, <from> , <to> ).m(o') = semanticCast (o.m( semanticCast o', J, <to> , <from> ), K, <from> , <to> ) interface I { K m(J x); } interface J { ... } interface K { ... } 69
Implementation - Proxies - Construct new proxies at method calls 70
Wrap up 71
Structural subtyping for contracts - Adds flexibility to conventional languages - Contracts still simple boolean expressions - Proper blame assignment - If a tree in the forest hasn't yet fallen, it didn't make a sound 72
Thank you. 73
Recommend
More recommend