I thought I'd slide over to cells-devel to get this in the archive.
Thomas F. Burdick wrote:
Kenny Tilton writes:
I'll give you a lead. Look at to-be/md-awaken/c-awaken and not-to-be/md-quiesce/c-quiesce. There is also a whole suite of c-link* and c-unlink* guys. Come to think of it, unlinking from unused cells handles both pointers (each way between user and used). Likewise linking. so to swap in a cell (roughly):
Aarg, "quiesce". I was wondering if there was an opposite of awaken.
"quiesce" was especially good during the c-gosub years (and, yes, the "gosub" is in fun/fond memory of The Early Years) when a cell would actually jump back in once the gosub "returned". btw, I forgot to answer about the mechanism. I forget, actually. IIRC, it was somewhat imperative, in that at some point one simply instantiated a gosub dynamically aiming it at the instances to which it applied (a to-be :after method on gosub could swap in any number of cells for any number of instances). One fun thing was that this was a case where Cells was implemented using Cells: the gosub instance had a "return" cell, which echoed a T value by backing out its cells and restoring the quiesced Cells.
I finally decided there must be a way to express the page-thumb positions cyclicity as a single rule always in effect, and liked the idea of yanking the entire gosub mechanism. Only I ended up just having each other setf the other, each a CV setf'ed by something else!! Hmmmmm.....probably the best way to go is hunker down and just make the cyclicity work, but first I want to fix propagation and see if I can lose the horrid dataflow interference handling (which stopped my first attempt at allowing cyclically dependent rules.
(let ((users (c-users old-cell))) (c-quiesce old-cell) (dolist (u users) (c-link u new-cell)) (c-awaken new-cell))
But then I can see a problem since c-awaken thinks it is getting a brand new cell, when new-cell should actually assume life as if it had been there all along--it needs to compare its first value calculated with the last value calculated by the old cell to keep all the contracts working.
Yeah, I'd noticed that. I figured I'd follow the normal cell-installation path, and check for begining-of-the-world assumptions. I got there with c-install pretty quickly (it doesn't look for a preexisting cell).
That would likely get shot down at the higher setf level, which (until Cloucell) simply rejected any attempt to store a cell in a slot (becasuse it usually meant a bleary edit session had yielded (c? blah-blah (c?......)). But then Cloucell wanted to have a generic model view which dynamically switch to the current model inspected by mapping cell view kids one by one to cell slots, ie, a rule picked out the cell as the md-value of the corresponding cell view.
btw, are you sure you need to do this? When will you know to switch rules? If that is model data, can't you just:
(c? (if (^time-to-switch) <compute this-a-way> <compute that-a-way>))
This works in most cases, but it can be a mess in larger systems. You can always work around it by making slot FOO do:
(c? (funcall (^foo-worker)))
then just replacing FOO-WORKER, but it seems cheesy to me.
I don't absolutely need to do this right now (I've been mostly hacking up silly small Cells examples for myself). But I was thrilled with this feature of KR when working on the Anaconda compiler. I used KR to maintain constraints in the code graph. When working on the analysis phase(s) that ran before the optimizer(s), it was nice to be able to change rules when the analyzer discovered it could.
Say the analyzer found that a certain significant area of the program graph didn't do any allocation, or did a small amount where we were able to statically determine the ceiling. This means that we don't necessarily need to keep our values tagged in that part of the graph[*]. Cool. We go through that area, and change the rules to that effect. It was good to keep the o-formulas the same part of the same file, where the analysis happened, not spread throughout the formulas of the program grapher.
[*] Even better/worse, we do potentially need the all-tagged code, because Lisp is dynamic. So, we duplcate that part of the graph, to ensure that code gets compiled under the old rules, *and* we modify the primary code to use the new rules. You multiply this across M dimensions of analysis, and you can't use booleans as hacks, you really need seperate worlds of rules.
Gotcha. This is a compiler and you want the compiler overhead out of the picture at run-time. But then you must already be solving the problem: "the compiler has decided this and that, now how do we leave things so this and that obtain at run time?" Oh, well, sounds like you know what you are doing, I'll shut up.
btw2, are you soloing on this as an exercise in learning Cells? Because I fully expect to be making such extensions myself (well, OK, where they make sense--but in this case I would do it for you even if I decided it was not something I wanted to have in the main fork--but again, as long as it does not break anything, I would err on the side of inclusiveness and just express my misgivings in the doc.
I've gotten quite used to being able to depend on KR, and I'm seriously considering Cells as a permanent replacement. I like what I've seen so far, so I kept digging; I want to ensure that it's good as far down as I've dug with KR -- rough edges notwithstanding, so far so good, btw :-). So, yes, partly it's an exercise in learning Cells' guts. Also, I started by pulling a Model (do you have a term for an instance with cell-slots?)
yes, "model". re-badging possible: I actually just got quite confused over here tidying up things so I could yank some apologies from the doc. Not sure I am done, because unhappily I just realized that DEFMODEL does not create a subclass of MODEL. :( It subclasses MODEL-OBJECT. The right thing is to change model-object to ORGANISM or some other pun for a cells-aware object, then change DEFMODEL to DEFORGANISM. This is something I am after anyway, because even though in my world Cells mean model-building, strictly speaking the MODEL thing is just one way to play with cells.
into the inspector, and kept going. I'd inspect, code, inspect, code, then discover that the code I'd written was already in Cells. So, it wasn't until I hit questions like, "has Kenny thought of this/is this supported?" that I thought to ask for help. Not to mention that my spare time has been fleeting over the last couple weeks.
And, I was doing something stupid. The latest version of MAKE-BLAST (think I got it right, finally) wasn't special-casing slots that get inherited from MODEL-OBJECT. That means, that .CELLS wasn't an alist in the resulting blast, it was a cell pointing to its prototype's .CELLS. That, uh, breaks.
This will be interesting to see. So when you clone via make-blast, the new instance keeps up with the original by having a cell in each slot which says:
:slotX (c? (slotX (original self)))
Just guessing.
Pretty much, except closing over the prototype, instead of using a slot.
(Aside: the body of make-blast-of-class did not answer the question I formed at this point, so my follow-up is here but I did not check out mboc.)
If (a) the rule for slotX is:
:slotX (c? (+ (^slotY) (^slotZ))
and (b) the blast can substitute a different rule for slotZ, then there would be a problem. Or do blasts always accept the prototype in its entirety? I'll just leave you with my main concern: Cells to me feel "instance-oriented", in that even where they have the same rule for the same slot, those rules can look to attributes of the instance (such as its position in the list of kids of its parent) when computing a value. (So even if slotZ is not overridden the semantics get mixed up.)
If you try just to re-use the Cell, the problem is that Cells gain instance-specific state once installed and evaluated (the model, users, useds).
I guess this is the classic case of deep vs shallow copy. What does "same as prototype" mean?
The body to MAKE-BLAST-OF-CLASS is currently:
;; PARITION-SLOTS-FOR-BLAST makes the policy of who gets inherited ;; from the prototype, who gets the default value, according to ;; CLOS, and who is specified with initargs. (defun make-blast-of-class (prototype class &rest initargs) (let ((class (if (symbolp class) (find-class class) class))) (multiple-value-bind (initargs inherited default) (partition-slots-for-blast prototype class initargs) (declare (ignore default)) (let ((blast (apply #'make-instance class initargs))) (loop for slot in inherited for slot-name = (slot-definition-name slot) do (install-cell blast slot-name (make-echo-cell prototype slot-name)) finally (bring-to-life blast)) blast))))
But if so, I wonder if we cannot optimize that by having the original simply keep track of its clones, and throwing in a mechanism to identify which slots were accepted as is from the original (I am just guessing wildly at the spec). Then you don't get all the slots with redundant links.
I'm not sure that a list of blasts would be any more efficient than a bunch of slots in environments, but if it were, and it were more optimal in Cells' internal dependency tracking, that would probably be a good idea. My mind is still very much in lazy mode, so pushing the prototype's values into the blasts didn't occur to me.
OK. I was thinking especially of the case where a class had a lot of slots, and that it would be more efficient for the original to remember one blast than for each blast slot to remember the original slot.
btw, the function md-slot-cell-type (I think! I have just been hacking that and may hvae changed something) returns nil for non-cell slots. Also, I very recently changed the default on the :cell option from nil to t.
I was debating with myself as to whether a blast should use a rule for non-cell slots, or cv slots, or ... I tentatively decided that every prototype inherited slot should be ruled. Mixing prototypes and class instantion, plus constraints, makes for a lot of abutting edges. I think I'm happy with this formulation for blasts, so once it's working, I think I'll try a knowledge representation problem, and see if it still holds up.
OK, sounds like you are happy, I'll lay off the backseat driving unless you choose to pursue anything above.
kt