[cells-gtk-devel] Re: [cells-devel] Use of :initform c? and c-in

Kenny Tilton ktilton at nyc.rr.com
Sun Oct 2 05:54:51 UTC 2005



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.]

As you said here (after the URL)...

>See: http://common-lisp.net/project/cells-gtk/faq.html
>

" 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".

>
>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.

-- 
Kenny

Why Lisp? http://wiki.alu.org/RtL_Highlight_Film

"I've wrestled with reality for 35 years, Doctor, and I'm happy to state I finally won out over it."
    Elwood P. Dowd, "Harvey", 1950






More information about the cells-gtk-devel mailing list