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

user interface macros



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
different idea about processing method bodies but it's not ready to
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)
		  (OPTION (EQL :READER))
		  (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)
				    (EQ (SECOND OPTION-FORM) ':READERS))
			   (RETURN VALUE-FORM)))))
	  (COND (READERS
		 (SETF (CDR (LAST (SECOND READERS))) `(,VALUE))
		 (VALUES CANONIC-CLASS-SPECIFICATION
			 CANONIC-SLOT-SPECIFICATION))
		(T
		 (VALUES CANONIC-CLASS-SPECIFICATION
			 `(':READERS
			   '(,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.