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

user interface macros



This is just the first installment of comments, another message to fill
in the parts that have .... here will follow next week (probably
Monday).

Comments from Moon and Cyphers on CLOS user interface macros section.
Indented text is from the TeX source, to provide context.

Issues sorted by category:

>>> Typos and omissions, wording problems

     User interface macro forms can be {\bit evaluated}, {\bit compiled}, or a
     compiled macro form can be {\bit executed}.  

This sentence is a bit confusing.  I think it meant to say
"User interface macro forms can be {\bit evaluated} or {\bit compiled}
and {\bit executed}."  Since that is true of all macros, I'm not really
sure why it needs to be said at all.

     In the syntax of the {\bf defclass} macro, the {\it initform} and {\it
     default-initarg-initial-value-form} arguments are forms which will be
     evaluated one or more times after the macro form is evaluated or executed.
     Special processing must be done on these arguments to ensure that the
     lexical scope of the forms is captured properly.  This is 

"is" --> "can be"
							       
							       done by building
     a closure of zero arguments which can be called to evaluate the form in its
     lexical environment.
     
Everywhere this says "closure" it should say "function."  "Closure" is not a
real Common Lisp word.  In implementations that distinguish closures from bare
functions, this value is allowed to be a bare function if that gives the
specified semantics.  All that matters is you can funcall this thing with no
arguments and it returns the proper value.

     (eval-when (:load-toplevel :execute)

This eval-when is pointless since "load eval" is the default.

Forgot to mention :documentation in the initialization arguments in the
expansion of defmethod.

	 (add-method #:g001
		     (make-instance (generic-function-default-method-class #:g001)
				    ':qualifiers '(:before)
				    ':specializers (list 'position (list 'eql 0))
				    ':lambda-list '(p l &optional (visiblyp t) &key color)
				    ':function (function <method-lambda>)
				    ':additional-initarg-1 't
				    ':additional-initarg-2 '39

These additional initargs aren't allowed to be in the keyword package.

     Given the generic function, production of the method lambda proceeds by
     calling {\bf make-method-lambda}.  The first argument in this call is the
     generic function obtained as described above.  The second argument is the
     result of calling {\bf class-prototype} on the result of calling {\bf
     generic-function-default-method-class} on the generic function.  The third
     argument is a lambda expression formed from the 

Insert "unspecialized"

						     method lambda list, the
     declarations and the method body.

Is the documentation included here (it isn't needed since documentation
is an initialization argument for the method).


>>> Inconsistency with X3J13
     
     \itemitem{\bull} The implementation is free to add additional keyword
     arguments and values to the canonicalized slot specification provided that
     these are not symbols in the \reserved-packages packages.

This is bound up with an attempt to make things extensible that does
not work.  That's discussed more below.  Here I just want to discuss
the \reserved-packages packages stuff.

Everywhere this document refers to symbols hidden from the user, it says it
wrong.  The correct wording derives from this paragraph of
PACKAGE-CLUTTER:REDUCE version 7:

  No external symbols of the LISP package may have properties with
  property indicators that are either external symbols of packages
  defined in the standard or are otherwise accessible in the USER
  package.

Also there's another mistake, it restricts both the argument name and
value when it should only be restricting the argument name.  The word
"property name" (from CLtL p.24) is the correct word to refer to
the even-numbered elements of a canonicalized slot specification.

So the correct wording here is:

  The implementation is free to add additional properties to the 
  canonicalized slot specification provided that the property names
  are not accessible in the common-lisp-user package and are not
  exported by any package defined in the Common Lisp standard.

If you want you can list explicitly the packages defined in the Common
Lisp standard but I don't see the point of that.

This also applies to this text under defmethod:

     \item{\bull} The implementation is free to include additional
     initialization arguments provided these are not symbols in the
     \reserved-packages packages.


>>> Inconsistency with earlier decisions that may not have been written down

When defmethod calls ensure-generic-function, it must not supply the
:lambda-list argument.  ensure-generic-function has to know the difference
between a call from defgeneric (which always replaces the lambda-list),
and a call from defmethod (which never replaces the lambda-list).
Symbolics CLOS works as outlined in this message:

   Date: Tue, 26 Sep 89 17:58 PDT
   From: Gregor.pa@Xerox.COM
   Subject: Re: Expansions of CLOS defining macros
   To: David A. Moon <Moon@STONY-BROOK.SCRC.Symbolics.COM>, Scott Cyphers
    <Cyphers@JASPER.SCRC.Symbolics.COM>
   cc: GSB@STONY-BROOK.SCRC.Symbolics.COM
   Message-ID: <19890927005844.2.GREGOR@SPIFF.parc.xerox.com>
   
   Do we get the right behavior by having the existing methods enfore the
   following?  (For purposes of this imagine that a generic function has
   three properties:  its state, its lambda list and its congruence)
   
     make-instance of a generic function with no :lambda-list initarg
   creates a generic function in state `UNSET-CONGRUENCE', that has no
   lambda list or congruence.
   
     make-instance of a generic function with a :lambda-list creates one in
   state `SET-CONGRUENCE', the lambda list and congruence are taken from
   the initarg
   
     add-method to a generic function in state UNSET-CONGRUENCE does no
   congruence checking and changes its state to `METHODS', it also sets the
   lambda list and congruence from the method.  (unless the lambda-list is
   already set, and congruent with the method, in which case the lambda
   list keeps its value -- see remove-method to understand this)
   
     add-method to a generic function in state SET-CONGRUENCE does the
   specified congruence checking (and may signal an error) and changes its
   state to `METHODS', the congruence and lambda list are not changed
   
     if remove-method removes the last method of a generic function, it
   changes the state to UNSET-CONGRUENCE, the congruence and lambda list
   are unaffected.
   
     reinitialize-instance on a generic function in state SET-CONGRUENCE or
   UNSET-CONGRUENCE will gladly change the generic functions lambda list
   and congruence and leave the generic function in state SET-CONGRUENCE
   
     reinitialize-instance on a generic-function in state METHODS will only
   change the lambda list if it agrees with the generic functions
   congruence.

   Date: Wed, 27 Sep 89 11:47 PDT
   From: Gregor.pa@Xerox.COM
   Subject: Re: Expansions of CLOS defining macros
   To: Scott Cyphers <Cyphers@JASPER.SCRC.Symbolics.COM>
   cc: Moon@STONY-BROOK.SCRC.Symbolics.COM, GSB@STONY-BROOK.SCRC.Symbolics.COM
   Message-ID: <19890927184723.4.GREGOR@SPIFF.parc.xerox.com>
   
       So :lambda-list becomes an optional initarg (89-003 page 3-65 says it's
       required, so this would need to be changed if it hasn't been already).
   
   Yes, that is right.
   
       This isn't quite -- If the generic function was defgeneric'd, adding and
       then removing a method shouldn't allow methods to be added which aren't
       congruent to the lambda list in the defgeneric.  See below.
   
   Yes.
   
       I think we need two states, METHODS, which is just
       GENERIC-FUNCTION-METHODS, and LAMBDA-LIST-SUPPLIED-P.  Your states would
       be SET-CONGRUENCE is (and LAMBDA-LIST-SUPPLIED-P (not METHODS)) and
       UNSET-CONGRUENCE is (not (or LAMBDA-LIST-SUPPLIED-P METHODS)).
   
       Remove-method doesn't need to do anything special.  add-method sets the
       lambda-list if (not (or LAMBDA-LIST-SUPPLIED-P METHODS)), and checks for
       congruence otherwise (which is just what you said).
   
   OK.


       (let ((#:g001 (ensure-generic-function 'move :lambda-list '(p l))))
	 (add-method #:g001

:LAMBDA-LIST shouldn't be here!.  It is the wrong value too.


>>> Unmotivated incompatibilities with 89-003

In the arguments to ENSURE-CLASS, this document has :DIRECT-SUPERCLASSES and
:DIRECT-SLOTS where 89-003 has :SUPERCLASSES and :SLOTS.  Symbolics CLOS uses
:DIRECT-SUPERCLASSES (based on mail with Gregor a few months ago) but :SLOTS.
For consistency, :DIRECT-SLOTS would be better, but we've passed the point of
being able to remove :SLOTS, although we could make :SLOTS mean :DIRECT-SLOTS.

Similarly, :DIRECT-DEFAULT-INITARGS should replace :DEFAULT-INITARGS.
Although no document and no implementation uses :DIRECT-DEFAULT-INITARGS
currently, it would be more consistent.
     
     A canonicalized default initarg is a list of three elements.  The first
     element is the name; the second is a closure of zero arguments which, when
     called, evaluates the default value form in its proper lexical
     environment; and the third is the actual form itself.
     
Symbolics CLOS and 89-003 put the form before the function.  Why was the order
gratuitously changed?

Also, there needs to be provision for implementation additions (we use one),
just as in the canonicalized slot specification.  Just allow the list to have
more than three elements, where elements after the first three are
implementation dependent.

     The second step is the creation of the new method metaobject by calling
     {\bf make-instance}.  The class of the new method metaobject is determined
     by calling {\bf generic-function-default-method-class} on the result of the
     call to {\bf ensure-generic-function} from the first step.

The name in 89-003 and in Symbolics CLOS is generic-function-method-class.
Why the gratuitous change?
     
     \item{\bull} The value of the {\bf :specializers} initarg is a list of the
     specializer names for the method.  For {\bf eql} specializers, this is a
     list in which the first element is the symbol {\bf setf} and the second
     element is the result of evaluating the eql specializer form in the lexical
     environment of the {\bf defmethod} form.  For any other kind of
     specializer, this is the value from the {\bf defmethod} form with no
     special processing done.
     
It's too weird to make this a list of elements that are parameter specializers
in one case and parameter specializer names in the other case.  89-003 p.3-69
requires parameter specializers here, i.e. classes rather than class names.
See also 89-003 p.3-16.  So not only is this weird, it's also a gratuitous
incompatibility.

Note that when a parameter specializer is a class, the class can be required
to exist when the defmethod is executed since a forward-referenced-class could
always be used.  In Symbolics CLOS the class must actually be defined when the
defmethod is executed, which seems reasonable to me.  I couldn't find any
discussion of this in any relevant-looking X3J13 cleanup issue.


>>> Attempts to add extensibility that don't work

.... to be written ....
.... will cover class-options, slot-options, the issue of options that can
     occur multiple times, and method-body processing ....


>>> Other differences from Symbolics CLOS implementation

     \item{\bull} The {\it direct superclasses} argument to {\bf defclass}
     becomes the value of the {\bf :direct-superclasses} keyword argument to
     {\bf ensure-class}.

We do this differently.  If the {\it direct superclasses} is NIL, then the
metaclass is asked for the list of direct superclasses to be used, using
class-default-direct-superclasses.  For STANDARD-CLASS, (STANDARD-OBJECT) is
used, for FUNCALLABLE-STANDARD-CLASS, (FUNCALLABLE-INSTANCE), etc.  Thus
ENSURE-CLASS receives the actual list of direct superclasses.  In our way of
defaulting these, it is possible for a metaclass to make classes which have no
superclasses.  The result of class-default-direct-superclasses is a list of
class objects even though the :direct-superclasses is otherwise a list of
class names; this might be a mistake, but does allow for anonymous classes
as default direct superclasses.


>>> Proposed different ideas

.... class parsing ....
.... method parsing ....
.... this might be the right place to object to NULL as the local environment test ....
.... environment reducer function ....