[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Some early comments on 88-003



I'm holding most of my comments until the next revision of chapter 3
comes out, since Gregor said a lot of things addressed at our meeting
last week will be fixed, and the draft will be coming out within a
couple of weeks.  However, here are a couple of points that were not
touched on in that meeting.

Introduction: I'd like to see a one-page description of each protocol,
instead of the present one or two sentence decriptions.  This should
cover what the protocol is for, who uses it, who implements it, what
its limitations are, etc.  By "who" I mean the CLOS system itself, a
programmer working within that system, or a programmer extending the
system.  I'd also like to see a much clearer exposition of contracts
for any generic functions that the user is intended to write methods
for, clarifying constraints on the user's method and constraints on
the caller.  Right now the document doesn't even say which generic
functions the user is supposed to be able to write methods for.

I think the naming layer is a little too separate.  I don't see why
metaobject classes shouldn't accept the name as an initialization
argument, instead of requiring it to be setf'ed separately.  This
name is just documentation: supplying the initialization argument
doesn't cause setf of symbol-class or setf of symbol-function to be
called of course.

I don't understand why accessor methods are said to be created by
an update-dependent method through a special deferred creation
mechanism, rather than being created in the normal fashion as part
of the macro expansion of defclass.  Earlier we said that there is
no real difference between an automatically created accessor method
and one that the user writes himself, and that both should run at
the same speed.  Page 24 gives a reason (the class may not be fully
defined) for delaying creating accessor methods, but this reason does
not make any sense.  There is no such restriction for ordinary methods.

The return value of method-applicable-keywords (by the way, a confusing
name), in the case where any keyword argument is valid because some
method specifies &allow-other-keys, can't work.  Page 27 says the symbol
&allow-other-keys is a member of the list to signal this.  But that's
ambiguous with the tasteless but valid practice of using the symbol
&allow-other-keys as a keyword argument name.  Also representing the
infinite list of all possible acceptable keywords with a finite list is
a poor idea, because anyone who doesn't check for this case and just
does MEMBER is bound to find some symbol that is not a member of the
finite list, even though it is a member of the conceptual infinite list.
This should be changed back to what I proposed originally, which is that
the symbol &allow-other-keys is returned in place of the whole list.

The way reinitialize-instance is specified can't work.  The basic
problem is that reinitialize-instance is specified to call
initialize-instance, but an initialize-instance method has no way to
find out whether it was called from reinitialize-instance or from
make-instance.  This makes it impossible to implement the specification
(page 30) that "only those arguments explicitly given will affect the
object."  First, a user-written initialize-instance :after method that
takes keyword arguments and defaults them is supposed to use the
defaults if called from make-instance, but ignore the defaults if called
from reinitialize-instance; there is no reasonable way to program that.
Second, page 2-59 specifies some permissible optimizations for the
default primary method for initialize-instance, and these optimizations
are incompatible with what reinitialize-instance is doing.  The use of
:allow-other-keys in the default primary method for
reinitialize-instance given on page 30 is also a dead giveaway that the
modularity is wrong.  In any case, make-instance and
reinitialize-instance aren't the only two functions that need to put an
instance into a consistent "initialized" state.  Class-changed and
update-instance-structure have similar needs (as Barmar pointed out
during the plenary meeting).  The general outline of the solution to
this is obvious: there should be a generic function that acts as a
common subroutine of all four of those functions, plus any others that
are discovered.  Initialization/update code that needs to be shared
should be in methods for this new generic function, code that does not
need to be shared should be in methods for the other generic functions.
The arguments to this new generic function should probably be an
instance, the name of the function that was originally called, and
initargs.  Reinitialize-instance should have its own method for dealing
with the slots, instead of reusing the initialize-instance one.  I don't
have a full proposal yet, but it would be along these lines.  It's also
worth thinking about a more radical idea, in which initialize-instance
would be broken into two parts, to get the modularity right.  More work
is needed here but I hope you can see why what's in the document today
won't fly.

Still in the reinitialization section, this update-dependents mechanism
isn't quite right.  The problem is the claim that all standard-objects
support update-dependents, which then requires you to say that there are
actually two different mechanisms for recording dependents, the general
one that works for all standard-objects and the efficient one which is
the one that you're really supposed to use.  It doesn't make sense to
stick every feature that sounds at all general into standard-object;
that's pre-multiple-inheritance thinking.  The update-dependents
mechanism should be provided by a mixin with a documented name, which is
a superclass of every class that needs the feature.  I also have some
minor comments about the interface to update-dependents, but they should
wait a bit.

The modularity of update-dependent for classes might be wrong, which
probably means just that I think the keyword arguments it takes aren't
the right ones.  I'm not sure of my thinking here yet, but I thought I
ought to mention it now in case it stimulates anyone else to think about
this.  When I get time I will do a detailed comparison of this with the
way Flavors does it, which I believe to be both correct and fairly efficient.

Don't forget that the document never says what the "access key instance
access" protocol is for, how it's used, and what the keys are -- Gregor
had to explain that in person.  It needs to be in the document.

Issues not covered at all (not a complete list, probably):
  - lexical generic functions, relation to class-direct-generic-functions
  - compile-time versus run-time environment issues