On Sunday 02 October 2005 01:54, Kenny Tilton wrote:
Peter Denno wrote:
Hi kenny, et al.,
I was just updating the cells-gtk documentation. I am wondering whether I answered this question about using :initform with c? versus c-in correctly,
Q: What is the difference between using c-in and c? in a slot's :initform ? A: The usual semantics of :initform do not apply when :initform is given a c? rule. Instead of just setting the value at initialization, when c? is used, the value of the slot is checked and updated using the rule throughout program execution.
It is hard addressing this question without having gotten clear first the big picture. Then it is easy. So here is the big picture:
[aside: I see the link to Bill Clemtson's Cells write-up does not get all the way to the write-up. Too bad, that was pretty good.]
Fixed. Thanks. And yes, I agree it is a very good description.
... more stuff much further down in this email....
As you said here (after the URL)...
" If you are at all familiar with developing moderately complex software that is operated through a GUI, then you have probably learned this: Keeping what is presented through the GUI in-sync with what the user is allowed to do, and in-sync with the computational state of the program is often tedious, complicated work."
Right. Cells solve a general problem of keeping program state (including what is being displayed to the user) self-consistent. How often have we seen a bug where we delete a huge chink of text and the scroll bars do not get updated properly? Then we all know what to do: drag the corner of the window to resize it, and then the scroll bars get updated correctly. What is going on? The bug (not propagating the new amount of text to the scroll bars) is in just one branch of code, where text deletion is done. The branch of code triggered by resizing does not have that bug.
As I said, this problem of keeping program state self-consistent is a general one, but as you said, a complex GUI is a great case of that.
So how do Cells help, or, what do they do? Somewhat abstractly, but most vitally, the paradigm goes from procedural ("gee, they just deleted a bunch of text. now what do I have to do to make other things consistent with that deletion. Hmmm, I have to redraw the screen, rewrap the text, update the scroll bars, flag the file as unsaved, turn on the "Save" menu item,.....") to a declarative paradigm (in which you describe what reality should arise from other realities and an unseen engine magically arranges for all that to happen): a file is changed if the undo history includes a destructive operation; a scroll bar is visible if there is more text than fits in the window; the line-breaks slot is a function of the text and margins, etc. This raises the next question....
Are you kidding? No, Cells is the little system that lets you provide a rule or formula (really, any arbitrary Lisp form) for the slot of an instance. I say instance instead of class because you can provide rules at make-instance time, so two different instances of the same class can and often do have different rules as dictated by application semantics). The rules will normally use other slots of other instances in their
code. For example: :line-breaks (c? (let ((max-chars-on-line (floor (width
window)
(char-width (chosen-font window))))) ;; assuming
fixed-width for simplicity (calculate line-breaks (text window) (width window) max-chars-on-line)))
What Cells do is guarantee that if you change fonts or resize the window or change the amount of text, the text will get re-wrapped. Now if you have a sense of deja-vu...
That is because you once used a spreadsheet like Lotus 1-2-3. It does the same thing for accountants that Cells do for programmers with a bunch of interdependent data structures that are constantly changing in the face of new program inputs. In the above example, because of the declarative paradigm, the folks handling code to do user font changes do not need to worry at all about line breaks. The person who handles text wrapping has to understand what other slots affect the wrapping, but that is their job! And it is much easier than sitting there after a font change trying to guess at everything that might need updating. This is the same as programming a spreadsheet: the only person who has to worry about a spreadsheet cell for, say, "earned run average" is the one writing the formula for ERA. And even they do not have to worry about the cell for "innings pitched" changing -- the spreadsheet software takes care of change. And if you do not think that is a huge win, you are too young to remember that spreadsheet programs were the first killer apps for microcomputers. Why were they?
Quite simply because folks were doing spreadsheets on paper long before computers came around. Now imagine having a complex financial model on a paper spreadsheet and you want to look at the upshot of a change in the prime rate or tax rates. You must manually change everything that depends on the rate, then everything that depends on those. And you have to do it in the right order, so that for each cell being recalculated you are sure you have recalculated any used cell (if it too is affected directly or indirectly by the rate). If that sounds crazy...
Yeah, it is, it sucks, and that is why GUI programming is so hard, because that is exactly what a programmer is doing while writing procedural code to keep GUI state self-consistent. And that is why the lamest user knows to close and reopen a window if it goes haywire, because windows so frequently do (go haywire). Programmers writing code to handle some user input (such as hitting the delete key to erase a huge chunk of text) sit there trying to think up all the code they need to write to get all other related program state updated as well. And they have to handle depth: A depends on B, B depends on C, C depends on D, D changes, now get /everything/ consistent with that. Yummy.
At this point, I wager the main problem is simply the extent of the paradigm shift: this is so different from normal programming that your brain has decided to shut down. It is too simple: slots of an instance somehow being maintained by Lotus 1-2-3 (Cells, in fact). Brains do not like to be messed with like that. You know how to program, and this is not it! You have my sympathy, and I confess that even after re-inventing Cells (the prior art is vast) I found myself falling back into a procedural paradigm on new code for at least a year. But Cells are as great for programming (not just GUIs) as spreadsheets are for accountants, and as much fun, so do not get discouraged. Take the pill: Cells is Lotus 1-2-3 for slots of CLOS instances. Plus a little twist:
When a slot changes, Cells will call a generic function specializable on the name of the slot and the types of the instance, new value, and old value. Very handy, and I heard from a friend that Microsoft Excel will call a VBA function or something when a spreadsheet cell changes. Same idea: it is great to have a model working by itself, but how do we get anything useful out of all those values changing automatically? What if the "buy" flag on a stock goes to "on"? How do we arrange for the damn stock actually to be bought? We need the option of a callback where we can initiate an actual trade electronically.
So. What is the difference between c-in (aka c-input) and c? (aka c-formula)? Simple. You have this big financial spreadsheet you use for "what if?" analysis. How do you play? You have one or more cells where you experiment with different possibilities. You just type in different prime rates or exchange rates or corporate tax rates. A thousand other cells have formulas leading back directly or indirectly to the rate, but a few cells do not have formulas, you just type in "42".
Same with CLOS and Cells: some slots just get set by procedural code, and some slots get calculated directly or indirectly off the first slots. So, having sung the praises of formulas and declarative programming, where would we want to use the evil procedural paradigm and SETF some slot? Actually, astute readers might be asking the opposite question: is it turtles all the way down? If every Cell is calculated, what is the bottom Cell standing on? Just as a spreadsheet must have some cells into which we type data, the Cells-based CLOS model must have some slots into which the application deposits program inputs, and these are for a GUI application simply the OS events. Every event handler (or /the/ event handler depending on the OS) does nothing but setf the received values into slots such as mouse-positon or mouse-state or key-code, inter alia.
So those slots are initialized as (c-input nil), and other slots get (c-formula <lisp forms>). End of story, except...
...certain complexities related to efficiency have been ducked. c-input is needed (and should only be used) where the application developer knows for sure they will have to setf that slot at some point. Big efficiencies result where one can safely code ":some-slot 42".
Thanks, that was helpful. But I am still looking for a definition that nails the distinction. A definition is a single sentence that distinguishes a concept from other concepts in the domain of discourse. ;^)
How about this:
The two define different kinds of cells:
"A c-input cell is a cell whose value may be set through explicit declarative code, such as by using setf on the slot."
"A c-formula cell is a cell whose value is obtained through evaluation of a formula."
Note that the usual semantics of :initform do not apply when :initform is given by c-formula. Instead of just setting the value at initialization, the c-formula (an arbitrary lisp form) specifies the dynamic relationship between the slot's value and other aspects of the program state.
[It's actually this last part that is worth noting, I think. The semantics of :initform are different. IMO cells would be easier to learn had you not used :initform for the purpose of associating a formula with the slot. -- though :initform and whatever other keyword you might use to specify the c-formula would be mutually exclusive.]
BTW, I will soon be rolling out some new capabilities with cells-gtk, including GtkDrawingArea, and bindings for the GDK primitives for drawing. I hope to have a demo that presents a graph and uses cells to update the edges as the user drags the nodes around.
I also have a cells-gtk "lisp listener" (or, more usefully, some custom command shell) in the works.
Glad to hear it. Lisp is continuing to catch on, and a universal GUI is needed. I think Cells-Gtk will be it.
In the other email you sent you wrote:
k. tilton:
Last I looked, Cells-Gtk did not work much like a spreadsheet. Most slots (or more than I am used to) were c-input. Things still worked automatically, because Cells allows a change callback (defined by def-c-output) to SETF a c-input slot, but I for one try to avoid having things work that way because it loses some of the benefits of Cells. Most of them, actually. We are now back to a situation where the programmer has to figure out what things to SETF in a change callback. And we now no longer have a rule to look to for a slot, where we can see in one place the full algorithm for deciding a slot's value.
Anyway, my big long spreadsheet analogy might have folks who have seen only Cells-Gtk wondering what I am going on about, so I thought I should make this addendum. Everything I wrote is valid and it does explain the roles of c-input and c-formula, but Cells-Gtk may not feel very spreadsheet-y.
p. denno:
If you could find any spot where we might use cells more effectively, I'll try to fix it.