Peter Hildebrandt wrote:
On Wed, 12 Dec 2007 15:36:26 +0100, Ken Tilton kennytilton@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