Efficient Multimethods in a Single Dispatch Language
Brian Foote | |
Ralph Johnson | |
Dept. of Computer Science | |
University of Illinois at Urbana-Champaign | |
201 N. Goodwin, Urbana, IL 61801, USA | |
foote@cs.uiuc.edu johnson@cs.uiuc.edu | |
James Noble | |
School of Mathematical and Computing Sciences | |
Victoria University of Wellington | |
P.O. Box 600, Wellington, New Zealand | |
kjx@mcs.vuw.ac.nz |
Motivation for Multimethods | |
Syntax Matters | |
A Tale of Two MOPs | |
Two Implementations | |
Enduring Significance |
One day an Englishman, a Scotsman, and an Irishman walked into a pub together. They each called for a dram of whiskey. As they were about to enjoy their libations, three flies landed, one in each of their drinks, and became stuck inside the glasses. The Englishman pushed his shot away in disgust. The Scotsman fished the fly out of his glass, and continued drinking it, as if nothing had happened. The Irishman, too, picked the fly out of his drink, held it out over the glass, and started yelling, "SPIT IT OUT, SPIT IT OUT YOU BASTARD!!!!" |
public class Scotsman extends Carouser | |
{ | |
public void imbibe(ScotchWhiskey libation) | |
{ | |
stomach.add(libation); | |
bloodAlchoholLevel += 0.01; | |
System.out.println("A wee dram..."); | |
} | |
public void imbibe(IrishWhiskey libation) | |
{ | |
stomach.add(libation); | |
bloodAlchoholLevel += 0.00; | |
System.out.println("Belfast Bog water..."); | |
} | |
public class Irishman extends Carouser | |
{ | |
public void imbibe (ScotchWhiskey libation) | |
{ | |
emptyStomach(); | |
System.out. | |
println("Caledonian Swill..."); | |
} | |
public void imbibe (IrishWhiskey libation) | |
{ | |
stomach.add(libation); | |
bloodAlchoholLevel += 0.01; | |
System.out.println("Sure and begora..."); | |
} | |
public void testOverloads() | |
{ | |
Irishman paddy = new Irishman(); | |
Scotsman angus = new Scotsman(); | |
System.out. | |
println("--> testOverloads()..."); | |
paddy.imbibe(new IrishWhiskey()); | |
paddy.imbibe(new ScotchWhiskey()); | |
angus.imbibe(new IrishWhiskey()); | |
angus.imbibe(new ScotchWhiskey()); | |
} | |
A Simple, Unsuccessful Variation
public void testBreakOverloads() | |
{ | |
Irishman paddy = new Irishman(); | |
Scotsman angus = new Scotsman(); | |
Carouser carouser = paddy; | |
System.out.println("--> testBreakOverloads()..."); | |
Dram dram = new IrishWhiskey(); | |
// You can't really do this properly... | |
carouser.imbibe(dram); | |
carouser = angus; | |
carouser.imbibe(new IrishWhiskey()); | |
} |
public void imbibe(Dram libation) | |
{ | |
System.out. | |
println("Saints preserve us..."); | |
} | |
public void imbibe(Dram libation) | |
{ | |
if (libation instanceof ScotchWhiskey) | |
{ | |
emptyStomach(); | |
System.out.println("Caledonian Swill..."); | |
} | |
else if (libation instanceof IrishWhiskey) | |
{ | |
stomach.add(libation); | |
bloodAlchoholLevel += 0.01; | |
System.out.println("Sure and begora..."); | |
} | |
else | |
System.out.println("Mother of God..."); | |
} | |
Kent Beck: May be Best Remembered as the Man Who brought Scatology and Software Engineering together… | |
If it stinks, change it! | |
--Grandma Beck | |
Code Smells are (not so) subtle indications a piece of code is in need of attention… …and is a likely candidate for refactoring… |
public class Irishman extends Carouser | |
{ | |
public void imbibe(Dram libation) | |
{ | |
libation.whenImbibedByIrishman(this); | |
} |
public class Scotsman extends Carouser | |
{ | |
public void imbibe(<ScotchWhiskey> libation) | |
{ | |
stomach.add(libation); | |
bloodAlchoholLevel += 0.01; | |
System.out.println("A wee dram..."); | |
} | |
public void imbibe(<IrishWhiskey> libation) | |
{ | |
stomach.add(libation); | |
bloodAlchoholLevel += 0.00; | |
System.out.println("Belfast Bog water..."); | |
} | |
CLOS: | |
(defmethod speak ((who
animal)) (format t "I'm an animal: ~A~%" who)) |
ScreenDisplay>>draw:
aGraphicalObject <Line> ”draw a line on a screen” |
|
ScreenDisplay>>draw:
aGraphicalObject <Arc> ”draw an arc on a screen” |
ParseNode>>acceptVistor: aVisitor | |
^self subclassResponsibility | |
VariableNode>>acceptVistor: aVisitor | |
^aVisitor visitWithVariableNode: self | |
ConstantNode>>acceptVistor: aVisitor | |
^aVisitor visitWithConstantNode: self | |
OptimizingVisitor>>visitWithConstantNode: aNode | |
^aNode value optimized | |
OptimizingVisitor>>visitWithVariableNode: aNode | |
^aNode lookupIn: self symbolTable |
OptimizingVisitor>>visitWithNode:
aNode <ConstantNode> ^self value optimized |
|
OptimizingVisitor>> visitWithNode: aNode <VariableNode> ^aNode lookupIn: self symbolTable |
Object | |
Behavior | |
ClassDescription | |
Class | |
Metaclass | |
Method | |
MethodDictionary | |
CompiledMethod | |
ByteArray | |
Context | |
MethodContext | |
BlockContext | |
Message | |
Process | |
ProcessScheduler | |
Semaphore | |
SharedQueue | |
Compiler | |
SystemDictionary | |
MultiMethod | |
Specializer | |
ClassSpecializer | |
EqualSpeciealizer | |
GenericMessage | |
MethodCombination | |
DiscriminatingMethod | |
Qualifiers (#Before #After, etc.) | |
SubStandardMethodCombination | |
SimpleMethodCombination | |
BetaMethodCombination | |
DispatchingMethodCombination | |
Generated Redispatching Methods
|D| = |S1| + |S2| × |S1| + |S3| × |S2| × |S1| … + |Sn| × ... × |S2| × |S1| × 2 |
The Beauty of Smalltalk | |
The Elegance of the CLOS MOP | |
Building Languages of Objects | |
The Power of Multimethods |
End to End Argument | |
Impact of Dynamic Types and Languages | |
The Arrogance of Closed Worlds | |
Reflection as a School of Architecture |
Ralph Johnson | |
James Noble | |
John Brant | |
Don Roberts | |
Richard P. Gabriel | |
Andrew Black | |
Stéphane Ducasse | |
Christophe Dony | |
Anonymous ECOOP Reviewers | |
UIUC Software Architecture Group |