Tibor's Musings

Common Lisp Object System

In a previous post, I looked at Common Lisp extensibility, how easy and how natural it is to implement single-method dispatch in the language; and how "unnecessary" it actually is, given the other powerful constructs of the language. Let's now look at CLOS, the Common Lisp Object System, to see how the object orientation is really done in the Lisp world.

Single-method dispatch

Single-method dispatch is based on a model where objects send each other messages. We know it from Java, C++, Python, etc. However, is it really the best way to model the real world?

Consider an object colliding with another object, such as two strawberries launched against each other. Which one sends the message to whom? Who's the sender and who's the receiver? Aren't they both equal partners in the collision event? If so, isn't it better to model this situation differently?

Enter generic functions and multi-method dispatch.

Multi-method dispatch

Example added in August 2010

Q: "So, I don't know about CLisp's OO or multi-methods. Could you give example pythonic syntax that makes clear how it would work?"

A: Pythonic pseudo code example defining the rock-paper-scissors game, emulating Common Lisp way of doing things, would go like this:

class Rock:

class Paper:

class Scissors:

defgeneric game(x, y)

defmethod game(Rock r, Paper p):
   print "%s wins" % p

defmethod game(Rock r, Scissors s):
   print "%s wins" % r

defmethod game(Paper p, Scissors s):
   print "%s wins" % s

A concrete rock-paper-scissors game round between John and Jane:

john_fist = Paper()
jane_fist = Scissors()
game(john_fist, jane_fist)
# Jane wins

For more, see e.g. http://www.c2.com/cgi/wiki?MultiMethods.



Everything should be made... simple?

Einstein once said that "Everything should be made as simple as possible, but not simpler". Sometimes people forget this "but not simpler" part. The simplifying point of view misses the fact that life is inherently complex and you cannot model it well using oversimplifications. The real world is full of multiple inheritance and multi-method dispatch: it suffices to just look around. In single-method dispatch language environments, people try to circumvent this, inventing artificial workarounds to get the job done. I argue that this is not the best approach. At some point in time one needs to stop simplifying and accept the inherent complexity of the problem. Then, of course, you need to tackle it with the right tools, the best ones available.