[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
CLOS vs FLAVORS questions
Disclaimer: I haven't actually done any nontrivial CLOS programming.
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.)?
I've been told that people use this to optimize some special cases. For
instance, one might write:
(defmethod plus ((x number) (y number))
(+ x y))
(defmethod plus ((x number) (y (eql 0)))
x)
(defmethod plus ((x (eql 0)) (y number))
y)
(defmethod plus ((x (eql 0)) (y (eql 0)))
0)
I also believe that EQL specializers can be used to emulate Flavors's :CASE
method combination.
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?
I think the goal was that the meta-object protocol would fill this void, as
it provides a functional interface to DEFCLASS behavior.
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?
Yes, this is probably tedious.
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?
Flavors has a facility for changing type: CHANGE-INSTANCE-FLAVOR. It's not
as fancy as CLOS's mechanism (I believe some of the modularity of that may
have been due to some complaints I had about CHANGE-INSTANCE-FLAVOR).
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?
No, I don't think this is guaranteed. An instance might not be updated
until it is referenced, although an implementation is permitted to update
it sooner. At whatever time it is updated,
UPDATE-INSTANCE-FOR-REDEFINED-CLASS is called once, to convert from the
original definition to the current definition. Thus, your methods for this
must be prepared to receive an old object of any previous version of the
class, and do the right thing.
Another puzzle for me was the use of the DEFINE-METHOD-COMBINATION option
:IDENTITY-WITH-ONE-ARGUMENT. Consider the operator "+" and note that
"(+ (F))" is not the same as "(F)" if "F" returns multiple values.
What are the actual, intended semantics of this?
I suspect that it should only return the first value, i.e. (+ (F)) is the
same as (VALUES (F)).
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).
Oh, you're talking about Old Flavors (which also explains why you claimed
that Flavors doesn't have a class change mechanism). New Flavors (which is
hardly new any more) is a generic function system, although message passing
is still supported for compatibility. In any case, the two systems are
completely isomorphic, and generic functions make many things easier to
write. For instance, mapping a message send over a list has to be written
as:
(mapcar #'(lambda (x) (send x :message)) list)
while a generic function can be mapped with:
(mapcar #'gen-fun list)
Taking the message or generic function as an argument is about the same in
both systems:
(defun foo-msg (message instance)
(send instance message))
(defun foo-gf (gen-fun instance)
(funcall gen-fun instance))
An advantage of the generic function system is that FOO-GF doesn't actually
require its first argument to be generic, but FOO-MSG requires its first
argument to be a message name.