[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)))

(defmethod plus ((x (eql 0)) (y number))

(defmethod plus ((x (eql 0)) (y (eql 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

(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.