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

(re)initialization revisited



This message addresses some important issues which we would like to get
feedback on as soon as possible.  Please send at least some sort of
comment as soon as time permits.

This message attempts to address some of the problems Moon brought up
with reinitialize-instance.  In thinking about this we came to the some
of the same conclusions he did, namely that we really needed to
integrate initialize-instance, update-instance-structure and
class-changed a little more.  Having done that, we were able to fit
reinitialize-instance in quite easily.

This message is in 3 parts.  The first part introduces the observations
we made when we went back to think about this.  The second part outlines
change to existing parts of chapter 1 and 2.  The third part starts from
that change, modularizes it better and introduces reinitialize-instance.

Part 1.
First lets look at the existing generic functions initialize-instance,
update-instance-structure and class-changed.  In looking at the
specification of these functions, we can see that the each one's primary
method is doing something very close to identical.  They could almost be
calling a common subroutine.  The most interesting thing is that except
for some very special cases, they could in fact call a common
subroutine.

We believe, that while we were designing these sections, we (all of us)
must have intended for them to "call" that common subroutine.  

Part 2.
So, we would like to modify the primary methods for initialize-instance,
update-instance-structure and class-changed to have a common behavior.
This common behavior could be defined by a shared subroutine.

(defun subroutine (instance &rest initargs)
  .. initialize the slots from the initargs in the order ..
  .. specified in chapter 1                              ..
  ..
  .. for any slots which are still unbound, initialize   ..
  .. them from their initforms                           ..
  )

In order to understand this change fully, lets look carefully at cases
where this would actually cause different behavior:

INITIALIZE-INSTANCE
  This is actually not a change for initialize instance.  There may be
  some problems with some of the mentioned optimizations, but lets come
  back to that in another message.

UPDATE-INSTANCE-STRUCTURE
  (Note that update-instance-structure would not call the subroutine
   with any initargs, so only the part which sets unbound slots to the
   result of evaluating their initforms would apply.)

  The difference is when a slot appears in both the old and new versions
  of the class, has an initform in the new version of the class, and is
  unbound in an instance at the time of class redefinition.

  Under the old definition, update-instance-structure would do nothing
  to the slot.  Under the new definition, update-instance-structure
  would set the slot to the result of evaluating its initform.

CLASS-CHANGED
  Class-changed is exactly analogous to update-instance-structure.  If
  there is a slot which appears in both the old and new classes, and
  that slot is unbound at the time of change-class, the new definition
  of class-changed will set its value to the result of evaluating the
  initform.  The old definition wouldn't have touched the slot at all.


Part 3.

The proposal we would like to make is to have the subroutine which does
the setting of slots described above moved to the standard method of a
new generic function called shared-initialize (all names here could be
improved upon).  Initialize-instance, update-instance-structure and
class-changed would each call shared-initialize.  

Users who wanted to add code to one of the specific update functions
would define a method on it.  Users who want to add initialization code
common to all the ways an instance is initialize would define a method
on shared-initialize.  In order for user defined methods on
shared-initialize to know why they were being called, shared-initialize
takes an argument which is the name of the generic function which called
it.

Given this structure, reinitialize-instance would be a generic-function
just like the other three, its standard method would call
shared-initialize with 'reinitialize-instance as the second argument.

Here is some sample code:

(defmethod make-instance ((class standard-class) &rest initargs)
  (let ((defaulted-initargs (default-initargs class initargs)))
    (check-initargs class defaulted-initargs)
    (let ((instance (apply #'allocate-instance class defaulted-initargs)))
      (apply #'initialize-instance class defaulted-initargs)
      instance)))


(defmethod initialize-instance ((instance standard-object) &rest initargs)
  (apply #'shared-initialize instance
                             'initialize-instance
                             initargs))

(defmethod reinitialize-instance ((instance standard-object) &rest initargs)
  (apply #'shared-initialize instance
                             'reinitialize-instance
                             initargs))

(defmethod update-instance-structure ((instance standard-object)
                                      added-slots
                                      discarded-slots
                                      property-list)
  (shared-initialize instance 'update-instance-structure))

(defmethod class-changed ((previous standard-object)
                          (current standard-object))
  (shared-initialize current 'class-changed))


(defmethod shared-initialize ((instance standard-object)
                              caller
                              &rest initargs)
  .. initialize the slots from the initargs in the order ..
  .. specified in chapter 1                              ..
  ..
  .. for any slots which are still unbound, initialize   ..
  .. them from their initforms                           ..
  )

-------