[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Comments on AMOP
- To: bobrow@parc.xerox.com, gregor@parc.xerox.com, desrivieres.PARC@xerox.com
- Subject: Comments on AMOP
- From: Jon L White <jonl@lucid.com>
- Date: Thu, 15 Nov 1990 20:21:33 PST
- Cc: mop@arisia.Xerox.COM
[Always cramped for time -- at least it's only the 15'th!]
I have a number of comments, ranging all the way from little
nit pickings to overall design directions. For sake of brevity
(ha!) I'll not point out typos etc that other reviewers must
have already noticed; and one of the design issues is so large
that I'll compose it into a separate message (slot-specifications
versus slot-definitions).
However, this does raise the issue of what good these comments
can do now. I think some of the metaobject protocol design points
are not "cast in concrete" yet, because only a relatively small
part of them have been implemented by the several vendors with
CLOS's in the field, or about to be in the field (of course, I
mean the stuff in additon to the X3J13 document 88-002R and
subsequent "cleanups" of that.) In addition to the relatively
small set of "introspective" functions and types that Dave Moon,
Scott Cyphers, myself, and Franz agreed upon last March, I might
count VALIDATE-SUPERCLASS in this category too. Although
Symbolics has not documented VALIDATE-SUPERCLASS, they provide
it internally, and seem to be willing to talk to people about
it's existence as long as it is understood that all the not-yet
standardized stuff is subject to change. Lucid provides "on line"
documentation (sometimes of dubious quality) for such facilities.
So I hope you all can take my comments in the right spirit --
namely, the positivie praise is not just "stroking", but sincere;
the negative comments are not just carping, but are trying to point
out troublesome spots, in case there is time to amend them somehow.
And the "constructive suggestions" and not just off-the-wall, but
re-directions for design points that will definitely cause some
unnecessary degree of trouble *** and for which it isn't too late
yet to alter them ***. You must realize, that with the final
publication of this document/book, it will be infinitely harder to
make corrections to poorly designed or overlooked points; witness
the difficulties we may have with the premature descriptions found
in CLtL/II now (as opposed to the corrected versions that will
eventually and hopefully come out in a final ANSI proposal.)
. . . . . . . . . . . . . . . . . . . .
First the praise. The world will definitely be better off for the
publication of this book. At present, only a handfull of cognescenti
have mastered CLOS internals. One can hardly see any of the important
implications of the rigorous specification (X3J13/88-002R) merely by
being astute about computer languages and reading the specification.
There is no doubt that the widespread dissemination of the PCL sources
has had a signal impact on making CLOS acceptable to the "cognescenti"
who have managed to grasp its beauty. But frankly, those sources are
virtually impenetrable; only the most dedicated hacker with lots of
time on his hands can effectively understand them and manipulate them
constructively.
But more. The overall organization of this "book" is very good; it
presents the topics in a very logical, step-by-step order, and will,
_I think_, be understandable by any reasonable computer scientist.
In particular, I like the breakdown of the example code; good cutting
into bite-sized pieces, good use of succinct programming paradigms!
[I always *knew* the inclusion of all those APL-inspired sequence
functions into Common Lisp was good for something! Quux Redux! Of
course, a completely functional style in this presentation doesn't mean
that this is a good way to actually implement the intermediate CLOS
functionality presented; it just makes it more reasonable not to have
to worry about efficiency considerations and programming hacks when you
are tyring to understand the basic algorithms. Also, the kinds of
efficiency considerations I am thinking of aren't something that can be
compensated mechanically from the functional style code, so let's not
use this time and space for the continuing arguments about what an SSC
("Sufficiently-Smart-Compiler") could do.]
I especially liked the Class-With-Attributes example; I've heard
comments and questions about CLOS from many people who have used KEE
in the past, and this section may actually help them in coding up some
of their former concepts. It would also be nice to see a bit more
attention given to a route that is susceptible to optimization. For
example you might multiply the number of actual slots by the number of
attributes and "interpolate" some slots corresponding to the attribute
storage by giving them internally generated names. So a class with
slots A, B and C and with attribute CHARM could be automatically
extended to have slots #:A-CHARM, #:B-CHARM, and #:C-CHARM (don't
take these names too seriously), and then it would be trivial to add
optimizations for forms like (SLOT-ATTRIBUTE <x> 'A 'CHARM).
The examples in sections 3.5 and 3.6 of "monitored" slots and "dynamic"
slots are really quite nice! I hope someone can actually use them.
Section 3.5 on "Slot Access" does a good job clearing up why the
function SLOT-VALUE isn't generic. The straw-man you offer (on the
middle of page 102) is, however, just a bit weak. Just as there is
STANDARD-OBJECT as a "maximal" class for STANDARD-CLASS, so it is
possible to have DATABASE-OBJECT as a "maximal" class for
DATABASE-CLASS, even when DATABASE-CLASS is a sublcass of
STANDARD-CLASS [I know that Andreas Paepcke has addressed just this
point in work related to his PCLOS. By the bye, my use of "maximal"
here is clear, no?] Knocking down this house of cards is a bit more
tedious since you have to draw the comparison between SLOT-VALUE and
SLOT-VALUE-USING-CLASS for other metaclasses that don't have "maximal"
elements. But still, this is a good section to have.
. . . . . . . . . . . . . . . . . . . .
Now, time for some critiques. If this note seems to be lengthy
on the critique side and shorter on the praise side, I do not
mean to be harsh; rather it is only because I wanted to mention
some thoughts I've had while reading the AMOP *** before *** they
become totally moot by its widespread dissemination.
There is one general downside to the overall presentation, even though
it isn't by any means a showstopper. I had been expecting, based on
previous comments from you all (the authors), a rationale behind some
of the CLOS components; in particular, the WHY's of why you would want
some of these otherwise unmotiveated metaobject thingies. I know, you
could argue that the reasoning is implicit after you have an overall
understanding of the presentation and why it all hangs together; I just
think it would be better to make these reasonings and comments more
explicit, and to do so at a very explicit level (i.e., the level that
says "user-control over programming constructs is good" or "extensibility
is better" or "motherhood and apple pie" doesn't cut it.) I won't take
time/space to make detailed suggestions on this matter, because it goes
too deep for casual contributions (and the effort to flesh it out this
way might be too great at this late a time in your publication cycle).
I just wanted to point out what I consider a serious lacuna. In truth,
I've only had the time to read Chapters 1-3 carefuly, and skim chapter 4;
but the disappointment is that the first half of the book is page after
page after page of details of an implementation, with few if any hints
at the expected rationales.
Now, a minor nit. The first chapter's title is "How CLOS *is*
Implemented" [emphasis mine]. This is bit much. It is about how
CLOSETTE is implemented, and I can recognize how closely CLOSETTE
parallels a re-worked and abbreviated version of PCL. So maybe a
title like "Implementing CLOS" would be more neutral? You surely mean
the code to be illustrative and not mandatory. For example, how much
of the functionality on pages 12 and 13 is:
(1) required by X3J13/002R and CLtL/II?
(2) included in the "de-facto, introspective metaboject protocl"
abstracted by Symbolics, Lucid, and Franz and included in
their products?
(3) mentioned in the continuing saga of the "Chapter 3" proposal?
(4) a PCL'ism probably not included in the foregoing?
Consider each point in turn for PRINT-OBJECT, CLASS-PRECEDENCE-LIST,
ENSURE-CLASS, and DIRECT-METHODS or CANONICALIZE-DIRECT-SUPERCLASSES.
This is clearly a presentation of CLOSETTE -- a workable implementation
of a small subset of CLOS -- not a critical study of what functionality
is logically required, or of what may eventually be desirable.
A more serious point is the substitution of novel terminology for what
are likely already standardly acceptable terms. Page 11 introduces the
term "Class metabojects"; how is this different from "Class objects?
Page 74 introduces "metaobject classes"; how different is this from
"metaclasses"? Further, the term "metaobject" is badly blurred by
(p.74) "a metaobject is an instance of some metaobject class"; does
this mean that the totally useless object I get by randomly calling
(ALLOCATE-INSTANCE 'STANDARD-METHOD) is a metaobject? That doesn't
fit with the general direction of metaobjects as objects used in the
definition of the basic system (and which the user needs to know about
in order to tailor the system to his own desires). With bated breath
I was awaiting the introductions of "meta classobjects" and "object
metaclasses"; it just looked like a case of picking three wordlets and
combining them all ways. I can't believe that avoidance of the word
"metaclass" has any value -- and your declaration of intent to do so,
on page 74, offered no justification. [SmallTalkers, and researchers
familiar with Pierre Cointe's and Patti Maes' works will likely not be
confused by it either; so that can't be the justification.]. I found
that playing the "three wordlet" game was quite frustrating, and
definitely would have preferred simpler and more conventional terms.
This is not a question of *consistency* of terminology, but rather the
invention of novel terms where standard ones would have sufficed.
In _many_ places, all the extra "objectology" could simply have been
dropped. Just say "class". In context, it is ususally easy to tell
if "class" refers to an abstract concept, or to an actual data object
(or, as the current AMOP say "Class metaobject"); this is particularly
true of prose that is describing the actions of a sample piece of code.
On the other hand, the terms on page 74 of "regular methods" (and
"specialized methods"?) work quite well. "Regular" is always up for
grabs as a locally-redefined adjective. Even "Regular metaclasses"
and "Specialized metaclasses" would work.
As I read the sample code on page 51 -- ALL-CLASSES -- I wondered why
you didn't just expose the hash table documented on page 14? Of course
it would be infinitely more efficient to maphash across the table than
to descend a "bushy" class hierarchy, but that isn't why I bring up the
question. Rather, I am wondering if you are afraid to call the hash
table found in the closure of FIND-CLASS a metaobject? Is it the case
that the only things you are willing to call "metaobjects" are of
metaclass Standard-Class (such as the class named by T?). At least
on page 46 you point out that every Lisp object has class. So this
anonymous hash-table has some class, and is very pertinent to the
implementation of the system. [By the bye, the entitlement of "class" to
everything brings up the question of the missing BUILT-IN-CLASS, but ...]
Does it have to be of a completely new datatype to be a metaobject?
The section on encapsulated classes -- Section 3.3.4 -- has some
conceptual difficulties. It says that encapsulation "defines a
subclass that assigns unique names to ***direct slots***" [emphasis
added]. But what is a "direct slot"? is it one whose composition
includes no inherited components? or is it one that simply has
*some* components attributable to the slot specifiers given directly
to the class creation functions? If the latter and not the former,
then how can the slot be considered "encapsulated" if it is exposed
to inheritance? But if the former, then the definition is circular,
since it is precisely "inheritance" that you are trying to block.
Since "encapsulated" mostly implies name inaccessibility, then why not
just use the package system for this purpose? (well, that is why it was
invented.) You could make a new, unique package for every encapsulated
class, and the accessibility of the private slots would be controlled
by package accessibility (which would just happen to coincide with the
internals of that class.) Further, you would use IMPORT and EXPORT to
control the modular concepts that other languages call "import" and
"export". Interesting.
Also the section on Discriminating functions and Method Functions has
some difficulties if it is targeted towards a mandatory piece of CLOS
functionality. Dick Gabriel has already argued against the notion of
"discriminating function" as being basic; rather, he proposed a design
that would create a "discriminating object" which a most-general disptach
function could operate upon in order to make the actual dispatch code for
a generic function. And I have (in other forums) brought up some of the
difficulties with forcing every method to support a single Method-Function
component. For effective methods, it is clear that a single "function"
could easily be generated; but it is not at all clear that
COMPUTE-EFFECTIVE-METHOD must compose its resultant function merely by
pulling out the Method-Functions of the various methods. So what good
is Method-Function? Having a component which is some sort of generator
usable by method combination would be fine, but the Method-Function as
currently mentioned isn't it.
. . . . . . . . . . . . . . . . . . . .
In the next few paragraphs, I want to make a couple of short-but-specific
constructive suggestions. I'm sure each of these isn't too late in the
release cycle of existing CLOS implementations, nor are they constrained
yet by X3J13 standards.
Page 41 introduces APPLY-METHOD. This is a good concept. Unfortunately
the ordering of arguments is pessimal. By analogy to APPLY, APPLY-METHOD's
signature should be like:
(APPLY <method-object> <next-methods-list> <arg1> <arg2> ... <rest-args>)
In the case where all the arguments to the method/generic-function have
been enlisted, this just becomes:
(APPLY <method-object> <next-methods-list> <all-args>)
The format currently in AMOP doesn't allow for the obvious analogy with
APPLY (and that's bad enough), but Lucid's implementation has already
supplied an APPLY-METHOD function with the preferable signature as an
extension (that was actually needed by two different end users). Why
not follow precedent here? and follow the good analogoue to APPLY.
Section 2.4 "Programmatic Creation of New Classes" is belaboring under
the pressure to have a symbol to name every class. Why not just go for
it and permit anonymous classes? Unfortunatley, the X3J13 specification
implies this constraint also (no anonymous classes); but we all have
been expecting it to be relaxed when the metaobject protocol is available.
And Dave Moon has been using some sort of anonymous classes in CLIM work
(I think); so Lucid had to modify some part of it's implementataion *not*
to enforce the 88-002R semantics, and not to depend upon every class
having a name (note: "name", not "propoer name"). Can we nip this in the
bug? this propensity to name everything. Besides, I really didn't see
how enforced name assignments helped the CLOSETTE presentation in any way.
Following below are comments about a few more minor problems,
which could easily be corrected.
-- page 21 uses STD-INSTANCE. Recent mailings to the X3J13 list
show the undesirability (and unnecessity) of using 1960's style
abbreviations, especially where hard-to-remember names are
concerned. So why not STANDARD-INSTANCE. If vowel-less
abbreviations are such a good idea, then why not use
STD-INST-LCL-SLTS? (I'll be I don't have to fill in the
vowels or other missing particles for you here.) If they aren't
such a great idea in general, then why stoop to them on STANDARD?
-- page 23 has ALLOCATE-INSTANCE as a regular non-generic function;
but even the page 25 code implies that it must be generic. So
why not name the defun'd function ALLOCATE-STANDARD-INSTANCE, and
later show that the generic function could simply dispatch to that
function in the case of a standard-class request. Or maybe even
rewrite the defun as a defmethod.
-- page 26 says that SHARED-INITIALIZE can set a value "From an
existing binding". One can ask "What does this mean?" perhaps it
would be good to mention a case or two in which you wind up inside
SHARED-INITIALIZE and there are "existing bindings" to look at
(e.g., redefinitions, :BEFORE methods and the like.)
-- page 37 defines COMPUTE-APPLICABLE-METHODS-USING-CLASSES; this is a
very misleading name to someone familiar with the nomenclature style
of "Chapter 3" -- such as functions like SLOT-VALUE-USING-CLASS,
ENSURE-<mumble>-USING-CLASS, and so on. Let me suggest instead
COMPUTE-APPLICABLE-METHODS-FROM-CLASSES.
-- page 51 calls CONVERT-TO-STRING on the CLASS-NAMES; but haven't
you already restricted the class NAME slots to being symbols?
(not necessarily proper names, but certainly not random objects).
And despite it's name, STRING-LESSP accepts symbols; so the symbol
which is the name of a class is acceptable for the comparison.
-- page 52 -- section 2.2.6 -- introduces the concept of "Well-Ordered
Classes". This is a misuse of the mathematical concept of a well
order; what you are really discussing is the concept of consistency
of partial orders. You could say "Consistently Ordered", without
having unwanted implications. Or, "Consistently Ordered by
subclasses." [By the bye, I was seriously disappointed that you
just presented this concept and didn't refer either to its utility
in method combination, nor to the very common misunderstanding that
the non-cognescenti have about the partial orderings induced by the
CPL algorithm.]
-- page 74 defines "exactly three system-supplied" metaclasses; what
happened to STANDARD-SLOT-DEFINITION? shortly thereafter, the
example of Class-With-Attributes uses slot-definition objects all
over the place.
-- Sections 2.2.7 and 2.3.1 -- about "regenerating" definitions for
classes, generic-funtions, and method -- implies that virtually all
the source code is stored (e.g., the :INITFORM's, as well as the
code bodies for methods). I wouldn't like this to be taken as
a revival of the "residential Lisp" versus "text files for sources"
debate. Can't you somehow "tone it down"? at least a word to the
wise that the source code for some parts *** might not *** be
available after file compilation, but that the other parts of
the regeneration are still accessible?
-- Chapter 3 uses terms for method definitions like "overridden" and
"redefined" (and "shadowed"?); but I don't recall seeing these
defined in AMOP. I know they are carefully defined elsewhere, but
that doesn't help if the definitions aren't available in AMOP too.
[or, maybe I just forgot where they were defined?]
-- Section 3.3 might try drawing a parallel between a hypothetical
VALUE attribute and the functionality of the SLOT-VALUE function;
that way, SLOT-ATTRIBUTE could more easily been seen as a
conceptual generalization of SLOT-VALUE.
-- page 99 says that "the regular method [on Finalize-Inheritance],
which is specialized to Standard-Class, *cannot* be overridden
or redefined" [emphasis in original]. Don't you mean "must not"
rather than "cannot"? in CLtL parlance, "it is an error ...".
-- On the same topic, you later say that the specialized method must
call CALL-NEXT-METHOD (in order to get the side-effects, or whatever.)
This might be a good place to point out, even if only in a footnote,
that CALL-NEXT-METHOD is the right thing, and putting an :AROUND
method would be wrong. I know you don't have :AROUND's in CLOSETTE;
but end users so often make the mistake of sledge-hammering in an
:AROUND method when all they really needed was a specialized method
that called CALL-NEXT-METHOD. This just seemed like a good place
to point out some examples of good style.
-- page 103 mentions that "a class C1 can be a subclass of C2 without
requiring that (CLASS-OF C1) be a subclass of (CLASS-OF C2), and
conversely." A meaningful example might be good here. For example,
in most real CLOS implementations, you would expect class FUNCTION
to be of BUILT-IN-CLASS, but its subclass STANDARD-GENERIC-FUNCTION
to be of another, incomparable metaclass. This seems to be a tough
point for many otherwise well-read but CLOS-naive people to understand,
since in SmallTalk the metaclass hierarchy is (I guess) just a dual of
the regular class hierarchy. This would also be a good place to
mention the relevancy of a concept like VALIDATE-SUPERCLASS.
. . . . . . . . . . . . . . . . . . . .
Well, that's about enough for now. High order bit? Its a net win!
Keep at it.
-- Jon --