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

Re: dependent update protocol



I am answering Moon's message, but will only include the parts that I want to
comment on.

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

    It's usually better not to try to pass extra args through a
    mapping function; instead the function being mapped can be a
    closure that knows the extra args.  This is more flexible, and is
    how all eight functions in CLtL whose names start with "map" work. 
    Thus I would make map-dependents take only two arguments.

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

I agree.

    I don't see how this can work if reinitialize-instance changes
    the set of dependents.  The map-dependents will map over the new
    dependents, and any old dependents that were removed will never be
    updated.  Maybe you can say that remove-dependent, or by convention
    each caller of it, calls update-dependent or a variant of it on the
    dependent being removed.
I don't see this as a problem.  The semantics of dependency that I see are that
a dependent wants to know when its "master" changes.  But if it is no longer a
dependent, then it has no more "need to know".  It may then become a dependent
on something else, and will be updated at that time.  If one wants to ensure
that an object of type T1 takes special action when a dependency is removed,
then an :after method on remove-dependent can do whatever is necessary.

    Also, since the style you propose depends on three methods for
    three different generic functions to be kept in sync, and does
    something unpredictable without necessarily signalling an error if
    one of the methods is left out, it doesn't seem very robust.  I
    would prefer to see either a more abstract data structure than a
    list, so that leaving out a method (or forgetting to call cdr) for
    one class would not damage the data seen by other class's methods,
    or else to use one generic function instead of three, so that all
    the code for one class would be in a single place and hence less
    likely to be out of sync.  In the latter case, this generic
    function would be called multiple times and one of its arguments
    would indicate whether state was being collected or distributed.


The intent of the before-reinitialization is to capture the appropriate state of
the instance before it changes, and that of after-reinitialization is to capture
some summary of what changed (note that these methods only look at the state of
the master object, and the inputs, not any dependent).  An alternative I prefer
(please comment) is to specify that before-reinitialization and
after-reinitialization both return property lists.
 

For example, I would specify that before-reinitialization for standard-class
returns a property list that contains (at least) old direct-superclasses,
direct-slots, and direct-options if these arguments are provided as reinit-args.
After-reinitialization for standard-class returns a property list which has
properties with value T for each of direct-superclasses, direct-slots, and
direct-options if that info in the class changed.  

By specifying property lists, the shadowing properties are well undertood, and
it is easy to arrange for noninterference with superclass properties.   Also
with this form, error checking can be implemented as property list or argument
list checks.