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

Comments on AMOP



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