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

dependent update protocol



    Date: Wed, 20 Apr 88 14:22 EDT
    From: David A. Moon <Moon@STONY-BROOK.SCRC.Symbolics.COM>

    And will clearly break if misused.  Here's an idea: someone, I forget
    who, suggested using a property list as the state that's passed around,
    instead of letting each class's methods decide how they are going to
    encapsulate the next class's state.  If you do that, then methods
    getting out of sync cannot cause any damage.  The only way for one class
    to damage another would be for both to use the same property name, but
    the property names can be class objects.

Incorporating this with what we had previously is fairly easy.  It looks
something like this:


   - there is a separate mixin for the dependents stuff, that mixin
     is used by standard-class to do dependents updating.  Users can
     use it in their code to particpate in that dependent updating
     protocol.

   - There is a mechanism for allowing the modified object to pass
     information describing the modification to all the dependents.
     This mechanism is designed to allow subclassing to work.  That
     is, a given class (say standard-class) may use this mechanism to
     pass information to its dependents.  The format of the
     information is a plist which each class in the chain can put
     information into and take it back out of.  Each class is
     expected to put one entry in the PLIST, and should use the
     actual class object as the key.  This will prevent name
     conflicts in the plist.  The format of the individual entries
     in the plist is completely unspecified.  It is undefined what
     happens if system information in the plist is modified or
     removed.  It is undefined what system information will be in
     the plist.

CLOS supports a general mechanism for registering dependents of objects,
and updating those objects on change.  It does this through the medium
of a mixin class, updatable-object-mixin, that supports updating of
dependents.  A dependent of an object O1 is any object that may need to
be updated when O1 changes.  An object becomes a dependent only by
explicitly registering itself as one.  The dependent registering
protocol allows redundant registering of dependents; this means that
code which wants an object to be registered as a dependent of an object
should always do so.  It should never depend on the particular
implementation to do so since specific implementations may use the
dependent updating protocol in different ways.

  Some examples of the use are:

1) A screen view of an object (perhaps a class) needs to update itself
   when the object (class definition) changes.  To do this it registers
   itself as a dependent of that object, whenever the object changes it
   will update all its dependents.

2) A subclass of a class wants to recompute its class precedence list
   when the class changes its direct superclasses.  The subclass
   registers itself as a dependent of the class, whenever the class
   changes, the subclass is updated and can recompute its class
   precedence list.

3) Some set of objects needs to be updated when a class changes.  This
   can be handled in one of two ways, all the objects can be registered
   as dependents, or a single object can be created which encapsulates
   all the other objects.  The single `large' object can be registered
   as a dependent.


(defclass updatable-object-mixin () ())

This class supports two protocols, registration and updating of
dependents.  The registration protocol is comprised of the generic
functions add-dependent, remove-dependent, and map-dependents.  The
updating protocol is comprised of methods on reinitialize-instance, and
the generic functions before-reinitialization, after-reinitialization
and update-dependent.

The Registration Protocol

\Defmeth add-dependent ((object updatable-object-mixin) new-dependent)
   adds new-dependent as a new dependent of object.  Does nothing if
   new-dependent is already a dependent of object.

\Defmeth remove-dependent ((object updatable-object-mixin) dependent)
   removes dependent as one of the dependents of object.  Does nothing
   if dependent is not already one of the dependents of object.

\Defmeth map-dependents ((instance updatable-object-mixin) function)
   For all dependents of instance, applies function to the dependent.

The Updating Protocol

The updating protocol has two parts.  The first part is an :around
method on reinitialize-instance which causes all the dependents of an
object to be updated whenever the object is reinitialized.  This part of
the protocol also provides a mechanism which allows the object to pass
information to its dependents describing the change effected by
reinitializing the object.  This mechanism is provided by having the
:around method on reinitialize-instance call the generic-function
before-reinitialization before the reinitialization, and the
generic-function after-reinitializing after the reinitialization.  The
value returned by after-reinitialization is passed in the call to
update-dependent on each of the dependents.

[We are still actively soliciting suggestions for better names for
before-reinitialization and after-reinitialization.]

(defmethod reinitialize-instance :around 
	   ((object updatable-object-mixin) &rest reinitargs)
  (let ((dependents ()))
     ;; Collect all the dependents ahead of time.  The fact that
     ;; a list is used here means nothing.  The fact that this
     ;; code preserves the order that map-dependents does it in
     ;; is important though.
     (map-dependents object #'(lambda (x) (push x dependents)))
     (setq dependents (reverse dependents))

     ;; Collect the before-reinitialization information.  This will be
     ;; a plist.
     (let ((before (apply #'before-reinitialization object reinitargs)))

       
       (call-next-method)

       ;; Collect the after-reinitialization information.  This will
       ;; also be plist.
       (let ((after 
	       (apply #'after-reinitialization object before reinitargs)))

	 (dolist (dep dependents)
	   (update-dependent object dep after))))))

The updatable-object-mixin provides implementations of
before-reinitialization, after-reinitialization, and update-dependent.

(defmethod before-reinitialization ((object updateable-object) &rest ignore)
  ())

(defmethod after-reinitialization ((object updateable-object)
				    before-reinitialization
				    &rest ignore)
  ())

(defmethod update-dependent ((dependent updateable-object)
			     (object updateable-object)
			     after-reinitialization)
  ())



Example:

This example shows how this protocol might be used.  In particular, it
demonstrates the proper style of using the plist to pass information
around.  The standard methods on before-reinitialization, 
after-reinitialization and update-dependent are there to support this.

Suppose that a specific kind of metaclass wants to propagate special
information when used as a submetaclass of itself.

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

(defmethod before-reinitialization ((c my-class) &rest reinitiargs)
  (let ((plist (call-next-method)))
    (setf (getf plist (class-named 'my-class))
	  (<compute-the-magic-info>))
    plist))

(defmethod after-reinitialization ((c my-class) before &rest reinitargs)
  (let ((plist (call-next-method)))
    (setf (getf plist (class-named 'my-class))
	  (<compute-the-magic-info>))))

(defmethod update-dependent ((dependent my-class)
			     (object my-class)
			     plist)
  (let ((magic-info (getf plist (class-named 'my-class))))
    (<do something with the magic info>)
    (call-next-method)
    (<maybe do something else with magic info>)))
-------