[comp.lang.clos] Need help with modifying slot access behavior

kandt@ai-jupiter.JPL.NASA.GOV (Kirk Kandt) (11/30/90)

This concerns 5/22/89 Victoria PCL.  I'm running Lucid 3.0.

I have written code in CLOS to add persistence to objects.  Since
a object can point to other objects (i.e., a slot has another object
as its value) the saving/restoration of an object can involve
the saving/restoration of many objects.  Consequently, I have
decided to restore objects on demand (i.e., when they are first 
referenced) because we are saving/restoring some very large networks.

During restoration the basic strategy is to restore the root object
and then to fill every slot whose value is an object with a 
"forwarding pointer" which specifies how to restore the object. Then,
when a slot is reference which contains a forwarding pointer then
that object is restored.

I have written the following code to test the idea.  I implemented
the "Lazy-Class" metaclass to cause slot access to have a different
behavior.  Namely, that anytime an instance is created whose metaclass
is Lazy-Class any slot assigned the value of 4 will be reassigned
the value 5; in the real situation I would check if the value was a
forwarding pointer and then substitute it with the actual value.

;;; -*- Mode: Lisp; Package: Pcl; Base: 10 -*-

(In-Package 'Pcl)

;;; The Lazy-Class meta-class permits the lazy evaluation of instance
variables.  If a slot is a
;;; forwarding hash-file pointer then the actual data item is read from
a hashfile and written to
;;; the slot.

(Defclass Lazy-Class (Standard-Class)
  ()
  )

(Defun Hash-File-Pointer-P (Object)
  "Return non-nil if object is a hash-file pointer."
  (= Object 4)						    ;(Typep Object 'Hash-file-Pointer)
  )

(Defun Update-Lazy-Slot-Using-Hashfile-Pointer (Instance Slot-Name
Hash-File-Pointer)
  "Update a slot of an instance by reading an item from a hash file. 
Hash-file-pointer specifies
   a file and key."
  (Setf (Slot-Value Instance Slot-Name) 5))

(Defmethod Slot-Value-Using-Class :Around ((Class Lazy-Class) Instance
Slot-Name)
  "This cause all references to a slot to be updated correctly if the
slot contains a
   forwarding hash-file pointer."
  (Let ((Slot-Value (Call-Next-Method Class Instance Slot-Name)))
    (Cond ((Hash-File-Pointer-P Slot-Value)
	   (Update-Lazy-Slot-Using-Hashfile-Pointer Instance Slot-Name Slot-Value))
	  (T Slot-Value))))

(Defclass Kirk ()
  ((X :Initform 0 :Initarg :X :Accessor X))
  (:Metaclass Lazy-Class))

(Setq Kirk (Make-Instance 'Kirk :X 1))

;;; End.

After loading this code the following typescript occurs.

Input: (trace slot-value-using-class)
(SLOT-VALUE-USING-CLASS)
Input: (slot-value kirk 'x)
1 Enter SLOT-VALUE-USING-CLASS #<Lazy-Class KIRK 61066146> #<KIRK 55441206> X
1 Exit SLOT-VALUE-USING-CLASS 1
1
Input: (slot-value kirk 'x)
1 Enter SLOT-VALUE-USING-CLASS #<Lazy-Class KIRK 61066146> #<KIRK 61144766> X
1 Exit SLOT-VALUE-USING-CLASS 1
1
Input: (setf (slot-value kirk 'x) 4)
4
Input: (slot-value kirk 'x)
1 Enter SLOT-VALUE-USING-CLASS #<Lazy-Class KIRK 61066146> #<KIRK 61144766> X
1 Exit SLOT-VALUE-USING-CLASS 5
5
Input: (slot-value kirk 'x)
1 Enter SLOT-VALUE-USING-CLASS #<Lazy-Class KIRK 61066146> #<KIRK 61144766> X
1 Exit SLOT-VALUE-USING-CLASS 5
5
Input: (x kirk)
1 Enter SLOT-VALUE-USING-CLASS #<Lazy-Class KIRK 61066146> #<KIRK 61144766> X
1 Exit SLOT-VALUE-USING-CLASS 5
5
Input: (x kirk)
1 Enter SLOT-VALUE-USING-CLASS #<Lazy-Class KIRK 61066146> #<KIRK 61144766> X
1 Exit SLOT-VALUE-USING-CLASS 5
5
Input: (x kirk)
5
Input: (x kirk)
5
Input: 

Calling SLOT-VALUE always does the right thing; calling the X generic fn
does not.
The question is why does (X KIRK) quit calling SLOT-VALUE-USING-CLASS 
after the second time?  More importantly, how can I make sure that it
always calls SLOT-VALUE-USING-CLASS?  Or, how can I change PCL to 
generate accessors that include the COND in SLOT-VALUE-USING-CLASS :AROUND?

Any help is greatly appreciated.  Please respond to comp.lang.clos or
me directly.  Thank you.

-- Kirk Kandt

Gregor@parc.xerox.com (Gregor Kiczales) (12/01/90)

   Organization: NASA/Jet Propulsion Laboratory
   Date: 	Thu, 29 Nov 1990 13:19:18 PST
   From: kandt%ai-jupiter.uucp%jpl-devvax.uucp@elroy.jpl.nasa.gov (Kirk Kandt)

   This concerns 5/22/89 Victoria PCL.  I'm running Lucid 3.0.

My comments will be with regard to the newer version of PCL.  They
apply, in essence, to older PCLs as well, but it probably isn't worth
the effort to fix both the older and newer PCLs as I suggest.

   (defmethod slot-value-using-class ((class lazy-class) object slot-name) ...)

   (defclass kirk ()
     ((x :initform 0 :initarg :x :accessor x))
     (:metaclass lazy-class))

   (setq kirk (make-instance 'kirk :x 1))

   Calling SLOT-VALUE always does the right thing; calling the X generic fn
   does not.
   The question is why does (X KIRK) quit calling SLOT-VALUE-USING-CLASS
   after the second time?  More importantly, how can I make sure that it
   always calls SLOT-VALUE-USING-CLASS?  Or, how can I change PCL to
   generate accessors that include the COND in SLOT-VALUE-USING-CLASS :AROUND?

There are actually two problems, both caused by stupidity on the part of
the person who wrote PCL.  Your message exhibits only one of them, but
unless I am wrong, the following code would fail as well:

(defmethod foo ((o kirk)) (slot-value o 'x))

That is, it wouldn't end up calling your special method on
SLOT-VALUE-USING-CLASS.

The second problem can be fixed, I believe, by fixing the
PRIMARY-PV-CACHE-MISS function in the file vector.lisp.  The first
problem (the one you demonstrate), that automatically generated
accessors don't call your methods on slot-value-using-class, can be
fixed, I believe, by fixing the function ACCESSOR-MISS-VALUES in the
file dfun.lisp.  The changes you want to make are going to be something
like:

(defun accessor-miss-values (generic-function applicable args)
  (declare (values type index))
  (let ((type
	  (and (eq (generic-function-method-combination generic-function)
		   *standard-method-combination*)
	       (every #'(lambda (m) (null (method-qualifiers m))) applicable)
	       (cond ((standard-reader-method-p (car applicable))
                      (AND (EQUAL (COMPUTE-APPLICABLE-METHODS
                                     #'SLOT-VALUE-USING-CLASS
                                     (CLASS-OF (CAR ARGS))
                                     (CAR ARGS)
                                     (STANDARD-ACCESSOR-METHOD-SLOT-NAME (CAR APPLICABLE)))
                                  (COMPUTE-APPLICABLE-METHODS
                                     #'SLOT-VALUE-USING-CLASS
                                     (FIND-CLASS 'STANDARD-CLASS)
                                     (MAKE-INSTANCE 'STANDARD-OBJECT)
                                     'FOO))
                            'READER))
		     ((standard-writer-method-p (car applicable)) 'writer)
		     (t nil)))))
    (values type
	    (and type
		 (let ((wrapper (wrapper-of (case type
					      (reader (car args))
					      (writer (cadr args)))))
		       (slot-name (accessor-method-slot-name (car applicable))))
		   (or (instance-slot-index wrapper slot-name)
		       (assq slot-name (wrapper-class-slots wrapper))))))))

Except that:

 1) You need to make them for writers too.
 2) The hack of computing the normal applicable methods should be cached
    somehow.
 3) All of this would be easier, and perform better, if you also changed
    PCL to use the new proposed protocol for SLOT-VALUE-USING-CLASS (as
    described in the message below).


I hope someone will decide to make these changes to PCL.  If they do, we
can add them to the sources on arisia.

Gregor

---

From: Gregor Kiczales <Gregor@parc.xerox.com>
To: MOP.PARC@xerox.com
Subject: change to instance structure protocol
Date: 	Thu, 22 Nov 1990 12:59:24 -0800

The current instance structure protocol specifies that the slot access
functions (e.g. SLOT-VALUE) are implemented in terms of slot acccess
generic functions (i.e. SLOT-VALUE-USING-CLASS).  The generic functions
dispatch on the (class of) the class and the (class of) the object.

This protocol has always had a problem, specifically if you want to
specialize access to some but not all of a class's slots, it is a bit
awkward.  You have to write a method on the generic function which first
tests to see if the slot in question is a special one; if it is, special
code is run, otherwise you do a CALL-NEXT-METHOD.

This problem gets even worse when we consider the problem of retaining
the implementations ability to do optimized slot access.  If the user
defines a method on a slot access generic function, the implementation
has to call that method (it must deoptimize slot access); but even if in
fact only some of the class's slots are affected, the performance of the
access to all of them will be affected.  The problem is the same one of
resolution, the current protocol only specializes on the class and the
object, not on individual slots.

The solution we have been using is to have a generic function called
SLOT-DEFINITION-ELIDE-PORTABLE-ACCESS-METHOD-P which allows the to test
whether it can retain its optimization in some cases.  But, this is a
hack, it distinguishes "compiled" and "interpreted" code in a weird way,
and is just plain blecherous.  (For reference, the part of the newest
MOP which describes this is included at the end of this message.)

There is an elegant solution, but I have refrained from proposing it for
some time.  Now, it really seems worth proposing.

The basic idea is to change the last (slot-name) argument to all of the
SLOT-XXX-USING-CLASS generic functions.  The new value would be the
actual effective slot definition metaobject rather than the slot name.
The slot access functions would be specified to lookup the effective
slot definition object using CLASS-SLOTS, SLOT-DEFINITION-NAME and EQL.

Model code:

(defun slot-value (object slot-name)
  (let* ((class (class-of object))
         (slot-definition (find slot-name (class-slots class)
                                :key #'slot-definition-name
                                :test #'eql)))
    (if (null slot-definition)
        (slot-missing object slot-name 'slot-value)
        (slot-value-using-class class object slot-definition))))

(defmethod slot-value-using-class ((class standard-class)
                                   (object standard-object)
                                   (slotd standard-effective-slot-definition))
  ..)

Given this, a user that wants to specialize the access to some but not
all slots of a class can do so by controlling the class of the effective
slot definition metaobjects.

(defclass my-class (standard-class) ())

(defmethod effective-slot-definition-class ((class my-class) direct-slots)
  (if <looking at the direct slots tells me this slot will be special>
      (find-class 'special-slot)
      (call-next-method)))

(defclass special-slot (standard-effective-slot-definition) ())

(defmethod slot-value-using-class ((class my-class) object (slotd special-slot))
  <do whatever it is>)

In effect, what has happened is that we have increased the "resolution"
of the specialization in the slot access protocol.  It is now possible
to specialize on a set of slots whereas before it was only possible to
specialize on the class and object.

With this new protocol, the implementation's ability to elide calls to
specified generic functions (and portable methods) arises the same way
it does in other parts of the protocol.  An advance test of method
applicability (`snooping' is what JonL would call it I guess) can see
that no portable methods are applicable and then retain the inline call.

This change is incompatible in syntax and the like, but my guess is that
any implementation technique that used to work still works.

Does anyone object to this change?


*** For reference, the part of the MOP (draft) which describes ***
*** the way things work currently.                             ***


\beginSubsection{Instance Structure Protocol}

The instance structure protocol is a three layer protocol.  The upper layer
is responsible for implementing the behavior of the slot access functions
like {\bf slot-value} and {\bf (setf slot-value)}.  The middle layer
controls the interaction between portable specializations of slot access
behavior and any implementation-specific optimizations of slot access.  The
lowest layer controls the implementation of instances, providing limited
mechanisms for direct access to the storage associated with an instance.

For each CLOS slot access function, there is a corresponding generic
function which actually provides the behavior of the function.  When called,
the slot access function simply calls the corresponding generic function,
and returns its result.  The arguments passed on to the generic function
include one additional value, the class of the {\it object} argument, which
always immediately precedes the {\it object} argument

The correspondences between slot access function and underlying slot access
generic function are as follows:

\boxfig
{\dimen0=.5pc
\def\slotargs{}%{(\it object slot-name\bf)}
\def\gfslotargs{}%{(\it class object slot-name\bf)}
\tabskip \dimen0 plus .5 fil
\halign to \hsize {#\hfil&#\hfil\cr
\noalign{\vskip -9pt}
\bf Slot Access Function &\bf Corresponding Slot Access Generic Function\cr
\noalign{\vskip 2pt\hrule\vskip 2pt}
\bf slot-boundp       \slotargs &\bf slot-boundp-using-class       \gfslotargs\cr
\bf slot-exists-p     \slotargs &\bf slot-exists-p-using-class     \gfslotargs\cr
\bf slot-makunbound   \slotargs &\bf slot-makunbound-using-class   \gfslotargs\cr
\bf slot-value        \slotargs &\bf slot-value-using-class        \gfslotargs\cr
\bf (setf slot-value) \slotargs &\bf (setf slot-value-using-class) \gfslotargs\cr
\noalign{\vskip -9pt}
}}
\caption{}
\endfig

While not actually specified, it is expected that most implementations will
have some mechanism for optimizing slot access.  At the time this document
is being written, most implementations optimize calls to {\bf slot-value}
and {\bf (setf slot-value)} which occur in the body of {\bf defmethod}
forms, and which appear implicitly in automatically generated slot
accessors.  The effect of these optimizations is to elide the actual call to
the corresponding slot access generic function by ``in-lining'' the behavior
of the generic function's specified method.

In the presence of applicable portable methods on slot access generic
functions, any such optimization must ensure that these methods are called;
just as if no optimization had been done in the first place.  But, in many
cases, portable methods on the slot access generic functions do not actually
affect the behavior of access to all the slots of instances.  Often, only a
small number of an instance's slots are affected.  In such cases, it is
desirable to allow the implementation's optimization mechanism to avoid the
call to the portable method, if it can be determined in advance that the
slot in question is not affected by any of the portable methods.

For any given class, this interaction between the implementation's
optimization mechanism and portable slot access methods is controlled on a
per-slot basis, by the corresponding effective slot definition metaobject.
When the class is finalized, the generic function {\bf
slot-definition-elide-portable-access-method-p} is called on each of the
effective slots.  If this generic function returns true, the implementation
is permitted (but not required) to elide the call to any applicable portable
methods on the slot access generic functions---the implementation is free to
run its optimized slot access code.  If this generic function returns false,
applicable portable methods on the slot access generic functions must be
called.

The only specified method on the generic function {\bf
slot-definition-elide-portable-access-method-p} always returns false.  Thus,
the default behavior is for applicable portable methods on slot access
generic functions to always be called.

\beginExample The following code demonstrates a typical use of this mechanism.
The class metaobject class {\bf my-class} supports the normal {\bf
:instance} and {\bf :class} allocated slots.  In addition, {\bf my-class}
supports a variant kind of slot for which the access must be done in a
special way.  For any variant slot, the predicate {\bf my-magic-slot-p}
returns true when called on the corresponding effective slot definition
metaobject.

The example shows the method on {\bf slot-value-using-class} which would be
defined to implement the behavior of the variant slots.  When the slot with
the given name is not a variant slot, {\bf call-next-method} is called to
invoke the normal implementation of slot access.  The method on {\bf
slot-definition-elide-portable-access-method-p} can be read as an
``up-front'' guarantee about whether or not the method on {\bf
slot-value-using-class} will end up calling and returning the result of {\bf
call-next-method}.

\screen!

(defmethod slot-value-using-class ((class my-class) object slot-name)
  (let ((slotd (find slot-name (class-slots class)
                     :key #'slot-definition-name)))
    (if (my-magic-slot-p slotd)
        <<do-something-special>>
        (call-next-method))))

(defmethod slot-definition-elide-portable-access-method-p
           ((class my-class) effective-slot-definition)
  (null (my-magic-slot-p effective-slot-definition)))

\endscreen!

\endExample

gregor@parc.xerox.com (Gregor Kiczales) (12/01/90)

   Organization: NASA/Jet Propulsion Laboratory
   Date: 	Thu, 29 Nov 1990 13:19:18 PST
   From: kandt%ai-jupiter.uucp%jpl-devvax.uucp@elroy.jpl.nasa.gov (Kirk Kandt)

   This concerns 5/22/89 Victoria PCL.  I'm running Lucid 3.0.

My comments will be with regard to the newer version of PCL.  They
apply, in essence, to older PCLs as well, but it probably isn't worth
the effort to fix both the older and newer PCLs as I suggest.

   (defmethod slot-value-using-class ((class lazy-class) object slot-name) ...)

   (defclass kirk ()
     ((x :initform 0 :initarg :x :accessor x))
     (:metaclass lazy-class))

   (setq kirk (make-instance 'kirk :x 1))

   Calling SLOT-VALUE always does the right thing; calling the X generic fn
   does not.
   The question is why does (X KIRK) quit calling SLOT-VALUE-USING-CLASS 
   after the second time?  More importantly, how can I make sure that it
   always calls SLOT-VALUE-USING-CLASS?  Or, how can I change PCL to 
   generate accessors that include the COND in SLOT-VALUE-USING-CLASS :AROUND?

There are actually two problems, both caused by stupidity on the part of
the person who wrote PCL.  Your message exhibits only one of them, but
unless I am wrong, the following code would fail as well:

(defmethod foo ((o kirk)) (slot-value o 'x))

That is, it wouldn't end up calling your special method on
SLOT-VALUE-USING-CLASS.

The second problem can be fixed, I believe, by fixing the
PRIMARY-PV-CACHE-MISS function in the file vector.lisp.  The first
problem (the one you demonstrate), that automatically generated
accessors don't call your methods on slot-value-using-class, can be
fixed, I believe, by fixing the function ACCESSOR-MISS-VALUES in the
file dfun.lisp.  The changes you want to make are going to be something
like:

(defun accessor-miss-values (generic-function applicable args)
  (declare (values type index))
  (let ((type
	  (and (eq (generic-function-method-combination generic-function)
		   *standard-method-combination*)
	       (every #'(lambda (m) (null (method-qualifiers m))) applicable)
	       (cond ((standard-reader-method-p (car applicable)) 
                      (AND (EQUAL (COMPUTE-APPLICABLE-METHODS
                                     #'SLOT-VALUE-USING-CLASS
                                     (CLASS-OF (CAR ARGS))
                                     (CAR ARGS)
                                     (STANDARD-ACCESSOR-METHOD-SLOT-NAME (CAR APPLICABLE)))
                                  (COMPUTE-APPLICABLE-METHODS
                                     #'SLOT-VALUE-USING-CLASS
                                     (FIND-CLASS 'STANDARD-CLASS)
                                     (MAKE-INSTANCE 'STANDARD-OBJECT)
                                     'FOO))
                            'READER))
		     ((standard-writer-method-p (car applicable)) 'writer)
		     (t nil)))))
    (values type
	    (and type
		 (let ((wrapper (wrapper-of (case type
					      (reader (car args))
					      (writer (cadr args)))))
		       (slot-name (accessor-method-slot-name (car applicable))))
		   (or (instance-slot-index wrapper slot-name)
		       (assq slot-name (wrapper-class-slots wrapper))))))))

Except that:

 1) You need to make them for writers too.
 2) The hack of computing the normal applicable methods should be cached
    somehow.
 3) All of this would be easier, and perform better, if you also changed
    PCL to use the new proposed protocol for SLOT-VALUE-USING-CLASS (as
    described in the message below).


I hope someone will decide to make these changes to PCL.  If they do, we
can add them to the sources on arisia.

Gregor

---

From: Gregor Kiczales <Gregor@parc.xerox.com>
To: MOP.PARC@xerox.com
Subject: change to instance structure protocol
Date: 	Thu, 22 Nov 1990 12:59:24 -0800

The current instance structure protocol specifies that the slot access
functions (e.g. SLOT-VALUE) are implemented in terms of slot acccess
generic functions (i.e. SLOT-VALUE-USING-CLASS).  The generic functions
dispatch on the (class of) the class and the (class of) the object.

This protocol has always had a problem, specifically if you want to
specialize access to some but not all of a class's slots, it is a bit
awkward.  You have to write a method on the generic function which first
tests to see if the slot in question is a special one; if it is, special
code is run, otherwise you do a CALL-NEXT-METHOD.

This problem gets even worse when we consider the problem of retaining
the implementations ability to do optimized slot access.  If the user
defines a method on a slot access generic function, the implementation
has to call that method (it must deoptimize slot access); but even if in
fact only some of the class's slots are affected, the performance of the
access to all of them will be affected.  The problem is the same one of
resolution, the current protocol only specializes on the class and the
object, not on individual slots.

The solution we have been using is to have a generic function called
SLOT-DEFINITION-ELIDE-PORTABLE-ACCESS-METHOD-P which allows the to test
whether it can retain its optimization in some cases.  But, this is a
hack, it distinguishes "compiled" and "interpreted" code in a weird way,
and is just plain blecherous.  (For reference, the part of the newest
MOP which describes this is included at the end of this message.)

There is an elegant solution, but I have refrained from proposing it for
some time.  Now, it really seems worth proposing.

The basic idea is to change the last (slot-name) argument to all of the
SLOT-XXX-USING-CLASS generic functions.  The new value would be the
actual effective slot definition metaobject rather than the slot name.
The slot access functions would be specified to lookup the effective
slot definition object using CLASS-SLOTS, SLOT-DEFINITION-NAME and EQL.

Model code:

(defun slot-value (object slot-name)
  (let* ((class (class-of object))
         (slot-definition (find slot-name (class-slots class)
                                :key #'slot-definition-name
                                :test #'eql)))
    (if (null slot-definition)
        (slot-missing object slot-name 'slot-value)
        (slot-value-using-class class object slot-definition))))

(defmethod slot-value-using-class ((class standard-class)
                                   (object standard-object)
                                   (slotd standard-effective-slot-definition))
  ..)

Given this, a user that wants to specialize the access to some but not
all slots of a class can do so by controlling the class of the effective
slot definition metaobjects.

(defclass my-class (standard-class) ())

(defmethod effective-slot-definition-class ((class my-class) direct-slots)
  (if <looking at the direct slots tells me this slot will be special>
      (find-class 'special-slot)
      (call-next-method)))

(defclass special-slot (standard-effective-slot-definition) ())

(defmethod slot-value-using-class ((class my-class) object (slotd special-slot))
  <do whatever it is>)

In effect, what has happened is that we have increased the "resolution"
of the specialization in the slot access protocol.  It is now possible
to specialize on a set of slots whereas before it was only possible to
specialize on the class and object.

With this new protocol, the implementation's ability to elide calls to
specified generic functions (and portable methods) arises the same way
it does in other parts of the protocol.  An advance test of method
applicability (`snooping' is what JonL would call it I guess) can see
that no portable methods are applicable and then retain the inline call.

This change is incompatible in syntax and the like, but my guess is that
any implementation technique that used to work still works.

Does anyone object to this change?


*** For reference, the part of the MOP (draft) which describes ***
*** the way things work currently.                             ***


\beginSubsection{Instance Structure Protocol}

The instance structure protocol is a three layer protocol.  The upper layer
is responsible for implementing the behavior of the slot access functions
like {\bf slot-value} and {\bf (setf slot-value)}.  The middle layer
controls the interaction between portable specializations of slot access
behavior and any implementation-specific optimizations of slot access.  The
lowest layer controls the implementation of instances, providing limited
mechanisms for direct access to the storage associated with an instance.

For each CLOS slot access function, there is a corresponding generic
function which actually provides the behavior of the function.  When called,
the slot access function simply calls the corresponding generic function,
and returns its result.  The arguments passed on to the generic function
include one additional value, the class of the {\it object} argument, which
always immediately precedes the {\it object} argument

The correspondences between slot access function and underlying slot access
generic function are as follows:

\boxfig
{\dimen0=.5pc
\def\slotargs{}%{(\it object slot-name\bf)}
\def\gfslotargs{}%{(\it class object slot-name\bf)}
\tabskip \dimen0 plus .5 fil
\halign to \hsize {#\hfil&#\hfil\cr
\noalign{\vskip -9pt}
\bf Slot Access Function &\bf Corresponding Slot Access Generic Function\cr 
\noalign{\vskip 2pt\hrule\vskip 2pt}
\bf slot-boundp       \slotargs &\bf slot-boundp-using-class       \gfslotargs\cr
\bf slot-exists-p     \slotargs &\bf slot-exists-p-using-class     \gfslotargs\cr
\bf slot-makunbound   \slotargs &\bf slot-makunbound-using-class   \gfslotargs\cr
\bf slot-value        \slotargs &\bf slot-value-using-class        \gfslotargs\cr
\bf (setf slot-value) \slotargs &\bf (setf slot-value-using-class) \gfslotargs\cr 
\noalign{\vskip -9pt}
}}
\caption{}
\endfig

While not actually specified, it is expected that most implementations will
have some mechanism for optimizing slot access.  At the time this document
is being written, most implementations optimize calls to {\bf slot-value}
and {\bf (setf slot-value)} which occur in the body of {\bf defmethod}
forms, and which appear implicitly in automatically generated slot
accessors.  The effect of these optimizations is to elide the actual call to
the corresponding slot access generic function by ``in-lining'' the behavior
of the generic function's specified method.

In the presence of applicable portable methods on slot access generic
functions, any such optimization must ensure that these methods are called;
just as if no optimization had been done in the first place.  But, in many
cases, portable methods on the slot access generic functions do not actually
affect the behavior of access to all the slots of instances.  Often, only a
small number of an instance's slots are affected.  In such cases, it is
desirable to allow the implementation's optimization mechanism to avoid the
call to the portable method, if it can be determined in advance that the
slot in question is not affected by any of the portable methods.

For any given class, this interaction between the implementation's
optimization mechanism and portable slot access methods is controlled on a
per-slot basis, by the corresponding effective slot definition metaobject.
When the class is finalized, the generic function {\bf
slot-definition-elide-portable-access-method-p} is called on each of the
effective slots.  If this generic function returns true, the implementation
is permitted (but not required) to elide the call to any applicable portable
methods on the slot access generic functions---the implementation is free to
run its optimized slot access code.  If this generic function returns false,
applicable portable methods on the slot access generic functions must be
called.

The only specified method on the generic function {\bf
slot-definition-elide-portable-access-method-p} always returns false.  Thus,
the default behavior is for applicable portable methods on slot access
generic functions to always be called.

\beginExample The following code demonstrates a typical use of this mechanism.
The class metaobject class {\bf my-class} supports the normal {\bf
:instance} and {\bf :class} allocated slots.  In addition, {\bf my-class}
supports a variant kind of slot for which the access must be done in a
special way.  For any variant slot, the predicate {\bf my-magic-slot-p}
returns true when called on the corresponding effective slot definition
metaobject.

The example shows the method on {\bf slot-value-using-class} which would be
defined to implement the behavior of the variant slots.  When the slot with
the given name is not a variant slot, {\bf call-next-method} is called to
invoke the normal implementation of slot access.  The method on {\bf
slot-definition-elide-portable-access-method-p} can be read as an
``up-front'' guarantee about whether or not the method on {\bf
slot-value-using-class} will end up calling and returning the result of {\bf
call-next-method}.

\screen!

(defmethod slot-value-using-class ((class my-class) object slot-name)
  (let ((slotd (find slot-name (class-slots class)
                     :key #'slot-definition-name)))
    (if (my-magic-slot-p slotd)
        <<do-something-special>>
        (call-next-method))))

(defmethod slot-definition-elide-portable-access-method-p 
           ((class my-class) effective-slot-definition)
  (null (my-magic-slot-p effective-slot-definition)))

\endscreen!

\endExample