[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Comments
- To: gregor@parc.xerox.com
- Subject: Comments
- From: Patrick Dussud <dussud@lucid.com>
- Date: Thu, 26 Apr 90 10:22:20 PDT
- Cc: bobrow@parc.xerox.com, Dussud@lucid.com, Gray@lucid.com, JonL@lucid.com, Moon@Stony-Brook.SCRC.Symbolics.com, Cyphers@Stony-Brook.SCRC.Symbolics.com, jkf@franz.com, paepcke@hplap.hpl.hp.com, jutta@ztivax.siemens.com, rivieres@parc.xerox.com, rpg@lucid.com
- Resent-date: Thu, 26 Apr 90 17:35 PDT
- Resent-from: Gregor J. Kiczales <gregor@parc.xerox.com>
- Resent-message-id: <19900427003502.3.GREGOR@SPIFF.parc.xerox.com>
- Resent-to: mop-archive@arisia.Xerox.COM
Typo on page 3-8: setf -> eql
It seems to me that the processing of the method lambda-list (unspecialize,
extract specializers....) should be done in method(s) specialized on the
generic function object. I allows for experimenting with new specializations
of method parameters without changing the defgeneric and defmethod macros.
Patrick.
% -*- Mode: TeX -*-
\input macros
% Here are some additional macros I use:
\def\boxit#1{\vbox{\hrule\hbox{\vrule\kern3pt\vbox{\kern3pt#1\kern3pt}\kern3pt\vrule}\hrule}}
\def\Defmethp
#1 #2{{\let\vtop=\Vtop\bf #1 {\tt #2}\hfill\brac{\it {\tt progn} Method\/}}
\Vskip\normalparskip!}
\def\method#1#2{{\bit method} {\bf #1 (#2)}} %\def\method#1#2{method {\bf #1 (#2)}}
\def\bmethod#1#2{{\bit :before method} {\bf #1 (#2)}}
\def\shortmethod#1#2
{\vskip\parskip\hbox to\hsize{\hskip\leftskip{\bf #1 {\tt (#2)}}
\hfill\brac{\it Primary Method\/}}}
\def\shortaftermethod#1#2
{\vskip\parskip\hbox to\hsize{\hskip\leftskip{\bf #1 {\tt :after (#2)}}
\hfill\brac{\it After Method\/}}}
% Wrap this around a collection of \shortxxx to keep them closer together.
\def\beginShorts{\begingroup\vskip\parskip\parskip=0pt}
\def\endShorts{\endgroup}
\def\today{\ifcase\month\or January\or Febuary\or March\or April\or May\or
June\or July\or August\ September \or October \or November\or December\fi
\space\number\day, \number\year}
\def\beginfncom#1{\begincom{#1}\ftype{Function}}
\def\begingfcom#1{\begincom{#1}\ftype{Standard Generic Function}}
\def\reserved-packages{{\bf common-lisp}, {\bf common-lisp-user}, or {\bf keyword}}
% End of special MOP macros.
\draftremark{\hbox{\vbox{\hbox{\prmeleven }
\line{\prmeleven Draft 11: \today \hfil Do Not Distribute}}}}
\tolerance=2500
\def\bookline{\CLOS\ Specification}
\def\chapline{Metaobject Protocol}
\def\newpage{\vfill\eject}
\beginChapter 3.{Common Lisp Object System Specification}%
{Metaobject Protocol}{Metaobject Protocol}
Authors: Gregor Kiczales and Daniel G. Bobrow
{\ifdraft Draft Dated: \today \hfil\break\fi}
All Rights Reserved
Do not reproduce or redistribute copies of this draft without the permission
of the authors.
The authors wish to thank <<put the right people here>>.
\endTitlePage
\beginSection{A section name}
\beginSubsection{Expansion of the User Interface Macros}
A list in which the first element is one of the symbols {\bf defclass},
{\bf defmethod}, {\bf defgeneric}, {\bf define-method-combination}, {\bf
generic-function}, {\bf generic-flet} or {\bf generic-labels}; and which
has proper syntax for that macro is called a {\bit user interface macro
form}. For convenience, when the meaning is unambiguous, this terminology
is often abbreviated. A user interface macro form may be called a macro
form or, for example, a {\bf defclass} user interface macro form may be
called a {\bf defclass} form.
User interface macro forms can be {\bit evaluated}, {\bit compiled}, or a
compiled macro form can be {\bit executed}. The effect of evaluating or
executing a user interface macro form is specified in terms of calls to
specified functions and generic functions which actually provide the
behavior of the macro. The arguments received by these functions and
generic functions are derived in a specified way from the macro form. Only
minimal aspects of the effect of compiling user interface macro forms are
specified.
Converting a user interface macro form into the arguments to the
appropriate functions and generic functions involves two major issues. The
first is the conversion of the macro argument syntax into a form more
suitable for later processing. The second is the processing of macro
arguments which are forms to be evaluated (including method bodies).
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 done by building
a closure of zero arguments which can be called to evaluate the form in its
lexical environment.
In the syntax of the {\bf defmethod} and {\bf defgeneric} macros the {\it
form*} arguments are lists of forms which comprise the body of the method
definitions. This list of forms must be processed specially to capture the
lexical scope of the macro form. In addition, the lexical functions
available only in the body of methods must be introduced. To allow this
and any other special processing (such as slot access optimization), a
specializable protocol is used for the body of methods. This is discussed
in the section ``Processing the Bodies of Methods''.
\beginsubsubsection{Compiler Processing of the User Interface Macros}
It is common practice for the compiler, while processing a file or set of
files, to maintain information about the definitions that have been
compiled so far. Among other things, this makes it possible to ensure that
a global macro definition ({\bf defmacro} form) which appears in a file
will affect uses of the macro later in that file. This information about
the state of the compilation is called the {\bit compile-file environment}.
When compiling files containing CLOS definitions, it is useful to maintain
certain additional information in the compile-file environment. This can
make it possible to issue various kinds of style warnings (e.g. lambda list
congruence) and to do various performance optimizations that would not
otherwise be possible.
At this time, there is such significant variance in the way existing Common
Lisp implementations handle compile-file environments that it would be
premature to specify this mechanism. Consequently, this document provides
only a minimal description of the compiler processing of the user-interface
macros. Specific implementations are free to provide additional definition
of their compile-file environment mechanism.
This document defines just enough of the compiler processing of the user
interface macros to allow portable metaobject classes to determine whether
instances are being created to represent definitions in the compile file
environment. In such cases, the values of other keyword and initialization
arguments may differ from the specified values so portable code must test
for such cases to prevent encountering unexpected values.
Testing for these cases is possible because compiler processing of the
user-interface macros calls the functions {\bf ensure-class} and {\bf
ensure-generic-function} with a non-null value for the {\bf :environment}
keyword argument. In addition, when {\bf make-instance} is called to
create a metaobject in the compile-file environment, the {\bf :environment}
initialization argument will have a non-null value.
In general, whether and when portable metaobject classes will be
instantiated to represent definitions in the compile file environment is
not defined. The only exception is that the function {\bf
ensure-generic-function} will be called and generic function metaobjects
will be created during compiler processing of {\bf defmethod} and {\bf
defgeneric} macros. This is discussed in the section ``Processing Method
Bodies''.
\endsubsubsection%{Compiler Processing of the User Interface Macros}
\beginsubsubsection{The defclass Macro}
The evaluation or execution of a {\bf defclass} form results in a call to
the {\bf ensure-class} function. The arguments received by {\bf
ensure-class} are derived from the {\bf defclass} form in a defined way.
The exact macroexpansion of the {\bf defclass} form is not defined, only
the relationship between the arguments to the {\bf defclass} macro and the
arguments received by the {\bf ensure-class} function.
\beginlist
\item{\bull} The {\it name} argument to {\bf defclass} becomes the {\it
name} (first required) argument to {\bf ensure-class}. This is the only
positional argument accepted by {\bf ensure-class}, all other arguments are
keyword arguments.
\item{\bull} The {\it direct superclasses} argument to {\bf defclass}
becomes the value of the {\bf :direct-superclasses} keyword argument to
{\bf ensure-class}.
\item{\bull} The {\it direct slots} argument to {\bf defclass} becomes the
value of the {\bf :direct-slots} keyword argument to {\bf ensure-class}.
Special processing of this value is done to regularize the form of each
slot specification and to properly capture the lexical scope of the
initforms. This is done by converting each slot specification to a
property list called a {\bit canonicalized slot specification}. The
resulting list of canonicalized slot specifications is the value of the
{\bf :direct-slots} keyword argument. Each canonicalized slot
specification is formed from the corresponding slot specification as
follows:
\beginNote Canonicalized slot specifications are called a property list and
strictly speaking they are. Later, when the canonicalized slot
specification is used it will be used as keyword arguments and values to a
generic function which will, in turn, pass it to {\bf make-instance} for
use as a set of initialization arguments and values.
\endNote
\itemitem{\bull} The name of the slot is the value of the {\bf :name}
property.
\itemitem{\bull} When the {\bf :initform} slot option is present in the slot
specification, then both the {\bf :initform} and {\bf :initfunction}
properties are present in the canonicalized slot specification. The value
of the {\bf :initform} property is the initform. The value of the {\bf
:initfunction} property is a closure of zero arguments which, when called,
evaluates the initform in its proper lexical environment.
\itemitem{\bull} The value of the {\bf :initargs} property is a list of the
values of each {\bf :initarg} slot option.
\itemitem{\bull} The value of the {\bf :readers} property is a list
of the values of each {\bf :reader} and {\bf :accessor} slot option.
\itemitem{\bull} The value of the {\bf :writers} property is a list of the
values specified by each {\bf :writer} and {\bf :accessor} slot option.
The value specified by a {\bf :writer} slot option is just the value of the
slot option. The value specified by an {\bf :accessor} slot option is two
element list: the first element is the symbol {\bf setf}, the second
element is the value of the slot option.
\itemitem{\bull} All other slot options appear as the values of properties
with the same name as the slot option. Note that this includes not only
the remaining specified slot options ({\bf :allocation} and {\bf :type}),
but also any other options and values appearing in the slot specification.
If one of these slot options appears more than once, the value of the
property will be a list of the specified values.
\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.
\item{\bull} The {\it default initargs} class option, if it is present in
the {\bf defclass} form, becomes the value of the {\bf :default-initargs}
keyword argument to {\bf ensure-class}. Special processing of this value
is done to properly capture the lexical scope of the default value forms.
This is done by converting each default initarg in the class option into a
{\bit canonicalized default initarg}. The resulting list of canonicalized
default initargs is the value of the {\bf :default-initargs} keyword
argument to {\bf ensure-class}.
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.
\item{\bull} Any other class options become the value of keyword arguments
with the same name. The value of the keyword argument is the tail of the
class option. If any class option appears more than once in the {\bf
defclass} form an error is signalled.
\endlist
The result of the call to {\bf ensure-class} is returned as the result of
evaluating or executing the {\bf defclass} form.
Examples of typical {\bf defclass} forms and expansions are shown in
figures <defclass-1> and <defclass-2>. Note that these are only sample
expansions, what is defined is not the form of the expansion, but rather
the arguments received by {\bf ensure-class}.
\boxfig{
\vskip-.14in
\screen!
(defclass plane (moving-object graphics-object)
((altitude :initform 0 :accessor plane-altitude)
(speed))
(:default-initargs :engine *jet*))
(eval-when (:load-toplevel :execute)
(ensure-class plane
:direct-superclasses '(moving-object graphics-object)
:direct-slots (list (list ':name 'altitude
':initform '0
':initfunction #'(lambda () 0)
':readers '(plane-altitude)
':writers '((setf plane-altitude)))
(list ':name 'speed))
:default-initargs (list (list ':engine #'(lambda () *jet*) '*jet*))))
\endscreen!
}
\caption{A defclass form with standard slot and class options. Also shown
is one possible correct expansion.}
\endfig
\boxfig{
\vskip-.14in
\screen!
(defclass sst (plane)
((mach :mag-step 2
:locator sst-mach :locator mach-location
:reader mach-speed :reader mach))
(:metaclass faster-class)
(:another-option foo bar))
(eval-when (:load-toplevel :execute)
(ensure-class 'sst
:direct-superclasses '(plane)
:direct-slots (list (list ':name 'mach
':mag-step '2
':locator '(sst-mach mach-location)
':readers '(mach mach-speed)))
:metaclass 'faster-class
:another-option '(foo bar)))
\endscreen!
}
\caption{A defclass form with non-standard class and slot options. Also
shown is one possible correct expansion.}
\endfig
\endsubsubsection%{The defclass Macro}
\beginsubsubsection{The defmethod Macro}
The evaluation or execution of a {\bf defmethod} form requires first that
the body of the method be converted to a method function. This process is
described in the section ``Processing of Method Bodies''. The result of
this process is a method function and a set of additional initialization
arguments to be used when creating the new method. Given these, the
evaluation or execution of a {\bf defmethod} form proceeds in three steps.
The first step ensures the existence of a generic function with the
specified name. This is done by calling the function {\bf
ensure-generic-function}. The first argument in this call is the generic
function name specified in the {\bf defmethod} form. The {\bf
:lambda-list} keyword argument is supplied in the call, its value is the
lambda list of the method reduced to a generic function lambda list.
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 initialization arguments received by the call to {\bf make-instance}
are as follows:
\beginlist
\item{\bull} The value of the {\bf :qualifiers} initarg is a list of the
qualifiers which appeared in the {\bf defmethod} form. No special
processing is done on these values.
\item{\bull} The value of the {\bf :lambda-list} initarg is the
unspecialized lambda list for the method.
\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.
\item{\bull} The value of the {\bf :function} initarg is the method
function.
\item{\bull} Any other initargs produced in conjunction with the method
function are also included.
\item{\bull} The implementation is free to include additional
initialization arguments provided these are not symbols in the
\reserved-packages packages.
\endlist
In the third step, {\bf add-method} is called to add the newly created
method to the set of methods associated with the generic function
metaobject.
The result of the call to {\bf add-method} is returned as the result of
evaluating or executing the {\bf defmethod} form.
An example showing a typical {\bf defmethod} form and its expansion is
shown in figure <defmethod-1>. The processing of the method body for this
method is shown in figure <method-body-1>.
\boxfig{
\vskip-.14in
\screen!
(defmethod move :before ((p position) (l (eql 0)) &optional (visiblyp t) &key color)
(set-to-origin p)
(when visiblyp (show-move p 0 color)))
(eval-when (:load-toplevel :execute)
(let ((#:g001 (ensure-generic-function 'move :lambda-list '(p l))))
(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))))
\endscreen!
}
\caption{A sample defmethod form and one possible correct expansion. In
the expansion, <method-lambda> is the result of calling make-method-lambda
as described in the section ``Processing Method Bodies''. The initargs
after :function are the additional initargs returned from the call to
make-method-lambda.}
\endfig
\endsubsubsection%{The defmethod Macro}
\beginsubsubsection{Processing Method Bodies}
The body of a {\bf defmethod} macro or {\bf :method} argument to a {\bf
defgeneric} macro is a list of forms. Before a method can be created,
this list of forms must be converted to a method function. This
conversion is a two step process.
The first step occurs during macroexpansion of the macro form. In this
step, the method body is converted to a lambda expression called a {\bit
method lambda}. This conversion is based on information associated with
the generic function definition in effect at the time the macro form is
expanded.
For {\bf defmethod} forms, the generic function definition is obtained by
calling {\bf ensure-generic-function} with a first argument of the generic
function name specified in the macro form. The {\bf :lambda-list} keyword
argument is not passed in this call. If the {\bf defmethod} form is being
expanded by the file compiler, so that it can be executed later, the call
also includes a non-null value for the {\bf :environment} keyword argument.
For {\bf defgeneric} forms, the generic function definition is obtained by
calling {\bf ensure-generic function} with arguments derived from the macro
form as described in the section ``The defgeneric Macro'' except that the
{\bf :initial-methods} keyword argument is not included. If the {\bf
defgeneric} form is being expanded by the file compiler, so that it can be
executed later, the call to {\bf ensure-generic-function} also includes a
non-null value for the {\bf :environment} keyword argument.
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 method lambda list, the
declarations and the method body.
The generic function {\bf make-method-lambda} returns two arguments. The
first is the method lambda itself. The second is a list of initialization
arguments and values. These will be included in the initialization
arguments when the method is created. Note that because these
initialization arguments appear in the expansion of the macro form, and
because this can happen as part of a file compilation, all of the
initialization argument values must have applicable methods for the generic
function {\bf make-load-form}.
In the second step, the method lambda is converted to a closure which
properly captures the lexical scope of the macro form. This is done by
having the method lambda appear in the macroexpansion as the argument of
the {\bf function} special form. When the expansion is actually evaluated
or executed the result of this special form is the method function.
\endsubsubsection%{Processing Method Bodies}
\endSubsection%{Expansion of the User Interface Macros}
\endSection%{A section name}
\endChapter
\bye