-------- Original Message -------- Subject: Re: [cells-devel] More newbie questions Date: Sun, 06 Mar 2005 15:55:29 -0500 From: Kenny Tilton ktilton@nyc.rr.com To: Mike J. Bell ckimyt@gmail.com References: cfae84d805030507185b924b26@mail.gmail.com 4229F03F.2050100@nyc.rr.com cfae84d8050306120043642e64@mail.gmail.com
Mike J. Bell wrote:
I'm quoting a bunch of stuff, sorry so long, but I think it's all related...comments below.
On Sat, 05 Mar 2005 12:45:35 -0500, Kenny Tilton ktilton@nyc.rr.com wrote:
Mike J. Bell wrote:
- Multi-method (?) calculations: I've seen simple examples in the
Cells docs that show a slot being dependent on another slot. It looks like you install this computation in the "destination" slot, i.e. the one that's dependent. What if you've got a situation where you have five slots, A B C D and E, that are parts of say three different objects, O1 O2 and O3. There's one computation that needs A B C D and E, and ends up modifying slots F G H and I in objects O4 and O5. Can it be done, making a single computation that depends on many slots in different instances that then updates slots in many instances? Or do you need to decompose this computation into (complicated!) pieces to be installed in each of the destination slots (F G H and I)?
You say complicated, I say "divide and conquer". Have you ever developed a finite state machine, breaking some complex problem down into a kazillion states? It seems painfully slow at first, until you realize how effortlessly the approach deals with the overall computation, which had you so confused that you resorted to using a finite state machine. :)
Cells is all about exposing the semantics of a slot in the one formula which determines the value of a slot. What you are describing is "classic" imperative programming, where this point in the code decides FGHI, another decides GH, another decides GHZ, etc etc. At which point no one can really tell us the semantics of H.
Now if you promise me that there is one and only one computation which decides FGHI, what I do in cases like that is simply create a new slot called FGHI (I wager the real name will be quite meaningful to anyone reading your code) and then F, G, H, and I can have extremely simple rules:
:f (c? (get-f (^fghi))) :g (c? (get-g (^fghi))) ..etc...
Can a change in one or more of A B C D and E cause a computation that updates C D F and G? (I.e. C and D are both inputs *and* outputs of the calculation. Note that self-cycling must be avoided!).
Again, sounds like you are resisting "divide and conquer". <g> I have been doing cells for about nine years now, and I have never had a problem decomposing big computations into so many slots mediated by so many cells. The cyclicity thing rarely materializes, but see my discussion of scrollbars in an earlier reply for how I handle that cyclic case.
- Multiple "owners" of responsibility, or aspect separation: (this
is related to #5 above) What if you have a normal calculation that happens 99% of the time that changes slot A into slot B (B is dependent on A). You write the formula into the slot code for B, and you're happy. But then, because of some strange requirement or other complicated design issue, you realize that B also depends, every once in a while (say 1%), on C D and E. But you don't want to stick this new computation and dependency code with the original code, because it will not only clutter reading, but also the complicated process may change in the future.
You say "clutter", I say "completely specified". <g> What is wrong with:
(c? (if (one-chance-in-a-million self) (big-hairy-mess (^c) (^d) (e))) (nice-and-easy (^a))))
And who cares if it changes in the future? Just maintain the formula, which will always fully document the semantics of a slot. One of the nice things about not using GOTOs is just /knowing/ without worrying that any given point in the code is always reached via visible control flow.
I think these comments strike into the heart of the differences between Cells and what I'm using. It seems to me they're both event-driven paradigms; Cells is declarative, and mine is imperative.
I guess my biggest concern about using Cells at this point (and I think I'm going to have to just try it and see!) is scalability, from a design perspective.
In the system I'm using right now, because the data and functions that operate on it are separate (and many to many), you get a really nice modularization of major aspects of an application. For instance, I wrote a program that loads and displays audio waveform data visually. The original code allowed integer multiples of zooming. The zoom factor was represented by one of these pieces of data. The drawing code listened for changes in this data in order to repaint the screen at the appropriate zoom level. The GUI widget (slider) posted changes to this zoom factor. This exemplifies the extreme separation of model, view and controller (as I'm sure Cells does as well).
Now, the really nice thing about the system I'm using is that when a new requirement came along about zoom levels, I didn't have to touch a single bit of old code. It went like this: a ruler was added to the screen to show a timecode display with the waveform. Because of strange "evenness" or regularity constraints on the design, the zoom level couldn't just be any old integer...it had to satisfy a divisibility property involving the sample rate of the audio. This contraint was written as a new function that took the sample rate and the zoom level as inputs, and published (possibly) a new zoom level that was "correct." The old code remained exactly the same, and the new code was added to the application, and everything just works.
In the declarative model that I think Cells uses, I would have had to change code that's already written, tested, debugged, etc. in order to effect the change in requirements.
Not sure what code you are referring to. I would not have the slider set the zoom, I would give the zoom a rule which watched the slider value, which would just go from zero to one. When the new requirement came along I would simply have the zoom rule look at the slider /and/ the sample rate. Seems easy enough to me, and more self-documenting than the approach you described. If the zoom value is coming out wrong, I like knowing without doubt exactly where to look. Hell, in the inspector I can even look at the code, which for debugging purposes I store in symbolic form in the Cell for those cases where a slot gets different rules for different instances so often that it is painful to work out which rule applies to some instance I am debugging.
I am not saying I do not find constraint programming interesting or that it would not be fun to simply drop a new rule in the basket as a way of modifying a program. On the other hand, I have heard nothing but nightmares about what it is like to program with multi-way and partial constraints, so I have not been tempted to add anything like that to Cells (so far <g>).
Granted this is a simple example, picked for its ease of describing, but I've had this pattern occur quite a bit coding in the system I'm using...i.e. 95% of my application stands as is, with most changes just introducing new code which operates on existing defined data in the application.
Please don't take any of my comments as negative criticism.
Not at all. We are both working the same very productive field, constraints of one kind or another, and we see the same benefits.
kt