[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
user interface macros
- To: Gregor J. Kiczales <gregor@parc.xerox.com>
- Subject: user interface macros
- From: Cyphers@STONY-BROOK.SCRC.Symbolics.COM, Moon@STONY-BROOK.SCRC.Symbolics.COM
- Date: Fri, 27 Apr 90 17:58 EDT
- Cc: mop.pa@Xerox.COM
- In-reply-to: <19900424043758.2.GREGOR@SPIFF.parc.xerox.com>
- Line-fold: No
- Sender: Moon@STONY-BROOK.SCRC.Symbolics.COM
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 ....