[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: CLOS vs FLAVORS questions
Date: Wed, 29 Jan 92 17:37:03 PST
From: jbarnett@charming.nrtc.northrop.com
Are CLOS programmers really able to use the dispatch-on-multiple-args
capability effectively? Does EQL dispatching get used in nontrivial
ways (same argument specialized sometimes by class, sometimes by a
symbol, etc.)?
Abosolutely, to both questions.
I started out a skeptic about the utility of multimethods, but I am
very, very thouroughly convinced of their utility and importance.
I most often use EQL methods on symbols. I often use these symbols
to denote a particular state that an object might be in. For example,
an editor buffer might be :NEW, :SAVED, or :MODIFIED. I think this
relates well to your next question:
I found the flavors MIXTURE option very useful. In your experience,
does the dispatch-on-multiple-args capability compensate for the loss
of MIXTURE? The alternative seems to be dynamic evaluation of DEFCLASS.
What are other techniques to get some of MIXTUREs capabilities?
With a little thoughtful reorganization of your datastructures to
separate out orthogonal pieces into separate objects, and passing
those objects to generic functions which are specialized on multiple
arguments, you can replace mixtures with multi-methods
(multiple-dispatch generic functions). Basically, just remembering
the keywords you would use to choose what flavor out of a :MIXTURE
and passing that to the generic functions will get you the varying
methods. To get the effect of different slots with different mixture
choices, instead of keeping the keywords, keep objects with the slots
that would vary.
A common strategic flaw in using flavors (or any object system) is
to try to collapse everything into a single object, with arbitrary
hair in method combination to sort out the combined behaviour.
Unfortunately, this quickly leads to combinatorial explosion of
flavors. The classic example is the old MIT window system.
The technique of splitting complex objects into multiple sibling
objects, passing each of those objects to the generic function, and
specializing on each method as appropriate (and using CALL-NEXT-METHOD
a lot), is very powerful. It can quickly reduce a huge family of
classes and the need to dynamically change classes, down to a simple
small set of classes handling orthogonal pieces of the problem.
On the surface of it, the repetitive use of WITH-SLOTS and/or
WITH-ACCESSORS appears to be insidious and tedious. There seems to be
a lot of extra work for the common case where a method only specializes
on one arg. Has your experience shown this to be a groundless fear on
my part?
It really isn't all that bad. The modularity benefits are well worth
the extra trouble.
In my application, I invision using the CLOS first-class capability to
change an instance's type so that different methods become applicable as
the object moves through its life-cycle. (I know that flavors does not
have a first-class set of tools to do this.) Are implementations of
this capability efficient enough to really be used?
It depends on how often you're planning on doing it. It's going to
be a lot slower than just changing the value of one slot, and
dispatching on that in your generic functions.
My reading of part of the CLOS spec was ambiguous. The case I have in
mind concerns reevaluating a DEFCLASS where I have implemented methods
to assist in the "correction". If I only do one redefinition, I don't
have any questions. If I do TWO (or more) redefinitions and I define
helper methods for each, do the CLOS semantics guarantee that all sets
of helper methods get executed and in the proper order?
Yes.
I would appreciate any comments on these questions and any other reports
of impressions and experiences with CLOS. In particular, I'm interested
in same to help compare generic function systems (CLOS) and message
sending systems (FLAVORS).
Even Symbolics flavors considers the SEND style to be obsolete.
The idea is that there should be no syntactic difference between
invocation of an ordinary function, and a generic one. You should
be able to start out with an operation being non-generic, make
it generic at a later date, and even later still make it non-generic
again (and make it call a different generic function as a subroutine,
perhaps with additional derived arguments). You should be able to
do this without ever having to go back and change all of your callers.
Even better, you can pass around generic functions as first-class
objects, just as you would any other function.