[cells-gtk-devel] Re: [cells-devel] Something like a def-family-observer?
Ken Tilton
kennytilton at optonline.net
Wed Dec 12 15:52:33 UTC 2007
Peter Hildebrandt wrote:
> On Wed, 12 Dec 2007 15:36:26 +0100, Ken Tilton
> <kennytilton at optonline.net> wrote:
>
>> Ken Tilton wrote:
>>
>>> Ken Tilton wrote:
>>>
>>>> Peter Hildebrandt wrote:
>>>>
>>>> OK, now that I am up to speed, let's go back to your original query.
>>>>
>>>>>
>>>>> Say, I have a model M that depends on the structure of a family
>>>>> tree. One of M's slots is therefore depending on the root of the
>>>>> family tree: (c? root). However, I want M to know about changes
>>>>> in the family tree, like, say, when a child or grandchild is
>>>>> added. Apparently cells (at least the cells_2.0 branch required
>>>>> by cells-gtk) does not broadcast change messages to the parents
>>>>> of a node (which I guess is the right thing in 99% of the cases).
>>>>>
>>>>> What's the best way to deal with that?
>>>>>
>>>>> (i) Is there some mechanism for this purpose present in cells? Or
>>>>> (ii) Do I roll my own special case solution? Or
>>>>> (iii) Is it worthwhile to build some general purpose solution to
>>>>> this problem?
>>>>>
>>>>> My approach towards (ii) (I haven't coded anything yet, waiting
>>>>> for you comments) would be something like building an observer
>>>>> tree each node of which observes one node in the family tree.
>>>>> Something like this:
>>>>> - Design a tiny tree observer model ("tto"?), suited to observing
>>>>> one family node
>>>>> (defmodel tty (family) (observed observed-kids reports-to))
>>>>>
>>>>> - Every tto knows about the parent model (M from above) and does
>>>>> the right thing when it sees a change (say, call a closure)
>>>>> - If the observed nodes has kids, it instantiates tto kids of its
>>>>> own to match the kids of the observed tree
>>>>> (def-c-output observed ((self tto))
>>>>> (make-tto :observed (c? new-value) :observed-kids (c? (kids
>>>>> new-value)))
>>>>> (setf (kids self) (mapcar (lambda (kid) (make-tto :observed
>>>>> (c? kid) :observed-kids (c? (kids kid)))) (kids new-value)
>>>>> ...)
>>>>> (def-c-output observed-kids ((self tto))
>>>>> ...)
>>>>>
>>>>> - Changing the root slot in M results in the instantiation of a
>>>>> tto for the root
>>>>>
>>>>> I guess that would work ... but I feel there must be a more
>>>>> elegant solution.
>>>
>>> Roughly (cuz of rough recall of Cells2):
>>> (defmodel family-observer (family)
>>> ;; we'll use the "md-value" slot for the observed
>>> ()
>>> (:default-initargs
>>> :kids (c? (the-kids
>>> (bwhen (o (^md-value self)) ;; not sure why not
>>> (loop for k in (^kids o) collecting
>>> (let ((this-k k)) ;; loop is weird
>>> (make-instance 'family-observer
>>> :md-value this-k)))))))
>>> That handles rows in/out.
>>
>>
>> Left unsaid was that you need an observer specialized on
>> family-observer to relay changes to the Gtk side.
>
>
> Ah, I get it. Would the following do the trick?
>
> (def-c-observer kids ((self f-o))
> (mapcar #'gtk-forget-about-and delete-that-row old-value)
I think you can track down the existing observer for the kids slot of
family to see what you would want to do. Both new and old values are
/lists/ of kids, some new, some departing, so you need to call
set-difference in each direction to find ones to add/delete on the gtk
side. or...
> (mapcar #'not-to-be old-value)) ;; would i need that?
No, that gets called by the existing kids observer on the family class,
which is also why calling it yourself in a rule would be unnecessary
(and a bad idea because it might be a whisker too soon). But you have me
thinking, why reinvent the sorting into new/lost done by the existing
kids observer? You can just add :after (:before?) methods on
md-awaken/not-to-be to add/delete on the gtk side. That means two
methods instead of one observer, but it seems more elegant/accurate.
>
> Help with this one is really appreciated, because I feel I have never
> quite understood how to handle making instances in a cells slot
> properly (I how to clean up properly).
One of the first things to impress me about Cells was that it was
relatively easy to handle things joining and exiting the model, but it
is definitely and literally an "edge" case (I think of the overall model
population expanding and contracting.) Cells3 got created precisely
because that luck ran out: Cells internals started finding themselves
operating on things that had been officially removed from the model,
because of delicate timing issues -- ISTR it had to do with processing
already "queued up" for an instance on the stack running after some code
decided the instance should go away. I ended up with code all over the
map to watch out for "dead" instances and ignore them. Interestingly, it
was my design for the RoboCup competition that broke things, and I had
written massive amounts of Cells code before then without a problem.
Funny how that works.
If you dig out cells-manifesto.txt from the latest Cells CVS, in there
you will find a semi-formal definition of dataflow integrity enforced by
Cells3. That unfortunately requires a little less transparency when one
is modifying model state from within an observer, but it makes models
more robust.
The newest Cells, btw, generalizes the kids slot to allow one to specify
any slot as "owning". (I think that is what I called it.) In fact, the
new kids observer might not do the set-difference thing, i think that is
now being done in the internals for any slot value change to an "owning"
slot. (It goes by slot, not by rule.)
btw, if I sound fuzzy on Cells, that's the good news, I do not have to
mess with it much any more. :)
>
>>
>>> As for individual values, well, I guess you generalize the handling
>>> of kids so it is just another slot-value. Changes to kids add/remove
>>> rows, changes to other things set values within a row.
>>> You know you need the kids handled, so what you might build that in
>>> and then have some macrology write the defmodel for custom
>>> subclasses of f-o:
>>> (def-family-observer my-tree (<def options?>) slot-1 slot-2)
>>
>>
>> Left unsaid is that def-family-observer expands into:
>>
>> (defmodel my-tree (family-observer)
>> <slot defs for slot-1 and slot-2>
>> (:default-initargs
>> :slot-1 (c? (slot-1 (md-value self)))
>> :slot-2 <same>))
>>
>> ...and observers for slot-1 and slot-2 specialized on my-tree to
>> relay changes to gtk.
>
>
> Cool. Gotta love lisp, macros, and cells. I wonder how many lines of
> C that would make.
:) I don't know, I used to do some pretty sick things with the C
preprocessor (and before that the cobol copy-replacing).
>
> As to the current status, I'm messing with CFFI to get some more
> bookkeeping deferred to the GTK side. Once you get started there's a
> function of everything :)
Yes, clearly a mature product. Good idea to let GTk handle as much as
possible.
kt
More information about the cells-devel
mailing list