I. the binary method problem A. what it is ------------------------------------------ BINARY METHODS def: a *binary method* is a method e.g., +, =, <, subset: ------------------------------------------ B. the problems ------------------------------------------ EXAMPLE class: Point superclass: Object instance variables: xValue yValue "instance methods" x: aNumber xValue := aNumber y: aNumber yValue := aNumber x ^xValue y ^yValue equal: p ^(xValue = p x) and: [yValue = p y] ------------------------------------------ Anything problematic about the equal method? ------------------------------------------ A USE OF THE POINT CLASS class: Procedure superclass: Object instance variables: "instance methods" breakit: p. | nuPt | nuPt := Point new x: 3.2. nuPt y: 4.5. ^p equal: nuPt ------------------------------------------ ------------------------------------------ A SUBCLASS class: ColorPoint superclass: Point instance variables: cValue "instance methods" c: aString cValue := aString c ^cValue equal: p ^(cValue = p c) and: [super equal: p] ------------------------------------------ does that seem reasonable? 1. breaks subtyping ------------------------------------------ COLORPOINT IS NOT A SUBTYPE OF POINT | nuCPt | nuCPt := ColorPoint new. ((nuCpt x: 8.7) y: 9.1) c: 'red'. (Procedure new) breakit: nuCPt ------------------------------------------ what happens? How could this be patched? 2. need for privileged access ------------------------------------------ NEED FOR PRIVILEGED ACCESS class: IntSet superclass: Object instance variables: elts "instance methods" new elts := OrderedCollection new add: anInt elts addFirst: anInt includes: anInt elts includes: anInt superSetOf: anIntSet "???" ------------------------------------------ C. summary Is there just one "binary method" problem? II. multi-methods and Cecil-like languages A. 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] ------------------------------------------ What about myColorPoint1 equal: myColorPoint2? What happens if we send myColorPoint equal: myPoint? What about myPoint equal: myColorPoint? Can we eliminate the repeated code in Point's equalsColorPoint method? How many methods does this technique generate, for n arguments? B. 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 ------------------------------------------ ------------------------------------------ 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: ------------------------------------------ C. tuples ------------------------------------------ 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: ------------------------------------------ ------------------------------------------ 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] ------------------------------------------ ------------------------------------------ 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). ------------------------------------------ Why not allow one element tuples? ------------------------------------------ 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 ==> ------------------------------------------ D. current research ------------------------------------------ SEPARATE DEVELOPMENT AND MERGE Point / \ ClrPnt ThreeDPnt Want to: ------------------------------------------ ------------------------------------------ WHAT COULD GO WRONG? Coverage for equal 1st \ 2nd | Point | ClrPnt | ThreeDPnt ========================================= Point | | | ClrPnt | | | ThreeDPnt | | | ------------------------------------------ Does this problem occur in single dispatch languages? Why? What could we do about it?