[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-devel
mailing list