Re: Generics vs. Message passing: object namespaces

Dave Roberts <[email protected]>
Newsgroups gmane.comp.lang.lightweight
Message-ID <[email protected]>
On Thu, 2007-02-22 at 09:26 +0000, Tom Locke wrote:
> To my mind one of the chief benefits of the OO style is in  
> organisation of code. We put things in places that makes sense, and  
> we find them easily later. Yes this breaks down if a method belongs  
> equally in two places, but it's still a big net win IMO.

I used to think this as well. When I first encountered CLOS, coming from
a standard C++ -> Java background, it seemed downright strange. I was so
steeped in the "invoke a method on an object" model that I just couldn't
wrap my head around generic functions. In fact, I remember that when I
first learned OOP I was told that one of the benefits of OOP was that it
bound a data structure and the code to manipulate it together.
Unlearning this was difficult at first.

Having worked with CLOS more now, I really appreciate the CLOS model. I
found that the key was thinking of method invocations not as message
sending, or even invoking something "on an object", but rather
dispatching based on some information, some of which may be type
information. In my CLOS "mental model" I just imagine a little
trampoline doing something the equivalent of:

(defun generic-foo (arg1 arg2)
	(typecase arg1
		(type1 (generic-foo-method1 arg1 arg2)
		(type2 (generic-foo-method2 arg1 arg2)))

Yes, I know GF method dispatch is far more complex, but that's basically
the common case.

The nice thing about it is that I don't have to write or manage that
dispatch table directly. I can do it declaratively, which makes code far
more modular. Simply load a file and new methods appear for that generic
function. Those new methods can be in different modules anywhere. I
don't have to have the source code to any of the other methods.

What you finally realize about CLOS is that it's good for more than just
OOP in the classical sense. In addition to method dispatch based on
object type, ala
(generic-foo object param1 param2)
you can also use EQL specializers to create modular dispatch tables.
Now, whenever I find myself wanting to write
(defun func (arg1 arg2)
	(ecase arg1
		(foo (...))
		(bar (...))))

instead I know write

(defmethod func ((arg1 (eql 'foo)) arg2)
	...)

(defmethod func ((arg1 (eql 'bar)) arg2)
	...)

The nice thing about this is that different modules can all contribute
to the func dispatch table, simply by executing a
(defmethod func ((arg1 (eql 'blah)) arg2)
	...)
form.

I didn't appreciate this aspect of CLOS method dispatch until I read
Peter Siebel's Practical Common Lisp. Peter makes extensive use of this
technique and I have come to appreciate it a lot.

In summary, remember the word "dispatch" and you'll be better for it
when trying to wrap your head around CLOS.

> In message-passing style OO, it's often possible to simply guess the  
> method name, and there is often support in the editor for enumerating  
> the available methods. So writing is easier. Reading is easier too -  
> that "_to_buffer" in the above example is largely noise. Noise that  
> adds up pretty quickly.

To a certain extent this is just editor mechanics. It's not quite as
easy as with the message-send model in that it's harder to deduce the
type of object you're trying to invoke a method on before you have
written down the object. In other words, with:
object.|
vs.
(|
where "|" is the carat location, the editor can be smarter about
whittling down the set of applicable methods based on the type of
object. But the editor can obviously still help you out with names in
the CLOS example, too, and SLIME will do it if you just start typing
something (anything).

-- Dave
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.