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

Comments on Concepts chapter 1/23/87

Here are my comments on the latest Concepts chapter, except for minor
editorial points.  Indented text is from the document source.  Page
numbers are from the Jan 15 version, but should be close.  I've omitted
comments that have already been resolved in the Jan 23 source; I haven't
seen a Jan 23 hardcopy yet, so I might later have more comments on
things that were added in that version.

    The naming conventions for generic functions are
    precisely the same as those for ordinary Common Lisp functions.

Huh?  I can't figure out what, if anything, this is supposed to mean.

    A method---also
    called a {\bit method object}---is a function that can be used in any
    of the ways that ordinary functions can be used in Common Lisp.  

Is this intended to imply that FUNCALL of a method object is allowed?  I
strongly disagree with that.  We agreed that FUNCALL of a
generic-function object is allowed, but that is an entirely different
story.  I strongly believe that the standard should specify that methods
can only be called by the internal mechanisms of the implementation of
generic functions, not directly by the user.

    A method object contains a method function and a set of {\bit parameter
    specializers\/} that specify when the given method is applicable.

Qualifiers, too.

    Like other objects, all classes are themselves instances of classes....

This paragraph is too early.  Metaclasses should not be mentioned until the
basics of classes have been discussed.  A good place would be at the end of
this subsection, right before \beginsubSection{Defining Classes}.

    We will say that a class,
    $C\sub{1}$, is a {\bit superclass\/} of a class, $C\sub{n}$, if there
    exists series of classes, $C\sub{2},\ldots,C\sub{n-1}$ such that
    $C\sub{i+1}$ is a direct superclass of $C\sub{i}$, for $1 \leq i<n$.

"series" -> "a series"
The last occurrence of "superclass" should be "subclass" (no wonder no
one could understand this paragraph!).

    We refer to the set of classes consisting of some given
    class $C$ along with all of its superclasses as ``the classes at or
    above $C$.''

Ugh!  Can't we find a more concise term for this?  At least in the latest
version (unlike Jan 15) the term seems to be used consistently.

    Classes are organized into a {\bit lattice}, which gives them a....

This whole paragraph doesn't make complete sense, because there is not
-one- local precedence order; there is a separate local precedence order
for each component class, and all of them are taken into account.

The discussion would be clearer if the local precedence order of a class
was defined to be the list formed by consing the name of the class onto
the front of its list of direct superclasses.  This local precedence
order, as a total order, expresses all of the constraints.  The
computation of the overall class precedence list can be described
entirely in terms of the local precedence lists, without the confusing
extra complexity of the lattice.  The lattice concept could be dumped
entirely, or could be retained for explanatory purposes in other parts
of the document, but when describing the class precedence list it just
gets in the way.  Thus I would rewrite these two paragraphs as follows:

  When a class is defined, the order in which its direct superclasses
  are mentioned in the defining form is important.  Each class has a
  {\bit local precedence order\/}, which is a list consisting of the
  class, followed by its direct superclasses in the order mentioned
  in the defining form, followed by the class {\bf t}.

  Each class has a {\bit class precedence list}, which is a total order
  on the set of classes at or above a given class.  The total order is
  expressed as a list ordered from most specific to least specific.  The
  class precedence list is used in several ways.  In general, more
  specific classes override features inherited from less specific
  classes.  The method selection and combination process uses the class
  precedence list to order methods from most specific to least specific.
  When one class has several superclasses, a more specific class can
  override some of the options declared in the {\bf defclass} form of a
  less specific class.

  A class precedence list is always consistent with the local precedence
  order of each class in the list.  The classes in each local precedence
  order appear within the class precedence list in the same order.  If
  the local precedence orders are inconsistent with each other, no class
  precedence list can be constructed, and an error will be signalled.
  The class precedence list and its computation will be discussed at
  length below.

p.1-7: The detailed description of the syntax of defclass ("first element",
"second element") might better be left to Chapter 2.  Too much information
from preceding and following sections is duplicated here, and often what
is said here is incomplete or incorrect.

    If a local description of a slot is provided, it completely overrides any
    description of that slot inherited from a superclass.

This is not true.

    The fourth element is the set of class options, which....

The class options are the rest of the form, not an element of the form.

p.1-8: The concept of slot is never really explained.  "The Structure of
Instances" would be a good section in which to explain it.  The explanation
should start with the concept that there is a list of names (symbols, with
the same restrictions as variable names in LET) not containing any
duplicates, and with each name can be associated a value and some slot
options.  The scope of the association depends on the :allocation.

    Creating a type by means of {\bf defstruct} creates a class in this
    lattice.  Such a class is an instance of {\bf structure-class} and a
    direct subclass of the class that corresponds to the type given as
    its {\bf :includes} argument.  If no classes are included, the new class
    is a direct subclass of the class {\bf t}.

I think the last sentence should be deleted, because I'm not sure it's
true and because I don't think it matters whether something is a direct
subclass of T.  As far as I can tell that either has no effect, or makes
the class precedence list computation signal an error, depending on
whether T is in the middle or at the end of the list of direct
superclasses.  Just qualify the second-to-last sentence with "if any."

p1-12 {Inheritance}:

In spite of several go-rounds, the slot inheritance discussion is
complicated and confusing.  This may be partly a reflection of the
design, but lacking any ideas for improving the design right now, let me
suggest improving the discussion.  I think a lot of the problem is the
confusing concept of slots provided by or belonging to a superclass;
there is no such thing: only instances have slots, what a superclass
provides is a slot-description, not a slot.  Also I think the whole
superclass/subclass orientation of the presentation is more confusing
than helpful.

Instead, I would speak of slot descriptions provided by classes in the
class precedence list.  Case 1 in the existing text is the case where
only one class provides a slot description, which is quite simple.  The
existing explanation is almost okay.

The other case is where -more- -than- -one- class in the class
precedence list provides a slot description with a given name.  There is
always only one slot created, and its characteristics involve some
combination of the several slot descriptions.  The slot options provided
by the various slot descriptions combine as follows:

:allocation comes from the most specific slot description -- if it
doesn't specify :allocation explicitly, :allocation :instance by default.

:initform comes from the most specific slot description that specifies
:initform explicitly.

:type is the AND of all the :types.  [At least, this seems to be the
only rule that makes it possible for the compiler to exploit the
:type information at all, other than requiring each slot-description
to specify exactly the same :type.]

:reader and :accessor create methods at defclass time, and these methods
are applicable, but the slot options themselves don't do anything at
class combination time.

This should be a lot simpler.

Somewhere (I have it written on the Inheritance page, but I think it
might belong with the not-yet-existent general discussion of slots) it
should be clarified that methods know only the slot names.  (The above
isn't strictly true now that :type has been introduced.  Methods know
the slot names -and- types, which is why types can only be restricted,
not expanded, by inheritance.)  Thus a method expecting to access a
:allocation :class slot, applied to an instance of a subclass that
redefines the slot with that name to be a :allocation :instance slot,
will access the instance slot instead.

Question about :allocation :none.  Does this mean that there is no slot
by this name, or that there is a slot but an error is signalled if you
access it?  The difference would be in the set of lexically apparent
variables created by with-slots.  I think the answer should be that there
is a slot.

p.1-24 {Determining the Class Precedence List} (moved to an earlier page
in the latest version of the document, which I think was an improvement):

    When one class has several superclasses, a more specific
    class can override....

This happens even with one superclass, so delete the "when" clause.

See my previous comment that the local precedence order concept provides
all of the constraints and the lattice concept is not needed.  With that
simplification, I think this way of describing the class precedence
computation would be okay with me.  My only other complaint is that the
original concept of local rules controlling what happens, so that the
programmer can understand things locally without having to take a global
view, has been buried under a flood of mathematics.  The concept is
still there, but it takes a determined reader to find it.  I think it
would be better to start this section with that concept, perhaps using
some text from the previous version of the section, and use that as
motivation for the mathematical discussion.

I found the description of preorder treewalk virtually incomprehensible.
I know what it's trying to say, and agree with that, but the way it is
said needs to be rewritten or clarified.

p.16 {Introduction to Generic Functions}:

    Typically a generic function object is stored as the function definition
    of the symbol that is the name of the generic function.

This sentence should be put in its own paragraph and augmented with a
clarification that this is the -only- association from the name to the
generic function.  Both defgeneric-options and defmethod use
symbol-function of the name to find the generic-function. The next
paragraph only mentions defgeneric-options, but most of what it says
applies to defmethod, too.  (And the -setf versions also, of course.
Their difference is that they use get-setf-generic-function in place
of symbol-function.)

SETF generic functions are never explained anywhere.  Most Symbolics
design reviewers found them completely incomprehensible.  A section
explaining them should be added between {Introduction to Generic Functions}
and {Introduction to Methods}.  If no one can be found to write this,
put in a blank section as a placeholder.

p.1-17 {Introduction to Methods}:

    Only required parameters can be specialized.

I would add "A future extension might allow optional and keyword
parameters to be specialized" so that people know where we are coming

    A parameter specializer
    is a list, {\tt ({\it variable-name parameter-specializer\/})},
    where {\it parameter-specializer\/} is one of:

This is using "parameter specializer" to mean two different things.
Someone changed the previous text "A specialized parameter specifier is
a list..." to this; the previous version was at least correct, although
awkward.  "Parameter specifier" is a term defined in CLtL.

The separate bullets for "any class" and "standard type class" are
extremely confusing.  The reader wonders why "standard type class" is
mentioned separately and whether "any class" somehow doesn't include it.

    Note that a parameter specializer cannot be a type specifier list,
    such as {\tt ({\bf vector single-float})}.  

This seems to be the last residue of a discussion of the relationship
between parameter specializers and type-specifiers.  I strongly believe
that the rest of that discussion should be restored.  The point is that
parameter specializers are a subset of type-specifiers, and thus
discrimination can be understood in terms of typep, and more
importantly, we are extending the Common Lisp type system upward
compatibily, not creating yet another type system.

Also the requirement that Common Lisp be modified to include
(deftype quote (object) `(member ,object)) has been removed from
the document and needs to be restored.

p1-19 {Method Selection and Combination}:

The \numitem headings don't stand out adequately.  Someone who knows TEX
should fix this section to use a more appropriate TEX feature.

At the end of step 2, or perhaps at the end of the section, add a remark
that two methods with identical specializers but different qualifiers
will not be ordered by this algorithm.  Normally this is okay, because
the two methods will play different roles in the effective method
because they have different qualifiers, and no matter which order they
appear in, in the result of step 2, the effective method will be the
same.  If the two methods play the same role, so that their order
matters, implementations are encouraged to signal an error (this would
happen as part of qualifier-pattern matching in
define-method-combination).  I don't mind requiring instead of
encouraging signalling an error, but this verges on program development
tools so perhaps others would object.  It is impossible for there to be
two methods for a single generic function with identical specializers
-and- identical qualifiers, because defining the second would replace
the first (see defmethod, add-method).

p1-27: {Short Form of Define-method-combination}:

I'm tempted to eliminate the operator as a positional argument and
replace it with a :operator keyword option.  The operator is usually
the same symbol as the name.

p1-28 {Long Form of Define-method-combination}:

Could someone who knows TEX convert the syntax expression to TEX?

    Thus a qualifier-pattern can be the empty list {\bf ()}
    (which matches primary methods, which are always unqualified)

This is wrong.  "Primary methods" and "unqualified methods" are two
separate concepts.  For standard method combination they happen to be
equal sets of methods, but that's essentially a coincidence.  The
correct parenthetical expression is "(which matches unqualified

p1-34 Meta objects:

    \CLOS\ provides several predefined meta-objects:  {\bf generic-function} 
    (the default class of a generic function), {\bf method} (the default
    class of a method), {\bf class} (the default class of a user-defined
    class), and the standard method-combination type). 

I thought method combination types were not objects in their own right,
but were just methods.  I have no real objection to making them into
objects, although it doesn't seem like anything would be gained by
doing so.

I also thought we were going to change these names, but maybe that
discussion is still ongoing.