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

dependent update protocol



Moon has suggested that the dependents protocol stuff should not be part
of standard-object; rather it should be defined as a separate mixin
since that is what multiple inheritance is for after all.  He also
mentioned some problems he had with the way the dependent protocol
worked.  He didn't identify those problems, but we had some problems
with it ourselves which we have tried to address.

This message outlines a new dependents updating protcol.  This protocol
is different from what we had before in several important ways:

   - 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.  Without specifying the
     format of that information, a subclass of standard-class can
     augment that information to pass extra information about how
     it has been modified.  Dependents of the subclass which expect to
     see the augmented information will see it, dependents which do not
     expect to see augmented information will see the original
     information.


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 args)
   For all dependents of instance, applies function to instance, 
   the dependent and args.

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 actively soliciting suggestions for better names for
before-reinitialization and after-reinitialization.]

(defmethod reinitialize-instance :around 
           ((object updatable-object-mixin) &rest reinitargs)
   (let ((before (apply #'before-reinitialization object reinitargs)))
     (call-next-method)
     (let ((update-args 
             (apply #'after-reinitialization object before reinitargs)))
       (map-dependents object #'update-dependent object update-args))))

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 how the protocol can be used by a subclass to encapsulate
the information the superclas might pass.  All use of this protocol
should be in this style.  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.  When ordinary
standard-class is used as a submetaclass, standard-class should see the
after-reinitialization information it was expecting to see.

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

(defmethod before-reinitialization ((c my-class) &rest reinitiargs)
  (cons (<compute-the-magic-info>)
        (call-next-method)))

(defmethod after-reinitialization ((c my-class) before &rest reinitargs)
  (cons (<compute-the-magic-info>)
        (apply #'call-next-method c (cdr before) reinitargs)))

(defmethod update-dependent ((dependent standard-class)
                             (object my-class)
                             after-reinitialization)
  (call-next-method dependent object (cdr after-reinitialization)))

(defmethod update-dependent ((dependent my-class)
                             (object my-class)
                             after-reinitialization)
  (<do something with the magic info>)
  (call-next-method dependent object (cdr after-reinitialization)))
-------