[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>)))
-------