CS 541 Lecture -*- Outline -*- * multi-methods and Cecil-like languages ** Ingall's double dispatch technique ------------------------------------------ DOUBLE DISPATCH class: Point superclass: Object instance variables: xValue yValue "instance methods" equal: p ^(p equalAsPoint: self) equalsPoint: p ^(xValue = p x) and: [yValue = p y] equalsColorPoint: cp ^(xValue = p x) and: [yValue = p y] class: ColorPoint superclass: Point instance variables: cValue "instance methods" equal: p ^(p equalsColorPoint: self) equalsColorPoint: cp ^(self equalsPoint: cp) and: [cValue = cp c] ------------------------------------------ Q: What about myColorPoint1 equal: myColorPoint2? Q: What happens if we send myColorPoint equal: myPoint? Q: What about myPoint equal: myColorPoint? Q: Can we eliminate the repeated code in Point's equalsColorPoint method? yes, but it seems to make the example even harder to follow. Q: How many methods does this technique generate, for n arguments? exponential in n The problem is the intermediate points in the "search" for the "right" method have to be coded by the programmer, and the search itself, has to be coded. Better to directly express the idea of what method to call for various dynamic combinations of arguments... ** the problem that leads to multiple dispatch ------------------------------------------ THE PROBLEM Want to: - allow programmer to directly express dynamic dispatch on more than one argument - have language handle search for best method ------------------------------------------ Telling the language about it gives additional opportunities for static checking optimization ------------------------------------------ MULTIPLE DISPATCH def: a language has *multiple dispatch* if the method selected can be based on Languages with multiple dispatch: def: a language has *single dispatch* if it has message passing, but only selects methods based on Languages with single dispatch: ------------------------------------------ ... the run-time class of more than one argument ... CLOS, Dylan, Cecil ... the run-time class of the "receiver" ... Smalltalk, C++, Java, Eiffel Binary methods aren't the only reason to want multiple dispatch, visitor pattern also needs it ** tuples See the paper "Multiple Dispatch as Dispatch on Tuples" by Leavens and Millstein in OOPSLA '98 ------------------------------------------ TUPLES + MESSAGES TO TUPLES = MULTIPLE DISPATCH Problem: How to add multimethods to a single dispatch language? Want to: - preserve meaning of existing programs - leave static properties of existing code unchanged - typing - encapsulation (info hiding) Idea: ------------------------------------------ ... - add tuples (e1, e2, e3) - allow methods to be declared for tuples - dispatch to method based on run-time class of all arguments - select the most specific applicable (best) method to handle call, give error if no best method exists ------------------------------------------ EXAMPLE class: Point superclass: Object "like Point before, but no equal method" class: ColorPoint superclass: Point "also no equal method" tuple class: (p1: Point, p2: Point) "instance methods" equal ^ (p1 x = p2 x) and: [p1 y = p2 y] tuple class: (cp1: ColorPoint, cp2: ColorPoint) "instance methods" equal ^ ((cp1 x = cp2 x) and: [cp1 y = cp2 y]) and: [cp1 c = cp2 c] ------------------------------------------ We don't allow privilaged access to objects in a tuple ------------------------------------------ SYNTAX and SEMANTICS Tuple expressions: ::= () | (1, ..., n), for n >= 2 Message sends (Smalltalk-like syntax) ::= ... | Multiple Dispatch: (E_1, ..., E_n) mname: args Let o_i be value of E_i so (o_1, ..., o_n) is receiving tuple Let cd_i be minimal dynamic class of o_i 1. Find set, A, of applicable methods in all tuple classes (C_1, ..., C_n) with - method named "mname:" - (hence right number of other args) - cd_i is subclass of C_i 2. if A is empty, "no such method" error 3. Let best(A) be unique class in A - if no such "method ambiguous" error 4. Call the method in best(A). ------------------------------------------ Q: Why not allow one element tuples? ... such that best(A) classes inherit (pointwise) from each other class in A ------------------------------------------ MULTIPLE DISPATCH vs. STATIC OVERLOADING Example declarations: | p1:Point p2:Point | p1 := ColorPoint new. p1 x: 1. p1 y: 2. p1 c: red. p2 := ColorPoint new. p2 x: 1. p2 y: 2. p2 c: blue. multiple dispatch: (p1, p2) equal ==> static overloading: (p1, p2) equal ==> ------------------------------------------ ... based on run-time class (dynamic overloading) ... based on static class or type ** modularity issues current research is how to solve the problem of ambiguity from independently developed code. ------------------------------------------ SEPARATE DEVELOPMENT AND MERGE Point / \ ClrPnt ThreeDPnt Want to: ------------------------------------------ ... - allow independent development using Point as base - use ClrPnt and ThreeDPnt in the same program - type check them individually but guarantee type safety ------------------------------------------ WHAT COULD GO WRONG? Coverage for equal 1st \ 2nd | Point | ClrPnt | ThreeDPnt ========================================= Point | | | ClrPnt | | | ThreeDPnt | | | ------------------------------------------ suppose that Point x Point is one method, and ClrPnt x ClrPnt, and ThreeDPnt x ThreeDPnt are others. Any problem? (no) now suppose we wrote 3 methods for ClrPnt and Point, and 3 for ThreeDPnt and Point. Any problem? (yes) Q: Does this problem occur in single dispatch languages? Why? Q: What could we do about it? See Millstein and Chambers's paper in ECOOP '99.