[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