# 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: Tue, 1 May 90 15:59 EDT
• Cc: mop.pa@Xerox.COM
• Line-fold: No
• Sender: Moon@STONY-BROOK.SCRC.Symbolics.COM

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

This is part 2 of our comments.  Part 1 was mailed last Friday.
Part 3, the final part, will be about Processing Method Bodies.

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

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.

Defining NULL to be the predicate that tests whether an environment is the
run-time environment is not very abstract.  This could run into serious
problems with future extensions that unify this environment with the
&environment argument to macros or that implement more than just the run-time
and compilation environments.  On the one hand, NIL might not be the only
representation for the "ordinary" environment, and on the other hand, there
might be several distinct environments that all have the property of
containing classes that can be instantiated and methods that can be executed.

I think we must choose either not to document any predicates on the
environment, and require portable metaobject classes to operate some other
way, or else to add one or more predicates that perform the specific tests on
environments that are actually needed.  I was unable to figure out what
specific predicates are needed, nor what the specific problem is that needs to
be solved (alluded to in "testing for these cases" above).  As a strawman,
I'll suggest (RUN-TIME-ENVIRONMENT-P env) => true if env is an environment
that causes FIND-CLASS to return instantiable classes and causes
ENSURE-GENERIC-FUNCTION to return callable generic functions.

The term "run time environment" is from section 4.2 of the draft ANSI CL
specification, revision 7.31 of 8/29/89.

\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.

This is not workable for several reasons.  First, it is unclear whether the
value of the property is the value of the slot-option, or a list whose single
element is the value of the slot-option, in the case of a slot-option that is
allowed to appear more than once but appears only once in this particular slot
specification.  Second, there might be slot-options that, like :INITFORM,
require processing such as capturing of the lexical environment.  Third, there
might be slot-options that, like :INITFORM and :ACCESSOR, must expand into
multiple properties in the canonicalized slot specification.  Fourth, there
might be slot-options that, like :ACCESSOR and :WRITER, interact with each
other, both contributing to a single property in the canonicalized slot
specification.

I think the only possible choice is to document the canonicalized slot
specification format for every standard slot option, and to allow extended
slot options (added either by an implementation or by a user) complete freedom
in how they appear in the canonicalized slot specification, restricted only by
what property names can be used.  The appropriate generalization to extended
slot options of the property name restrictions given for the standard slot
options appears to be the following:  The properties in the canonicalized slot
specification must be named by either the symbol that names a related slot
option, if the slot option can appear only once; or the symbol that is the
plural form of the name of a related slot option, in the same home package and
with the same export status, if the slot option can appear multiple times; or
a symbol that is not accessible in the common-lisp-user package and is not
exported by any package defined in the Common Lisp standard.

If this pluralization rule is not adopted, then :READERS and :WRITERS should
be renamed to :READER and :WRITER; but that would be both confusing and an
incompatible change for implementations that followed 89-003.

See below for a proposal for how users can add extended slot options and have
them processed into canonicalized slot specifications by the DEFCLASS macro.

\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.

This has much the same problem as noted for slot options.  Also the part about
"the tail of the class option" disagrees with figure 3-2 and our
implementation for :metaclass and :documentation.  There might be extended
class options that can appear multiple times and/or that require processing
such as capturing of the lexical environment.  I think that :metaclass and
:documentation should be canonicalized into just the class name or
documentation string, not a list of it, and that extended class options
should follow rules similar to the rules for extended slot options.

See below for a proposal for how users can add extended class options and have
them processed into ENSURE-CLASS arguments by the DEFCLASS macro.

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.

This has a subtle bug.  Suppose the defgeneric form is -not- being expanded by
the file compiler, the generic function has been defined before with
initial-methods, and the lambda-list is being changed incongruently.  The new
lambda-list will be installed before the new initial-methods are installed,
causing a spurious congruency error.  There could also be inconsistency
between the old initial-methods and the new method-combination.  Also there
could be difficulties for programming environments, because if compiling a
method body provokes an error and the execution of the defgeneric is aborted,
the generic function will already have been partially redefined.  It would be
better to process the method bodies with a generic function instance that has
not been installed as the actual definition of the generic function, and only
modify the actual definition of the generic function after the initial-methods
are available.  In Genera, we use the class prototype of the generic function
class as the generic function for processing the method bodies.  Perhaps it
would be better to make a new generic function instance with all the
initialization arguments except the :initial-methods, but to make it with
make-instance rather than ensure-generic-function so that it is not installed
as the definition of its name.  This would be true both in the file compiler
and in ordinary macro expansion.

>>> Proposed different ideas

The rest of this message describes one possible way that the DEFCLASS
macro could be made extensible by metaclasses, allowing new class options
and new slot options to be added by users.  There is also a proposed
mail yet.

Processing of a DEFCLASS form:

A DEFCLASS form is converted into two ENSURE-CLASS forms; one is
evaluated at compile-file time and the other is evaluated at load time.
The compile-file version is supplied with the :ENVIRONMENT keyword with
a value having indefinite extent which reflects the "compile-fileness"
of the environment of the DEFCLASS macro.  The load-time version does
not supply the environment keyword.

Most of the conversion from a DEFCLASS form to an ENSURE-CLASS form is
performed by methods specialized on the metaclass.  These methods
generate a canonic class specification.  The canonic class specification
will serve as the keyword and value forms for ENSURE-CLASS.  Note that
the canonic class specification is not a plist; it is a list of forms,
which will be referred to as a forms-plist.  If X is a forms-plist, then
(MAPCAR #'EVAL X) is a plist.  Typically the forms in a forms-plist are
of the form (QUOTE value) or (FUNCTION value), and the examples will
often assume this.

Generic Function: CANONICALIZE-CLASS-OPTION
Arguments:
class -- An instance of the metaclass.
option-name -- A symbol.
option -- A symbol or a list.
canonic-class-specification -- A forms-plist.
environment -- An environment.

Values:
new-canonic-class-specification -- A forms-plist.

Description:
Verifies and interprets one class option.  DEFCLASS processes class
options in the order in which they appear in the DEFCLASS form.  Class
options are processed before superclasses and slots have been processed.
The interpretation of the class option is accomplished by adding or
modifying keywords and values to the returned canonic class
specification.  Methods are permitted to modify the
canonic-class-specification argument, but implementations are not
permitted to depend on this.  All elements of the canonic class
specification should be forms to be evaluated at definition time.

Examples:

;;; Unspecialized method to handle all invalid class options.
(DEFMETHOD CANONICALIZE-CLASS-OPTION
((CLASS STANDARD-CLASS)
(OPTION-NAME T)
(OPTION T)
(CANONIC-CLASS-SPECIFICATION LIST)
(ENVIRONMENT T))
(WARN "~s is not a valid class option.  It will be ignored." OPTION)
CANONIC-CLASS-SPECIFICATION)

(DEFMETHOD CANONICALIZE-CLASS-OPTION
((CLASS STANDARD-CLASS)
(OPTION-NAME (EQL :METACLASS))
(OPTION CONS)
(CANONIC-CLASS-SPECIFICATION LIST)
(ENVIRONMENT T))
(COND ((LOOP FOR (OPTION-FORM VALUE-FORM) ON CANONIC-CLASS-SPECIFICATION
BY #'CDDR
DOING (PROGN VALUE-FORM)
THEREIS (AND (CONSP OPTION-FORM)
(EQ (FIRST OPTION-FORM) 'QUOTE)
(EQ (SECOND OPTION-FORM) OPTION-NAME)))
(WARN "The ~s class option is specified twice.  ~s will be ignored."
OPTION-NAME OPTION)
CANONIC-CLASS-SPECIFICATION)
(T
(',OPTION-NAME (FIND-CLASS ',(SECOND OPTION))
,@CANONIC-CLASS-SPECIFICATION))))

(DEFMETHOD CANONICALIZE-CLASS-OPTION
((CLASS STANDARD-CLASS)
(OPTION-NAME (EQL :DEFAULT-INITARGS))
(OPTION CONS)
(CANONIC-CLASS-SPECIFICATION LIST)
(ENVIRONMENT T))
(COND ((LOOP FOR (OPTION-FORM VALUE-FORM) ON CANONIC-CLASS-SPECIFICATION
BY #'CDDR
DOING (PROGN VALUE-FORM)
THEREIS (AND (CONSP OPTION-FORM)
(EQ (FIRST OPTION-FORM) 'QUOTE)
(EQ (SECOND OPTION-FORM) OPTION-NAME)))
(WARN "The ~s class option is specified twice.  ~s will be ignored."
OPTION-NAME OPTION)
CANONIC-CLASS-SPECIFICATION)
(T
(',OPTION-NAME
(,,@(LOOP FOR (KEYWORD VALUE) ON (CDR OPTION) BY #'CDDR
COLLECT
(,',KEYWORD
,',VALUE
,#'(LAMBDA () ,VALUE))))
,@CANONIC-CLASS-SPECIFICATION))))

Generic Function: CANONICALIZE-SUPERCLASSES
Arguments:
class -- An instance of the metaclass.
superclasses -- The list of metaclass names.
canonic-slot-specification -- A forms-plist.
environment -- An environment.

Values:
new-canonic-class-specification -- A forms-plist.

Description:
Verifies and interprets the class superclasses.
Class options are processed before superclasses.  Slots are
processed after superclasses have been processed.  The interpretation of
the superclasses is accomplished by adding or modifying keywords and
values to the returned canonic class specification.  Methods are
permitted to modify the canonic-class-specification argument, but
implementations are not permitted to depend on this.  All elements of
the canonic class specification should be forms to be evaluated at
definition time.

Examples:

(DEFMETHOD CANONICALIZE-SUPERCLASSES
((CLASS STANDARD-CLASS)
(SUPERCLASSES NULL)
(CANONIC-CLASS-SPECIFICATION LIST)
(ENVIRONMENT T))
(':DIRECT-SUPERCLASSES
'(STANDARD-OBJECT)
,@CANONIC-CLASS-SPECIFICATION))

(DEFMETHOD CANONICALIZE-SUPERCLASSES
((CLASS STANDARD-CLASS)
(SUPERCLASSES CONS)
(CANONIC-CLASS-SPECIFICATION LIST)
(ENVIRONMENT T))
(':DIRECT-SUPERCLASSES ',SUPERCLASSES
,@CANONIC-CLASS-SPECIFICATION))

(DEFMETHOD CANONICALIZE-SUPERCLASSES
((CLASS FUNCALLABLE-STANDARD-CLASS)
(SUPERCLASSES NULL)
(CANONIC-CLASS-SPECIFICATION LIST)
(ENVIRONMENT T))
(':DIRECT-SUPERCLASSES '(FUNCALLABLE-INSTANCE)
,@CANONIC-CLASS-SPECIFICATION))

Generic Function: CANONICALIZE-SLOT-SPECIFICATIONS
Arguments:
class -- An instance of the metaclass.
slot-specifications -- The list of slot specifications appearing
in the DEFCLASS form.
canonic-class-specification -- A forms-plist.
environment -- An environment.
Values:
new-canonic-class-specification -- A forms-plist.

Description:

Verifies and interprets the slot options.  DEFCLASS processes slot
options in the order in which they appear in the DEFCLASS form.  Slot
options are processed after class options and superclasses have been
processed.  The interpretation of the slot options is accomplished by
adding or modifying keywords and values to the returned canonic class
specification.  Methods are permitted to modify the
canonic-class-specification argument, but implementations are not
permitted to depend on this.  All elements of the canonic class
specification should be forms to be evaluated at definition time.

For the benefit of classes in which slot order is significant, the
CANONICALIZE-SLOT-SPECIFICATIONS method specialized on STANDARD-CLASS
puts the canonic slot descriptions in the same order in which they
appear in the slot-specifications argument (which is the same order they
appear in the DEFCLASS form).

Example:

(DEFMETHOD CANONICALIZE-SLOT-SPECIFICATIONS
((CLASS STANDARD-CLASS)
(SLOT-SPECIFICATIONS LIST)
(CANONIC-CLASS-SPECIFICATION LIST)
(ENVIRONMENT T))
(LET ((CANONIC-CLASS-SPECIFICATION CANONIC-CLASS-SPECIFICATION))
(DOLIST (SLOT-SPECIFICATION SLOT-SPECIFICATIONS)
(SETQ CANONIC-CLASS-SPECIFICATION
(CANONICALIZE-SLOT-SPECIFICATION
CLASS
SLOT-SPECIFICATION
CANONIC-CLASS-SPECIFICATION
ENVIRONMENT)))
CANONIC-CLASS-SPECIFICATION))

Generic Function: CANONICALIZE-SLOT-SPECIFICATION
Arguments:
class -- An instance of the metaclass.
slot-specification -- One slot specification appearing
in the DEFCLASS form.
canonic-class-specification -- A forms-plist.
environment -- An environment.
Values:
new-canonic-class-specification -- A forms-plist.

Description:
Verifies and interprets one slot specification.  The interpretation of the
slot specification is accomplished by adding or modifying keywords and
values to the returned canonic class specification.  Methods are permitted
to modify the canonic-class-specification argument, but implementations
are not permitted to depend on this.  All elements of the canonic class
specification should be forms to be evaluated at definition time.

Example:

(DEFMETHOD CANONICALIZE-SLOT-SPECIFICATION
((CLASS STANDARD-CLASS)
(SLOT-SPECIFICATION NULL)
(CANONIC-CLASS-SPECIFICATION LIST)
(ENVIRONMENT T))
(WARN "~s is an invalid slot specification.  It will be ignored."
SLOT-SPECIFICATION)
CANONIC-CLASS-SPECIFICATION)

(DEFMETHOD CANONICALIZE-SLOT-SPECIFICATION
((CLASS STANDARD-CLASS)
(SLOT-SPECIFICATION SYMBOL)
(CANONIC-CLASS-SPECIFICATION LIST)
(ENVIRONMENT T))
(CANONICALIZE-SLOT-SPECIFICATION CLASS (LIST SLOT-SPECIFICATION)
CANONIC-CLASS-SPECIFICATION ENVIRONMENT))

(DEFMETHOD CANONICALIZE-SLOT-SPECIFICATION
((CLASS STANDARD-CLASS)
(SLOT-SPECIFICATION CONS)
(CANONIC-CLASS-SPECIFICATION LIST)
(ENVIRONMENT T))
(LET ((NAME (FIRST SLOT-SPECIFICATION)))
(WHEN (NULL NAME)
(WARN "~s is not a valid name for a slot." NAME)
(RETURN-FROM CANONICALIZE-SLOT-SPECIFICATION CANONIC-CLASS-SPECIFICATION))
;; Check to see if the slot is already defined
(LET ((CANONIC-SLOT-SPECIFICATIONS
(LOOP FOR (OPTION-FORM VALUE-FORM) ON CANONIC-CLASS-SPECIFICATION
BY #'CDDR
DOING
(WHEN (AND (CONSP OPTION-FORM)
(EQ (FIRST OPTION-FORM) 'QUOTE)
(EQ (SECOND OPTION-FORM) ':DIRECT-SLOTS))
(RETURN (REST VALUE-FORM))))))
(DOLIST (CANONIC-SLOT-SPECIFICATION CANONIC-SLOT-SPECIFICATIONS)
(WHEN (EQ (LOOP FOR (OPTION-FORM VALUE-FORM)
ON (REST CANONIC-SLOT-SPECIFICATION)
BY #'CDDR
DOING
(WHEN (AND (CONSP OPTION-FORM)
(EQ (FIRST OPTION-FORM) 'QUOTE)
(EQ (SECOND OPTION-FORM) ':NAME))
(RETURN (SECOND VALUE-FORM))))
NAME)
(WARN "The slot name ~s is used for more than one slot.~@
The second slot will be ignored."
NAME)
(RETURN-FROM CANONICALIZE-SLOT-SPECIFICATION
CANONIC-CLASS-SPECIFICATION)))
(LET ((CANONIC-CLASS-SPECIFICATION CANONIC-CLASS-SPECIFICATION)
(CANONIC-SLOT-SPECIFICATION (':NAME ',NAME)))
(LOOP FOR (OPTION VALUE) ON (REST SLOT-SPECIFICATION) BY #'CDDR
DOING
(MULTIPLE-VALUE-SETQ (CANONIC-CLASS-SPECIFICATION CANONIC-SLOT-SPECIFICATION)
(CANONICALIZE-SLOT-OPTION
CLASS
CANONIC-CLASS-SPECIFICATION
OPTION
VALUE
CANONIC-SLOT-SPECIFICATION
ENVIRONMENT)))
(COND (CANONIC-SLOT-SPECIFICATIONS
;; Add this canonic slot specification to the end of the list
(SETF (CDR (LAST CANONIC-SLOT-SPECIFICATIONS))
((LIST ,@CANONIC-SLOT-SPECIFICATION)))
CANONIC-CLASS-SPECIFICATION)
(T
(':DIRECT-SLOTS (LIST (LIST ,@CANONIC-SLOT-SPECIFICATION))
,@CANONIC-CLASS-SPECIFICATION)))))))

Generic Function: CANONICALIZE-SLOT-OPTION
Arguments:
class -- An instance of the metaclass.
canonic-class-specification -- A forms-plist.
option -- a keyword.
value -- The value of the option.
canonic-slot-specification -- A forms-plist.
environment -- An environment.

Values:
new-canonic-class-specification -- A forms-plist.
new-canonic-slot-specification -- A forms-plist.

Description:
Verifies and interprets one slot option. The interpretation of the slot
option is accomplished by adding or modifying keywords and values to the
returned canonic class specification and/or the returned canonic slot
specification.  Methods are permitted to modify the canonic specification
arguments, but implementations are not permitted to depend on this.  All
elements of the canonic specification arguments should be forms to be
evaluated at definition time.

Examples:

(DEFMETHOD CANONICALIZE-SLOT-OPTION
((CLASS STANDARD-CLASS)
(CANONIC-CLASS-SPECIFICATION LIST)
(OPTION T)
(VALUE T)
(CANONIC-SLOT-SPECIFICATION LIST)
(ENVIRONMENT T))
(WARN "The slot option ~s is invalid" OPTION)
(VALUES CANONIC-CLASS-SPECIFICATION
CANONIC-SLOT-SPECIFICATION))

(DEFMETHOD CANONICALIZE-SLOT-OPTION
((CLASS STANDARD-CLASS)
(CANONIC-CLASS-SPECIFICATION LIST)
(VALUE T)
(CANONIC-SLOT-SPECIFICATION LIST)
(ENVIRONMENT T))
(LET ((READERS (LOOP FOR (OPTION-FORM VALUE-FORM) ON CANONIC-SLOT-SPECIFICATION
BY #'CDDR
DOING
(WHEN (AND (CONSP OPTION-FORM)
(EQ (FIRST OPTION-FORM) 'QUOTE)
(RETURN VALUE-FORM)))))
(SETF (CDR (LAST (SECOND READERS))) (,VALUE))
(VALUES CANONIC-CLASS-SPECIFICATION
CANONIC-SLOT-SPECIFICATION))
(T
(VALUES CANONIC-CLASS-SPECIFICATION
'(,VALUE)
,@CANONIC-SLOT-SPECIFICATION))))))

An example of DEFCLASS implemented using these generic functions is:

(DEFMACRO DEFCLASS (NAME SUPERCLASSES SLOT-SPECIFICATIONS
&REST CLASS-OPTIONS
&ENVIRONMENT ENVIRONMENT)
(LET* ((METACLASS-NAME
(OR (LOOP FOR OPTION IN CLASS-OPTIONS
DOING
(WHEN (AND (CONSP OPTION)
(EQ (FIRST OPTION) ':METACLASS))
(RETURN (SECOND OPTION))))
'STANDARD-CLASS))
(METACLASS (FIND-CLASS METACLASS-NAME T NIL))
(PROTOTYPE (CLASS-PROTOTYPE METACLASS))
(CANONIC-CLASS-SPECIFICATION NIL))
(LOOP FOR CLASS-OPTION IN CLASS-OPTIONS DOING
(SETQ CANONIC-CLASS-SPECIFICATION
(CANONICALIZE-CLASS-OPTION
PROTOTYPE
(IF (ATOM CLASS-OPTION)
CLASS-OPTION
(FIRST CLASS-OPTION))
CLASS-OPTION
CANONIC-CLASS-SPECIFICATION
ENVIRONMENT)))
(SETQ CANONIC-CLASS-SPECIFICATION
(CANONICALIZE-SUPERCLASSES
PROTOTYPE
SUPERCLASSES
CANONIC-CLASS-SPECIFICATION
ENVIRONMENT))
(SETQ CANONIC-CLASS-SPECIFICATION
(CANONICALIZE-SLOT-SPECIFICATIONS
PROTOTYPE
SLOT-SPECIFICATIONS
CANONIC-CLASS-SPECIFICATION
ENVIRONMENT))
(PROGN
(EVAL-WHEN (:COMPILE-FILE)
(ENSURE-CLASS ',NAME
:ENVIRONMENT ,(COMPILE-FILE-ENVIRONMENT ENVIRONMENT)
,@CANONIC-CLASS-SPECIFICATION))
(ENSURE-CLASS ',NAME
,@CANONIC-CLASS-SPECIFICATION))))

The function COMPILE-FILE-ENVIRONMENT takes an environment as an argument
and returns an object with indefinite extent which has the same
COMPILE-FILEness properties as its argument.