Permalink
Courtesy Implementation in Nice
17 AUG 2004
Martin Fowler is talking about "courtesy implementations", which of course is all very sensible. Working with a language with first-class multi-methods makes this even more painless. Here's Fowler's example in Nice:
interface Node {}
class Box implements Node {
List children = new ArrayList();
void `<<` (Node argNode) { children.add(argNode); }
}
class Elephant implements Node {}
int num_elephants(Node n);
num_elephants(Elephant e) = 1;
num_elephants(Box b) {
var result = 0;
for (node: b.children) {
result += node.num_elephants();
}
return result;
}
Things to notice about this code:
- Despite being code for a statically typed language, it's about the same size as Martin's Ruby code, maybe even a little shorter.
- If you create a new class which implements
Node, the compiler will complain if you don't also provide a new implementation ofnum_elephantsto go with it, just like an abstract method in Java. - Nice syntax accepts method definitions and overrides within class bodies, but they're still multi-methods, just as if they were defined outside the class. Whatever you're more comfortable with is fine.
- Because classes don't contain methods, "adding a method to a base class" is as trivial as defining any other kind of method. You want to define new methods on Object, or String? Go right ahead, nothing's stopping you.
- Everybody brings up the "but what if I have a list of arbitrary objects" question for statically typed languages, so I'll also mention this alternative definition of
num_elephants:int num_elephants(Object o) = 0; num_elephants(Elephant e) = // Same as original num_elephants(Box b) = //Same as original
This would let you find the number of elephants in anything. Things that you haven't specialized the method for simply get a default of 0. So,"foo".num_elephants()would return 0. Things that care, can give better results. Things that don't, don't break. - Nice has some great power tools from the world of functional programming. Here's a shorter way of implementing
num_elephantsfor theBoxclass:num_elephants(Box b) = b.children.map(num_elephants).foldLeft( (int i, int j) => i + j);
-
I didn't have to declare
num_elephantswhen I declared Node, I did it later, and that's fine. I could have done it in another file or package, too.
Nice takes this even further, though. You can override a method for a particular object. Smalltalk, Ruby, Python all do this, too. But your garden variety statically typed languages don't let you play this game. Let's go ahead and allow strings to count as elephants, too:
num_elephants(String s) = s.indexOf("Elephant") > -1 ? 1 : 0
but let's make a special exception:
num_elephants("SpecialElephant") = 5;
Methods are so convenient, Nice doesn't even have (or need) a switch statement. Think about it.
