lec23_6up.pdf
Document Details
Uploaded by SharpScandium
2011
Tags
Full Transcript
So far... The difference between OOP and “records of functions with shared...
So far... The difference between OOP and “records of functions with shared private state” is dynamic-dispatch (a.k.a. late-binding) of self CS152: Programming Languages (Informally) defined method-lookup to implement Lecture 23 — Advanced Concepts in dynamic-dispatch correctly: use run-time tags or code-pointers Object-Oriented Programming Now: Subclassing vs. subtyping Then fancy stuff: multiple-inheritance, interfaces, static overloading, multiple dispatch Dan Grossman Spring 2011 Next lecture: Bounded polymorphism and classless OOP Dan Grossman CS152 Spring 2011, Lecture 23 2 Type-Safety in OOP Revisiting Subclassing is Subtyping Should be clearer about what type-safety means... Recall we have been “confusing” classes and types: C is a class and a type and if C extends D then C is a subtype of D “Not getting stuck” has meant “don’t apply numbers”, “don’t add functions”, “don’t read non-existent record fields”, etc. Therefore, if C overrides m, the type of m in C must be a subtype of the type of m in D Pure OO has only method calls (and maybe field access) Stuck if method-lookup fails (no method matches) Just like functions, method-subtyping is contravariant arguments Stuck if method-lookup is ambiguous (no best match) and covariant results If code knows it has a C, it can call methods with “more” So far only failure is receiver has no method with right name/arity arguments and know there are “fewer” results Dan Grossman CS152 Spring 2011, Lecture 23 3 Dan Grossman CS152 Spring 2011, Lecture 23 4 Subtyping and Dynamic Dispatch More subtyping We defined dynamic dispatch in terms of functions taking self as With single-inheritance and the class/type confusion, we don’t get an argument all the subtyping we want Example: Taking any object that has an m method from int But unlike other arguments, self is covariant!! to int Else overriding method couldn’t access new fields/methods Interfaces help somewhat, but class declarations must still say they implement an interface Sound because self must be passed, not another value with the supertype Object-types bring the flexibility of structural subtyping to OOP This is the key reason encoding OO in a typed λ-calculus requires With object-types, “subclassing implies subtyping” ingenuity, fancy types, and/or run-time cost We won’t attempt it Dan Grossman CS152 Spring 2011, Lecture 23 5 Dan Grossman CS152 Spring 2011, Lecture 23 6 More subclassing Subclass not a subtype Breaking one direction of “subclassing = subtyping” allowed more class P1 { subtyping (so more code reuse) Int x; Int get_x() { x } Breaking the other direction (“subclassing does not imply Bool compare(P1 p) { self.get_x() == p.get_x() } subtyping”) allows more inheritance (so more code reuse) } class P2 extends P1 { Simple idea: If C extends D and overrides a method in a way that Int y; makes C ≤ D unsound, then C ≤ D. This is useful: Int get_y() { y } class P1 {... Int get_x(); Bool compare(P1);... } Bool compare(P2 p) { self.get_x() == p.get_x() && class P2 extends P1 {... Bool compare(P2);... } self.get_y() == p.get_y() } } But this is not always correct... As expected, P2≤P1 is unsound (assuming compare in P2 is overriding unlike in Java or C++) Dan Grossman CS152 Spring 2011, Lecture 23 7 Dan Grossman CS152 Spring 2011, Lecture 23 8 Subclass not a subtype Where we are Can still inherit implementation (need not reimplement Summary of last few slides: Separating types and classes expands get_x) the language, but clarifies the concepts: We cannot always do this: what if get_x called Typing is about interfaces, subtyping about broader interfaces self.compare? Possible solutions: Re-typecheck get_x in subclass Inheritance (a.k.a. subclassing) is about code-sharing Use a “Really Fancy Type System” Combining typing and inheritance restricts both I see little use in allowing subclassing that is not subtyping Most OO languages purposely confuse subtyping (about type-checking) and inheritance (about code-sharing), which is But I see much use in understanding that typing is about interfaces reasonble in practice and inheritance is about code-sharing Dan Grossman CS152 Spring 2011, Lecture 23 9 Dan Grossman CS152 Spring 2011, Lecture 23 10 Multiple Inheritance Diamond Issues Why not allow class C extends C1,C2,...{...} If C extends C1 and C2 and C1,C2 have a common superclass (and C≤C1 and C≤C2)? D (perhaps transitively), our class hierarchy has a diamond If D has a field f , should C have one field f or two? What everyone agrees: C++ has it and Java doesn’t If D has a method m, C1 and C2 will have a clash If subsumption is coercive (changing method-lookup), how we All we’ll do: Understand some basic problems it introduces and how interfaces get most of the benefits and some of the problems subsume from C to D affects run-time behavior (incoherent) Problem sources: Diamonds are common, largely because of types like Object with Class hierarchy is a dag, not a tree (not true with interfaces) methods like equals Subtype hierarchy is a dag, not a tree (true with interfaces) Dan Grossman CS152 Spring 2011, Lecture 23 11 Dan Grossman CS152 Spring 2011, Lecture 23 12 Multiple Inheritance, Method-Name Clash Implementation Issues If C extends C1 and C2, which both define a method m, what This isn’t an implementation course, but many semantic issues does C mean? regarding multiple inheritance have been heavily influenced by clever implementations Possibilities: In particular, accessing members of self via compile-time 1. Reject declaration of C (Too restrictive with diamonds) offsets... 2. Require C to override m (Possibly with directed resends) ... which won’t work with multiple inheritance unless upcasts 3. “Left-side” (C1) wins (Must decide if upcast to “right-side” “adjust” the self pointer (C2) coerces to use C2’s m or not) That’s one reason C++ has different kinds of casts 4. C gets both methods (Now upcasts definitely coercive and with diamonds we lose coherence) Better to think semantically first (how should subsumption affect the behavior of method-lookup) and implementation-wise second 5. Other? (I’m just brainstorming based on sound principles) (what can I optimize based on the class/type hierarchy) Dan Grossman CS152 Spring 2011, Lecture 23 13 Dan Grossman CS152 Spring 2011, Lecture 23 14 Digression: Casts Least Supertypes A “cast” can mean many things (cf. C++). Consider if e1 then e2 else e3 (or in C++/Java, e1 ? e2 : e3 ) At the language level: We know e2 and e3 must have the same type upcast: no run-time effect until we get to static overloading downcast: run-time failure or no-effect With subtyping, they just need a common supertype conversion: key question is round-tripping Should pick the least (most-specific) type “reinterpret bits”: not well-defined Single inheritance: the closest common ancestor in the class-hierarchy tree At the implementation level: Multiple inheritance: there may be no least common supertype upcast: usually no run-time effect but see last slide downcast: usually only run-time effect is failure, but... Example: C1 extends D1, D2 and C2 extends D1, D2 conversion: same as at language level Solutions: Reject (i.e., programmer must insert explicit casts to “reinterpret bits”: no effect (by definition) pick a common supertype) Dan Grossman CS152 Spring 2011, Lecture 23 15 Dan Grossman CS152 Spring 2011, Lecture 23 16 Multiple Inheritance Summary Interfaces An interface is just a (named) (object) type. Example: Method clashes (what does inheriting m mean) interface I { Int get_x(); Bool compare(I); } Diamond issues (coherence issues, shared (?) fields) A class can implement an interface. Example: Implementation issues (slower method-lookup) Least supertypes (may be ambiguous) class C implements I { Int x; Int get_x() {x} Complicated constructs lead to difficult language design Bool compare(I i) {...} // note argument type Doesn’t necessarily mean they are bad ideas } Now discuss interfaces and see how (and how not) multiple If C implements I, then C ≤ I interfaces are simpler than multiple inheritance... Requiring explicit “implements” hinders extensibility, but simplifies type-checking (a little) Basically, C implements I if C could extend a class with all abstract methods from I Dan Grossman CS152 Spring 2011, Lecture 23 17 Dan Grossman CS152 Spring 2011, Lecture 23 18 Interfaces, continued Using Interfaces Subinterfaces (interface J extends I {...}) work exactly as subtyping suggests they should Although it requires more keystrokes and makes efficient implementation harder, it may make sense (be more extensible) to: An unnecessary addition to a language with abstract classes and multiple inheritance, but what about single inheritance and Use interface types for all fields and variables multiple interfaces: Don’t use constructors directly: For class C implementing I, class C extends D implements I1,I2,...,In write: I makeI(...) { new C(...) } Method clashes (no problem, inherit from D) Diamond issues (no problem, no implementation diamond) This is related to “factory patterns”; constructors are behind a level of indirection Implementation issues (still a “problem”, different object of type I will have different layouts) It is using named object-types instead of class-based types Least supertypes (still a problem, this is a typing issue) Dan Grossman CS152 Spring 2011, Lecture 23 19 Dan Grossman CS152 Spring 2011, Lecture 23 20 Static Overloading Static Overloading Continued So far, we have assumed every method had a different name Because of subtyping, multiple methods can match! Same name implied overriding and required a subtype “Best-match” can be roughly “Subsume fewest arguments. For a tie, allow subsumption to immediate supertypes and recur” Many OO languages allow the same name for methods with different argument types: Ambiguities remain (no best match): A f(B x) {... } A f(B) vs. C f(B) (usually rejected) C f(D x, E y) {... } A f(I) vs. A f(J) for f(e) where e has type T , T ≤ I, F f(G x, H z) {... } T ≤ J and I,J are incomparable (We saw this before) Complicates definition of method-lookup for e1.m(e2,...,en) A f(B,C) vs. A f(C,B) for f(e1,e2) where B ≤ C, and e1 and e2 have type B Previously, we had dynamic-dispatch on e1: method-lookup a function of the class of the object e1 evaluates to (at run-time) Type systems often reject ambiguous calls or use ad hoc rules to give a best match (e.g., “left-argument precedence”) We now have static overloading: Method-lookup is also a function of the types of e2,...,en (at compile-time) Dan Grossman CS152 Spring 2011, Lecture 23 21 Dan Grossman CS152 Spring 2011, Lecture 23 22 Multiple Dispatch Example Static overloading saves keystrokes from shorter method-names We know the compile-time types of arguments at each class A { int f; } call-site, so we could call methods with different names class B extends A { int g; } Bool compare(A x, A y) { x.f == y.f } Bool compare(B x, B y) { x.f == y.f && x.g == y.g } Multiple (dynamic) dispatch (a.k.a. multimethods) is much more Bool f(A x, A y, A z) { compare(x,y) && compare(y,z) } interesting: Method-lookup a function of the run-time types of arguments Neat: late-binding for both arguments to compare (choose second method if both arguments are subtypes of B, else first method) It’s a natural generalization: the “receiver” argument is no longer treated differently! With power comes danger. Tricky question: Can we add “&& compare(x,z)” to body of f and have an equivalent function? So e1.m(e2,...,en) is just sugar for m(e1,e2,...,en) With static overloading? It wasn’t before, e.g., when e1 is self and may be a subtype With multiple dispatch? Dan Grossman CS152 Spring 2011, Lecture 23 23 Dan Grossman CS152 Spring 2011, Lecture 23 24 Pragmatics Revenge of Ambiguity Not clear where multimethods should be defined The “no best match” issues with static overloading exist with No longer “belong to a class” because receiver isn’t special multimethods and ambiguities arise at run-time It’s undecidable if “no best match” will happen: Multimethods are “more OO” because dynamic dispatch is the essence of OO // B