[cells-cvs] CVS update: cells/doc/cells-read-me.txt cells/doc/hw.lisp cells/doc/01-Cell-basics.lisp
Kenny Tilton
ktilton at common-lisp.net
Tue Dec 16 15:03:04 UTC 2003
Update of /project/cells/cvsroot/cells/doc
In directory common-lisp.net:/tmp/cvs-serv6620/doc
Modified Files:
01-Cell-basics.lisp
Added Files:
cells-read-me.txt hw.lisp
Log Message:
Preparing for first CVS of Cello
Date: Tue Dec 16 10:03:04 2003
Author: ktilton
Index: cells/doc/01-Cell-basics.lisp
diff -u cells/doc/01-Cell-basics.lisp:1.1.1.1 cells/doc/01-Cell-basics.lisp:1.2
--- cells/doc/01-Cell-basics.lisp:1.1.1.1 Sat Nov 8 18:45:24 2003
+++ cells/doc/01-Cell-basics.lisp Tue Dec 16 10:03:04 2003
@@ -1,420 +1,420 @@
-;; -*- mode: Lisp; Syntax: Common-Lisp; Package: cello; -*-
-;;______________________________________________________________
-;;
-;;
-;;
-;; Cell Basics
-;;
-;; Copyright © 1996,2003 by Kenny Tilton. All rights reserved.
-;;
-
-(in-package :cells)
-
-#|
-
-Here is a minimal primer on Cells, just enough for you to
-keep up with the next tutorial. That will be a substantial project
-in which we develop a CLOS object inspector.
-
-The Inspector project will give you a feel for what it is like to
-program with Cells and Cello /after/ you are fluent in the
-technology. The intent is not to teach you Cello, rather to
-motivate your learning it.
-
-So why the primer on Cells? If things like C? and CV and DEF-C-ECHO
-do not mean anything to you, the Hunh? Factor will be overwhelming.
-
-
-Cells
------
-Think of a CLOS slot as a cell in a paper spreadsheet, a financial
-modeling tool popular enough to make VisiCalc the first business
-killer app for microcomputers.
-
-As a child I watched my father toil at home for hours over paper
-spreadsheets with pencil and slide rule. After he changed one value,
-he had to propagate that change to other cells by first remembering
-which other ones included the changed cell in their computation.
-Then he had to do the calculations for those, erase, enter...
-and then repeating that process to propagate those changes in a
-cascade across the paper.
-
-VisiCalc let my father take the formula he had in mind and
-put it in (declare it to) the electronic spreadsheet. Then VisiCalc
-could do the tedious work: recalculating, knowing what to recalculate,
-and knowing in what order to recalculate.
-
-Cells do for programmers what electronic spreadsheets did for my father.
-Without Cells, CLOS slots are like cells of a paper spreadsheet.
-A single key-down event can cause a cascade of change throughout an
-application. The programmer has to arrange for it all to happen,
-all in the right order: delete any selected text, insert
-the new character, re-wrap the text, update the undo mechanism, revisit
-the menu statuses ("Cut" is no longer enabled), update the scroll bars,
-possibly scroll the window, flag the file as unsaved...
-
-With Cells, the programmer looks at program state differently. One
-asks, "How could I compute, at any point of runtime, a value for
-a given slot of an arbitrary instance, based only on other runtime state
-(other slots of other instances)." Great fun, by the way, as well as
-enforcing good programming practices like encapsulation.
-
-An example will help. Consider indeed the state of the "Cut" menu item.
-In some applications, programmers have a dozen places in their code
-where they tend to the status of the Cut menu item. One might be:
-
-(defun do-clear (edit-structure)
- (when (selected-range edit-structure)
- <set up undo>
- <toss selected text>
- <etc><etc>
- (menu-item-enable *edit-cut* nil)
- (menu-item-enable *edit-copy* nil)
- (menu-item-enable *edit-clear* nil)))
-
-Other programmers wait until the user clicks on the Edit menu,
-then decide just-in-time from program state whether the Cut item
-should be enabled:
-
-(defmethod prep-for-display ((m edit-menu))
- <lotsa other stuff>
- (when (typep (focus *app*) 'text-edit-widget)
- (menu-item-enable (find :cut (items m) :key #'item-name)
- (not (null (selected-range (focus *app*)))))))
-
-This latter programmer is ready for Cells, because they
-have already shifted from imperative to declarative thinking;
-they have learned to write code that works based not on what
-has happened lately, but instead only on the current program
-state (however it got that way).
-
-The Cell programmer writes:
-
-(make-instance 'menu-item
- :name :cut
- :label "Cut"
- :cmd-key +control-x+
- :actor #'do-cut
- :enabled (c? (when (typep (focus *app*) 'text-edit-widget)
- (not (null (selected-range (focus *app*)))))))
-
-...and now they can forget the menu item exists as they work
-on the rest of the application. The menu-item enabled status
-will stay current (correct) as the selected-range changes
-and as the focus itself changes as the user moves from field
-to field.
-
-That covers the spirit of Cells. Now let's look at the syntax
-and mechanics, with examples you can execute once you have
-loaded the Cells package. See the read-me.txt file in the
-root directory into which the Cello software was unzipped.
-
-We'll model a falling stone, where the distance fallen is half
-the product of the acceleration (due to gravity) and the
-square of the time falling.
-
-|#
-
-(in-package :cells)
-
-(defmodel stone ()
- ((accel :cell t :initarg :accel :initform 0 :accessor accel)
- (time-elapsed :cell t :initarg :time-elapsed
- :initform (cv 0)
- :accessor time-elapsed)
- (distance :cell t :initarg :distance :initform 0 :accessor distance))
- (:default-initargs
- :distance (c? (/ (* (accel self)
- (expt (time-elapsed self) 2))
- 2))))
-
-(def-c-echo accel ((self stone) new old old-bound-p)
- (trc "ECHO accel" :new new :old old :oldp old-bound-p)) ;; TRC provides print diagnostics
-
-(def-c-echo time-elapsed ((self stone)) ;; short form (I'm lazy)
- (trc "ECHO time-elapsed" :new new-value :old old-value :oldp old-value-boundp))
-
-(def-c-echo distance ((self stone))
- (format t "~&ECHO distance fallen: ~d feet" new-value))
-
-
-#|
-Let's look at non-standard syntax found in the forms above,
-in the order in which they appear:
-
- (defmodel ...
-
-defmodel is just a defclass wrapper which also sets up plumbing for Cells.
-
- ... :cell t ...
-
-Without this option, a model instance slot cannot be powered
-by a cell (and cell slot access overhead is avoided).
-
-With this option, one can specify what kind of Cell
-is to be defined: ephemeral, delta or t (normal). We'll leave
-those esoteric cell slot types for another tutorial and just
-specify t to get normal cells (the ones used 99% of the time).
-
- time-elapsed ... :initform (cv 0)...
-
-(CV <value>) allows the cellular slot (or "cell", for short)
-to be setf'ed. These are inputs to the dataflow,
-which usually flows from C? to C? but has to start somewhere.
-Since modern interactve applications are event-driven, in
-real-world Cello apps most CV dataflow inputs are slots closely
-corresponding to some system value, such as the position slots
-of a cell-powered Mouse class. Moving on...
-
-A naked value such as the 32 supplied for accel cannot be changed; a
-runtime error results from any such attempt. This makes Cells faster,
-because some plumbing can be skipped: no dependency gets recorded between
-the distance traveled and the acceleration. On the other hand, a more
-elaborate model might have the acceleration varying according to the distance
-between the stone and Earth (in which case we get into an advance
-topic for another day, namely how to handle circularity.)
-
-Next: (:default-initargs
- :distance (c? (/ (* (accel self)
- (expt (time-elapsed self) 2))
- 2)
-
-C? associates a rule with a cellular slot (or "cell", for short). Any
-read operation on another cell (directly or during a function call)
-establishes a dependency of distance on that cell -- unless that cell
-can never change. Why would a Cell not be able to change?
-
-Cell internals enforce a rule that a Cell with a naked value (ie, not wrapped
-in CV or C?) cannot be changed by client code (ok, (setf slot-value) is a backdoor).
-Cell internals enforce this, simply to make possible the optimization
-of leaving off the overhead of recording a pointless dependency.
-
-Next: (def-c-echo...
-
-Here is the signature for the DEF-C-ECHO macro:
-
- (defmacro def-c-echo (slotname (&optional (selfarg 'self)
- (newvarg 'new-value)
- (oldvarg 'old-value)
- (oldvargboundp 'old-value-boundp))
- &body echobody) ....)
-
-def-c-echo defines a generic method one can specialize on any of the four
-parameters. The method gets called when the slot value changes, and during
-initial processing by:
-
- (to-be....)
-
-TO-BE brings a new model instance to life, including calling
-any echos defined for cellular slots.
-
-Why not just do this in initialize-instance? We build complex
-models in the form of a tree of many model instances, any of
-which may depend on some other model instance to calculate
-some part of its state. Models find the one they are curious
-about by searching the tree.
-
-This means we cannot just bring a model instance to life at
-make-instance time; some cell rule may go looking for another
-model instance. We must wait until the instance is
-embedded in the larger model tree, then we can kick off to-be.
-
-Likewise, when we yank an instance from the larger model we
-will call NOT-TO-BE on it.
-
-The good news is that unless I am doing little tutorial examples
-I never think about calling TO-BE. Trees are implemented in part
-by a "kids" (short for "children") cell. The echo on that cell
-calls TO-BE on new kids and NOT-TO-BE on kids no longer in the list.
-
-Now evaluate the following:
-
-|#
-
-(defparameter *s2* (to-be (make-instance 'stone
- :accel 32 ;; (constant) feet per second per second
- :time-elapsed (cv 0))))
-
-#|
-
-...and observe:
-0> ECHO accel :NEW 32 :OLD NIL :OLDP NIL
-0> ECHO time-elapsed :NEW 0 :OLD NIL :OLDP NIL
-ECHO distance fallen: 0 feet
-
-
-Getting back to the output shown above, why echo output on a new instance?
-
-When we call TO-BE we want the instance to come to life. That means
-evaluating every rule so the dependencies get established, and
-propagating cell values outside the model (by calling the echo
-methods) to make sure the model and outside world (if only the
-system display) are consistent.
-
-;-----------------------------------------------------------
-Now let's get moving:
-
-|#
-
-(setf (time-elapsed *s2*) 1)
-
-#|
-...and observe:
-0> ECHO time-elapsed :NEW 1 :OLD 0 :OLDP T
-ECHO distance fallen: 16 feet
-
-behind the scenes:
-- the slot value time-elapsed got changed from 0 to 1
-- the time-elapsed echo was called
-- dependents on time-elapsed (here just distance) were recalculated
-- go to the first step, this time for the distance slot
-
-;-----------------------------------------------------------
-To see some optimizations at work, set the cell time-elapsed to
-the same value it already has:
-|#
-
-(setf (time-elapsed *s2*) 1)
-
-#| observe:
-nothing, since the slot-value did not in fact change.
-
-;-----------------------------------------------------------
-To test the enforcement of the Cell stricture against
-modifying cells holding naked values:
-|#
-
-(handler-case
- (setf (accel *s2*) 10)
- (t (error) (trc "error is" error)
- error))
-
-#| Observe:
-c-setting-debug > constant ACCEL in STONE may not be altered..init to (cv nil)
-0> error is #<SIMPLE-ERROR @ #x210925f2>
-
-;-----------------------------------------------------------
-Nor may ruled cells be modified arbitrarily:
-|#
-
-(handler-case
- (setf (distance *s2*) 42)
- (t (error) (trc "error is" error)
- error))
-
-#| observe:
-c-setting-debug > ruled DISTANCE in STONE may not be setf'ed
-0> error is #<SIMPLE-ERROR @ #x2123e392>
-
-;-----------------------------------------------------------
-Aside from C?, CV, and DEF-C-ECHO, another thing you will see
-in Cello code is how complex views are constructed using
-the Family class and its slot KIDS. Every model-object has a
-parent slot, which gets used along with a Family's kids slot to
-form simple trees navigable up and down.
-
-Model-objects also have slots for mdName and mdValue (don't
-worry camelcase-haters, that is a declining feature of my code).
-mdName lets the Family trees we build be treated as namespaces.
-mdValue just turns out to be very handy for a lot of things. For
-example, a check-box instance needs some place to indicate its
-boolean state.
-
-Now let's see Family in action, using code from the Handbook of
-Silly Examples. All I want to get across is that a lot happens
-when one changes the kids slot. It happens automatically, and
-it happens transparently, following the dataflow implicit in the
-rules we write, and the side-effects we specify via echo functions.
-
-The Silly Example below just shows the Summer (that which sums) getting
-a new mdValue as the kids change, along with some echo output. In real-world
-applications, where kids represent GUI elements often dependent on
-each other, vastly more can transpire before a simple push into a kids
-slot has run its course.
-
-Evaluate:
-|#
-
-(defmodel Summer (Family)
- ()
- (:default-initargs
- :kids (cv nil) ;; or we cannot add any addend kids later
- :mdValue (c? (reduce #'+ (kids self)
- :initial-value 0
- :key #'mdValue))))
-
-(def-c-echo .mdValue ((self Summer))
- (trc "The sum of the values of the kids is" new-value))
-
-(def-c-echo .kids ((self Summer))
- (trc "The values of the kids are" (mapcar #'mdValue new-value)))
-
-;-----------------------------------------------------------
-; now just evaluate each of the following forms one by one,
-; checking results after each to see what is going on
-;
-(defparameter *f1* (to-be (make-instance 'Summer)))
-
-#|
-observe:
-0> The sum of the values of the kids is 0
-0> The values of the kids are NIL
-
-;----------------------------------------------------------|#
-
-(push (make-instance 'model :mdValue 1) (kids *f1*))
-
-#| observe:
-0> The values of the kids are (1)
-0> The sum of the values of the kids is 1
-
-;----------------------------------------------------------|#
-
-(push (make-instance 'model :mdValue 2) (kids *f1*))
-
-#| observe:
-0> The values of the kids are (2 1)
-0> The sum of the values of the kids is 3
-
-;----------------------------------------------------------|#
-
-(setf (kids *f1*) nil)
-
-#| observe:
-0> The values of the kids are NIL
-0> The sum of the values of the kids is 0
-
-Now before closing, it occurs to me you'll need a little
-introduction to the semantics of ^SLOT-X macros generated
-by the DEFMODEL macro. Here is another way to define our stone:
-
-|#
-
-(setq *s2* (to-be (make-instance 'stone
- :accel 2
- :time-elapsed (cv 3)
- :distance (c? (+ (^accel) (^time-elapsed))))))
-
-#| In the olden days of Cells, when they were called
-Semaphors, the only way to establish a dependency
-was to use some form like:
-
- (^some-slot some-thing)
-
-That is no longer necessary. Now any dynamic access:
-
-(1) during evaluation of a form wrapped in (c?...)
-(2) to a cell, direct or inside some function
-(3) using accessors named in the defmodel form (not SLOT-VALUE)
-
-...establishes a dependency. So why still have the ^slot macros?
-
-One neat thing about the ^slot macros is that the default
-argument is SELF, an anaphor set up by C? and its ilk, so
-one can make many rules a little easier to follow by simply
-coding (^slot). Another is convenient specification of
-Synapses on dependencies, a more advanced topic we can
-ignore a while.
-
-
-|#
+;; -*- mode: Lisp; Syntax: Common-Lisp; Package: cello; -*-
+;;______________________________________________________________
+;;
+;;
+;;
+;; Cell Basics
+;;
+;; Copyright (c) 1996,2003 by Kenny Tilton. All rights reserved.
+;;
+
+(in-package :cells)
+
+#|
+
+Here is a minimal primer on Cells, just enough for you to
+keep up with the next tutorial. That will be a substantial project
+in which we develop a CLOS object inspector.
+
+The Inspector project will give you a feel for what it is like to
+program with Cells and Cello /after/ you are fluent in the
+technology. The intent is not to teach you Cello, rather to
+motivate your learning it.
+
+So why the primer on Cells? If things like C? and CV and DEF-C-ECHO
+do not mean anything to you, the Hunh? Factor will be overwhelming.
+
+
+Cells
+-----
+Think of a CLOS slot as a cell in a paper spreadsheet, a financial
+modeling tool popular enough to make VisiCalc the first business
+killer app for microcomputers.
+
+As a child I watched my father toil at home for hours over paper
+spreadsheets with pencil and slide rule. After he changed one value,
+he had to propagate that change to other cells by first remembering
+which other ones included the changed cell in their computation.
+Then he had to do the calculations for those, erase, enter...
+and then repeating that process to propagate those changes in a
+cascade across the paper.
+
+VisiCalc let my father take the formula he had in mind and
+put it in (declare it to) the electronic spreadsheet. Then VisiCalc
+could do the tedious work: recalculating, knowing what to recalculate,
+and knowing in what order to recalculate.
+
+Cells do for programmers what electronic spreadsheets did for my father.
+Without Cells, CLOS slots are like cells of a paper spreadsheet.
+A single key-down event can cause a cascade of change throughout an
+application. The programmer has to arrange for it all to happen,
+all in the right order: delete any selected text, insert
+the new character, re-wrap the text, update the undo mechanism, revisit
+the menu statuses ("Cut" is no longer enabled), update the scroll bars,
+possibly scroll the window, flag the file as unsaved...
+
+With Cells, the programmer looks at program state differently. One
+asks, "How could I compute, at any point of runtime, a value for
+a given slot of an arbitrary instance, based only on other runtime state
+(other slots of other instances)." Great fun, by the way, as well as
+enforcing good programming practices like encapsulation.
+
+An example will help. Consider indeed the state of the "Cut" menu item.
+In some applications, programmers have a dozen places in their code
+where they tend to the status of the Cut menu item. One might be:
+
+(defun do-clear (edit-structure)
+ (when (selected-range edit-structure)
+ <set up undo>
+ <toss selected text>
+ <etc><etc>
+ (menu-item-enable *edit-cut* nil)
+ (menu-item-enable *edit-copy* nil)
+ (menu-item-enable *edit-clear* nil)))
+
+Other programmers wait until the user clicks on the Edit menu,
+then decide just-in-time from program state whether the Cut item
+should be enabled:
+
+(defmethod prep-for-display ((m edit-menu))
+ <lotsa other stuff>
+ (when (typep (focus *app*) 'text-edit-widget)
+ (menu-item-enable (find :cut (items m) :key #'item-name)
+ (not (null (selected-range (focus *app*)))))))
+
+This latter programmer is ready for Cells, because they
+have already shifted from imperative to declarative thinking;
+they have learned to write code that works based not on what
+has happened lately, but instead only on the current program
+state (however it got that way).
+
+The Cell programmer writes:
+
+(make-instance 'menu-item
+ :name :cut
+ :label "Cut"
+ :cmd-key +control-x+
+ :actor #'do-cut
+ :enabled (c? (when (typep (focus *app*) 'text-edit-widget)
+ (not (null (selected-range (focus *app*)))))))
+
+...and now they can forget the menu item exists as they work
+on the rest of the application. The menu-item enabled status
+will stay current (correct) as the selected-range changes
+and as the focus itself changes as the user moves from field
+to field.
+
+That covers the spirit of Cells. Now let's look at the syntax
+and mechanics, with examples you can execute once you have
+loaded the Cells package. See the read-me.txt file in the
+root directory into which the Cello software was unzipped.
+
+We'll model a falling stone, where the distance fallen is half
+the product of the acceleration (due to gravity) and the
+square of the time falling.
+
+|#
+
+(in-package :cells)
+
+(defmodel stone ()
+ ((accel :cell t :initarg :accel :initform 0 :accessor accel)
+ (time-elapsed :cell t :initarg :time-elapsed
+ :initform (cv 0)
+ :accessor time-elapsed)
+ (distance :cell t :initarg :distance :initform 0 :accessor distance))
+ (:default-initargs
+ :distance (c? (/ (* (accel self)
+ (expt (time-elapsed self) 2))
+ 2))))
+
+(def-c-echo accel ((self stone) new old old-bound-p)
+ (trc "ECHO accel" :new new :old old :oldp old-bound-p)) ;; TRC provides print diagnostics
+
+(def-c-echo time-elapsed ((self stone)) ;; short form (I'm lazy)
+ (trc "ECHO time-elapsed" :new new-value :old old-value :oldp old-value-boundp))
+
+(def-c-echo distance ((self stone))
+ (format t "~&ECHO distance fallen: ~d feet" new-value))
+
+
+#|
+Let's look at non-standard syntax found in the forms above,
+in the order in which they appear:
+
+ (defmodel ...
+
+defmodel is just a defclass wrapper which also sets up plumbing for Cells.
+
+ ... :cell t ...
+
+Without this option, a model instance slot cannot be powered
+by a cell (and cell slot access overhead is avoided).
+
+With this option, one can specify what kind of Cell
+is to be defined: ephemeral, delta or t (normal). We'll leave
+those esoteric cell slot types for another tutorial and just
+specify t to get normal cells (the ones used 99% of the time).
+
+ time-elapsed ... :initform (cv 0)...
+
+(CV <value>) allows the cellular slot (or "cell", for short)
+to be setf'ed. These are inputs to the dataflow,
+which usually flows from C? to C? but has to start somewhere.
+Since modern interactve applications are event-driven, in
+real-world Cello apps most CV dataflow inputs are slots closely
+corresponding to some system value, such as the position slots
+of a cell-powered Mouse class. Moving on...
+
+A naked value such as the 32 supplied for accel cannot be changed; a
+runtime error results from any such attempt. This makes Cells faster,
+because some plumbing can be skipped: no dependency gets recorded between
+the distance traveled and the acceleration. On the other hand, a more
+elaborate model might have the acceleration varying according to the distance
+between the stone and Earth (in which case we get into an advance
+topic for another day, namely how to handle circularity.)
+
+Next: (:default-initargs
+ :distance (c? (/ (* (accel self)
+ (expt (time-elapsed self) 2))
+ 2)
+
+C? associates a rule with a cellular slot (or "cell", for short). Any
+read operation on another cell (directly or during a function call)
+establishes a dependency of distance on that cell -- unless that cell
+can never change. Why would a Cell not be able to change?
+
+Cell internals enforce a rule that a Cell with a naked value (ie, not wrapped
+in CV or C?) cannot be changed by client code (ok, (setf slot-value) is a backdoor).
+Cell internals enforce this, simply to make possible the optimization
+of leaving off the overhead of recording a pointless dependency.
+
+Next: (def-c-echo...
+
+Here is the signature for the DEF-C-ECHO macro:
+
+ (defmacro def-c-echo (slotname (&optional (selfarg 'self)
+ (newvarg 'new-value)
+ (oldvarg 'old-value)
+ (oldvargboundp 'old-value-boundp))
+ &body echobody) ....)
+
+def-c-echo defines a generic method one can specialize on any of the four
+parameters. The method gets called when the slot value changes, and during
+initial processing by:
+
+ (to-be....)
+
+TO-BE brings a new model instance to life, including calling
+any echos defined for cellular slots.
+
+Why not just do this in initialize-instance? We build complex
+models in the form of a tree of many model instances, any of
+which may depend on some other model instance to calculate
+some part of its state. Models find the one they are curious
+about by searching the tree.
+
+This means we cannot just bring a model instance to life at
+make-instance time; some cell rule may go looking for another
+model instance. We must wait until the instance is
+embedded in the larger model tree, then we can kick off to-be.
+
+Likewise, when we yank an instance from the larger model we
+will call NOT-TO-BE on it.
+
+The good news is that unless I am doing little tutorial examples
+I never think about calling TO-BE. Trees are implemented in part
+by a "kids" (short for "children") cell. The echo on that cell
+calls TO-BE on new kids and NOT-TO-BE on kids no longer in the list.
+
+Now evaluate the following:
+
+|#
+
+(defparameter *s2* (to-be (make-instance 'stone
+ :accel 32 ;; (constant) feet per second per second
+ :time-elapsed (cv 0))))
+
+#|
+
+...and observe:
+0> ECHO accel :NEW 32 :OLD NIL :OLDP NIL
+0> ECHO time-elapsed :NEW 0 :OLD NIL :OLDP NIL
+ECHO distance fallen: 0 feet
+
+
+Getting back to the output shown above, why echo output on a new instance?
+
+When we call TO-BE we want the instance to come to life. That means
+evaluating every rule so the dependencies get established, and
+propagating cell values outside the model (by calling the echo
+methods) to make sure the model and outside world (if only the
+system display) are consistent.
+
+;-----------------------------------------------------------
+Now let's get moving:
+
+|#
+
+(setf (time-elapsed *s2*) 1)
+
+#|
+...and observe:
+0> ECHO time-elapsed :NEW 1 :OLD 0 :OLDP T
+ECHO distance fallen: 16 feet
+
+behind the scenes:
+- the slot value time-elapsed got changed from 0 to 1
+- the time-elapsed echo was called
+- dependents on time-elapsed (here just distance) were recalculated
+- go to the first step, this time for the distance slot
+
+;-----------------------------------------------------------
+To see some optimizations at work, set the cell time-elapsed to
+the same value it already has:
+|#
+
+(setf (time-elapsed *s2*) 1)
+
+#| observe:
+nothing, since the slot-value did not in fact change.
+
+;-----------------------------------------------------------
+To test the enforcement of the Cell stricture against
+modifying cells holding naked values:
+|#
+
+(handler-case
+ (setf (accel *s2*) 10)
+ (t (error) (trc "error is" error)
+ error))
+
+#| Observe:
+c-setting-debug > constant ACCEL in STONE may not be altered..init to (cv nil)
+0> error is #<SIMPLE-ERROR @ #x210925f2>
+
+;-----------------------------------------------------------
+Nor may ruled cells be modified arbitrarily:
+|#
+
+(handler-case
+ (setf (distance *s2*) 42)
+ (t (error) (trc "error is" error)
+ error))
+
+#| observe:
+c-setting-debug > ruled DISTANCE in STONE may not be setf'ed
+0> error is #<SIMPLE-ERROR @ #x2123e392>
+
+;-----------------------------------------------------------
+Aside from C?, CV, and DEF-C-ECHO, another thing you will see
+in Cello code is how complex views are constructed using
+the Family class and its slot KIDS. Every model-object has a
+parent slot, which gets used along with a Family's kids slot to
+form simple trees navigable up and down.
+
+Model-objects also have slots for md-name and md-value (don't
+worry camelcase-haters, that is a declining feature of my code).
+md-name lets the Family trees we build be treated as namespaces.
+md-value just turns out to be very handy for a lot of things. For
+example, a check-box instance needs some place to indicate its
+boolean state.
+
+Now let's see Family in action, using code from the Handbook of
+Silly Examples. All I want to get across is that a lot happens
+when one changes the kids slot. It happens automatically, and
+it happens transparently, following the dataflow implicit in the
+rules we write, and the side-effects we specify via echo functions.
+
+The Silly Example below just shows the Summer (that which sums) getting
+a new md-value as the kids change, along with some echo output. In real-world
+applications, where kids represent GUI elements often dependent on
+each other, vastly more can transpire before a simple push into a kids
+slot has run its course.
+
+Evaluate:
+|#
+
+(defmodel Summer (Family)
+ ()
+ (:default-initargs
+ :kids (cv nil) ;; or we cannot add any addend kids later
+ :md-value (c? (reduce #'+ (kids self)
+ :initial-value 0
+ :key #'md-value))))
+
+(def-c-echo .md-value ((self Summer))
+ (trc "The sum of the values of the kids is" new-value))
+
+(def-c-echo .kids ((self Summer))
+ (trc "The values of the kids are" (mapcar #'md-value new-value)))
+
+;-----------------------------------------------------------
+; now just evaluate each of the following forms one by one,
+; checking results after each to see what is going on
+;
+(defparameter *f1* (to-be (make-instance 'Summer)))
+
+#|
+observe:
+0> The sum of the values of the kids is 0
+0> The values of the kids are NIL
+
+;----------------------------------------------------------|#
+
+(push (make-instance 'model :md-value 1) (kids *f1*))
+
+#| observe:
+0> The values of the kids are (1)
+0> The sum of the values of the kids is 1
+
+;----------------------------------------------------------|#
+
+(push (make-instance 'model :md-value 2) (kids *f1*))
+
+#| observe:
+0> The values of the kids are (2 1)
+0> The sum of the values of the kids is 3
+
+;----------------------------------------------------------|#
+
+(setf (kids *f1*) nil)
+
+#| observe:
+0> The values of the kids are NIL
+0> The sum of the values of the kids is 0
+
+Now before closing, it occurs to me you'll need a little
+introduction to the semantics of ^SLOT-X macros generated
+by the DEFMODEL macro. Here is another way to define our stone:
+
+|#
+
+(setq *s2* (to-be (make-instance 'stone
+ :accel 2
+ :time-elapsed (cv 3)
+ :distance (c? (+ (^accel) (^time-elapsed))))))
+
+#| In the olden days of Cells, when they were called
+Semaphors, the only way to establish a dependency
+was to use some form like:
+
+ (^some-slot some-thing)
+
+That is no longer necessary. Now any dynamic access:
+
+(1) during evaluation of a form wrapped in (c?...)
+(2) to a cell, direct or inside some function
+(3) using accessors named in the defmodel form (not SLOT-VALUE)
+
+...establishes a dependency. So why still have the ^slot macros?
+
+One neat thing about the ^slot macros is that the default
+argument is SELF, an anaphor set up by C? and its ilk, so
+one can make many rules a little easier to follow by simply
+coding (^slot). Another is convenient specification of
+Synapses on dependencies, a more advanced topic we can
+ignore a while.
+
+
+|#
More information about the Cells-cvs
mailing list