From: Frank Buss
Subject: dataflow basics
Date: 
Message-ID: <1ky35tbjpdcr1$.xnfs62zmvz2l$.dlg@40tude.net>
After playing a bit with Cells, I want to discuss the basic concepts of
dataflow programming. The power of Lisp comes from using many of its
constructs in an orthogonal way, e.g. you can mix defuns and CLOS in the
way you want it. Cells is limited to CLOS, so in the following I'm
developing a framework for playing with dataflow concepts without CLOS.

The implementation is not as fast and not as easy to use as Cells, but it
is easy to understand and could be a base for testing some ideas.

You can use it like this:

(defparameter a (make-in-cell 0))
(defparameter b (make-in-cell 0))
(defparameter result
  (make-out-cell
   (lambda ()
     (+ (cell-value a) (cell-value b)))
   (list a b)))

#'make-in-cell and #'make-out-cell returns cell objects, the same way like
#'make-array returns an array. The code above defines two in-cells, which
are bound to a and b, and an out cell, which is updated, when the cell a or
b is updated (second parameter) and with an update function (first
parameter). #'cell-value returns the value of a cell (think of it like
#'aref for arrays).

Now you can play with it like this:

CL-USER > (cell-value result)
0

CL-USER > (setf (cell-value a) 1)
NIL

CL-USER > (setf (cell-value b) 3)
NIL

CL-USER > (cell-value result)
4

Finally there is a #'set-cell-callback function, which is called whenever a
cell was updated. We register a callback for the result cell:

(set-cell-callback
 result
 (lambda ()
   (format t "result changed to ~a~%" (cell-value result))))

and when you change one input cell, the function is called:

CL-USER > (setf (cell-value a) 2)
result changed to 5
NIL

This is nothing special and Cells can it, too. But you could define it at
runtime, too. A simple example of a spreadsheet:

First a function, which creates a spreadsheet, with any values initially
defined as in-cells:

(defun make-spreadsheet (x y)
  (loop for i from 0 below x
        with s =  (make-array (list x y)) 
        finally (return s) do
        (loop for j from 0 below y do
              (setf (aref s i j) (make-in-cell 0)))))

As an example, you want to calculate the gross values of 10 net values and
sum it. First setup the spreadsheet:

(loop for i from 1 to 10 do
      (setf (aref s 1 i)
            (make-out-cell (create-tax-function i)
                           (list (aref s 0 i)))))

(setf (aref s 1 11)
      (make-out-cell (lambda ()
                       (loop for i from 1 to 10 sum
                             (cell-value (aref s 1 i))))
                     (loop for i from 1 to 10 collect (aref s 1 i))))

(setf (cell-value (aref s 0 0)) 1.1)
(setf (aref s 0 0) "net")
(setf (aref s 1 0) "gross")
(setf (aref s 0 11) "sum:")

and now you can use it like this:

CL-USER > (setf (cell-value (aref s 0 1)) 20.00)
NIL

CL-USER > (setf (cell-value (aref s 0 2)) 5.60)
NIL

CL-USER > (print-spreadsheet s)
net     gross   
20.0    23.799999999999997
5.6     6.664   
0       0.0     
0       0.0     
0       0.0     
0       0.0     
0       0.0     
0       0.0     
0       0.0     
0       0.0     
sum:    30.464  
NIL


I don't know how it is called, but I would name it "instance based
dataflow", opposed to Cells, which is type based (you define a fixed set of
rules per CLOS class, if I didn't missed a way to do instance based
dataflow with Cells).

Of course, instance based dataflow could be easily enhanced to type based
dataflow by calling the binding functions after the instance was created.
Some other improvements: Like in Cells, some macrology could extract all
references from the update functions automaticly.

There are some open questions:

- how do you handle cyclic dependencies?
- is it a good idea to pass the old value to the callback function, too?
- do we need lazy evaluation of the out-cells?

My goal is a dataflow theory, which provides a powerful base for all
possible dataflow tasks and which is clean and consistent. Maybe this
simple implementation can help testing it.


Appendix: the code which implements make-*-cell etc., including the
examples:


(defun make-in-cell (init-value)
  "create a cell, to which values can be written and which can be read"
  (let ((symbol (gensym)))
    (setf (symbol-value symbol) init-value)
    symbol))

(defun update-cell-dependencies (cell)
  "internal function to update out cell values"
  (loop for out-cell in (get cell 'update-list) do
        (let ((new-value (funcall (get out-cell 'update-function)))
              (callback-function (get out-cell 'callback-function)))
          (setf (symbol-value out-cell) new-value)
          (when callback-function
            (funcall callback-function))
          (update-cell-dependencies out-cell))))

(defun make-out-cell (update-function dependencies)
  "create a cell, which can be read, only"
  (let ((symbol (gensym)))
    (setf (get symbol 'update-function) update-function)
    (loop for foreign-symbol in dependencies do
          (let ((update-list (get foreign-symbol 'update-list)))
            (if update-list
                (push symbol update-list)
              (setf (get foreign-symbol 'update-list) (list symbol)))))
    (setf (symbol-value symbol) (funcall update-function))
    symbol))

(defun cell-value (cell)
  "get the value of a cell"
  (symbol-value cell))

(defun set-cell-value (cell value)
  "set the value of a cell"
  (when (get cell 'update-function)
    (error "can't write to out cell"))
  (setf (symbol-value cell) value)
  (update-cell-dependencies cell))

(defun set-cell-callback (cell callback-function)
  "set a callback function for an output cell"
  (setf (get cell 'callback-function) callback-function))

; define setf expander for cell-values
(defsetf cell-value set-cell-value)

; sample

(defparameter a (make-in-cell 0))
(defparameter b (make-in-cell 0))
(defparameter result
  (make-out-cell
   (lambda ()
     (+ (cell-value a) (cell-value b)))
   (list a b)))

(cell-value result)
(setf (cell-value a) 1)
(setf (cell-value b) 3)
(cell-value result)

(set-cell-callback
 result
 (lambda ()
   (format t "result changed to ~a~%" (cell-value result))))

(setf (cell-value a) 2)


(defun make-spreadsheet (x y)
  (loop for i from 0 below x
        with s =  (make-array (list x y)) 
        finally (return s) do
        (loop for j from 0 below y do
              (setf (aref s i j) (make-in-cell 0)))))

(defun print-filled (object &optional (length 8))
  (let ((out (with-output-to-string (s)
               (format s "~a" object))))
    (format t "~a" out)
    (loop while (< (length out) length) do
          (princ " ")
          (decf length))))

(defun print-spreadsheet (s)
  (let ((dimensions (array-dimensions s)))
    (loop for y from 0 below (cadr dimensions) do
          (loop for x from 0 below (car dimensions) do
                (let ((value (aref s x y)))
                  (print-filled
                   (if (symbolp value)
                       (cell-value value)
                     value))))
          (terpri))))

(defun create-tax-function (i)
  (lambda ()
    (* 1.19 (cell-value (aref s 0 i)))))

(defparameter s (make-spreadsheet 2 12))

(loop for i from 1 to 10 do
      (setf (aref s 1 i)
            (make-out-cell (create-tax-function i)
                           (list (aref s 0 i)))))

(setf (aref s 1 11)
      (make-out-cell (lambda ()
                       (loop for i from 1 to 10 sum
                             (cell-value (aref s 1 i))))
                     (loop for i from 1 to 10 collect (aref s 1 i))))

(setf (cell-value (aref s 0 0)) 1.1)
(setf (aref s 0 0) "net")
(setf (aref s 1 0) "gross")
(setf (aref s 0 11) "sum:")

(setf (cell-value (aref s 0 1)) 20.00)
(setf (cell-value (aref s 0 2)) 5.60)

(print-spreadsheet s)


-- 
Frank Buss, ··@frank-buss.de
http://www.frank-buss.de, http://www.it4-systems.de

From: levy
Subject: Re: dataflow basics
Date: 
Message-ID: <1175881082.756916.115910@d57g2000hsg.googlegroups.com>
You can do all of that with cells I think and also with computed-class
too (see web pages) and even if you miss something I believe its
easier to add it to those libraries than implement your own.

Computed class has computed variables, computed slots, computed
standalone cells, computed whatever... per instance.

from http://common-lisp.net/project/computed-class/

This library provides various tools that help dealing with constraint
based change propagation in Lisp programs. One such tool is computed-
class which is a metaclass for CLOS classes that support per instance
lazy computation of slot values while keeping track of dependencies
and managing invalidation of such computed results transparently. The
engine behind this is fully integrated with CLOS but it is also
independent from it. It makes it possible to use computed local
variables that can even be captured by lambda's (see clet) and also to
explicitly handle the computed-state structs (the cells) as first-
class citizens.

levy

Frank Buss �rta:
> After playing a bit with Cells, I want to discuss the basic concepts of
> dataflow programming. The power of Lisp comes from using many of its
> constructs in an orthogonal way, e.g. you can mix defuns and CLOS in the
> way you want it. Cells is limited to CLOS, so in the following I'm
> developing a framework for playing with dataflow concepts without CLOS.
>
> The implementation is not as fast and not as easy to use as Cells, but it
> is easy to understand and could be a base for testing some ideas.
>
> You can use it like this:
>
> (defparameter a (make-in-cell 0))
> (defparameter b (make-in-cell 0))
> (defparameter result
>   (make-out-cell
>    (lambda ()
>      (+ (cell-value a) (cell-value b)))
>    (list a b)))
>
> #'make-in-cell and #'make-out-cell returns cell objects, the same way like
> #'make-array returns an array. The code above defines two in-cells, which
> are bound to a and b, and an out cell, which is updated, when the cell a or
> b is updated (second parameter) and with an update function (first
> parameter). #'cell-value returns the value of a cell (think of it like
> #'aref for arrays).
>
> Now you can play with it like this:
>
> CL-USER > (cell-value result)
> 0
>
> CL-USER > (setf (cell-value a) 1)
> NIL
>
> CL-USER > (setf (cell-value b) 3)
> NIL
>
> CL-USER > (cell-value result)
> 4
>
> Finally there is a #'set-cell-callback function, which is called whenever a
> cell was updated. We register a callback for the result cell:
>
> (set-cell-callback
>  result
>  (lambda ()
>    (format t "result changed to ~a~%" (cell-value result))))
>
> and when you change one input cell, the function is called:
>
> CL-USER > (setf (cell-value a) 2)
> result changed to 5
> NIL
>
> This is nothing special and Cells can it, too. But you could define it at
> runtime, too. A simple example of a spreadsheet:
>
> First a function, which creates a spreadsheet, with any values initially
> defined as in-cells:
>
> (defun make-spreadsheet (x y)
>   (loop for i from 0 below x
>         with s =  (make-array (list x y))
>         finally (return s) do
>         (loop for j from 0 below y do
>               (setf (aref s i j) (make-in-cell 0)))))
>
> As an example, you want to calculate the gross values of 10 net values and
> sum it. First setup the spreadsheet:
>
> (loop for i from 1 to 10 do
>       (setf (aref s 1 i)
>             (make-out-cell (create-tax-function i)
>                            (list (aref s 0 i)))))
>
> (setf (aref s 1 11)
>       (make-out-cell (lambda ()
>                        (loop for i from 1 to 10 sum
>                              (cell-value (aref s 1 i))))
>                      (loop for i from 1 to 10 collect (aref s 1 i))))
>
> (setf (cell-value (aref s 0 0)) 1.1)
> (setf (aref s 0 0) "net")
> (setf (aref s 1 0) "gross")
> (setf (aref s 0 11) "sum:")
>
> and now you can use it like this:
>
> CL-USER > (setf (cell-value (aref s 0 1)) 20.00)
> NIL
>
> CL-USER > (setf (cell-value (aref s 0 2)) 5.60)
> NIL
>
> CL-USER > (print-spreadsheet s)
> net     gross
> 20.0    23.799999999999997
> 5.6     6.664
> 0       0.0
> 0       0.0
> 0       0.0
> 0       0.0
> 0       0.0
> 0       0.0
> 0       0.0
> 0       0.0
> sum:    30.464
> NIL
>
>
> I don't know how it is called, but I would name it "instance based
> dataflow", opposed to Cells, which is type based (you define a fixed set of
> rules per CLOS class, if I didn't missed a way to do instance based
> dataflow with Cells).
>
> Of course, instance based dataflow could be easily enhanced to type based
> dataflow by calling the binding functions after the instance was created.
> Some other improvements: Like in Cells, some macrology could extract all
> references from the update functions automaticly.
>
> There are some open questions:
>
> - how do you handle cyclic dependencies?
> - is it a good idea to pass the old value to the callback function, too?
> - do we need lazy evaluation of the out-cells?
>
> My goal is a dataflow theory, which provides a powerful base for all
> possible dataflow tasks and which is clean and consistent. Maybe this
> simple implementation can help testing it.
>
>
> Appendix: the code which implements make-*-cell etc., including the
> examples:
>
>
> (defun make-in-cell (init-value)
>   "create a cell, to which values can be written and which can be read"
>   (let ((symbol (gensym)))
>     (setf (symbol-value symbol) init-value)
>     symbol))
>
> (defun update-cell-dependencies (cell)
>   "internal function to update out cell values"
>   (loop for out-cell in (get cell 'update-list) do
>         (let ((new-value (funcall (get out-cell 'update-function)))
>               (callback-function (get out-cell 'callback-function)))
>           (setf (symbol-value out-cell) new-value)
>           (when callback-function
>             (funcall callback-function))
>           (update-cell-dependencies out-cell))))
>
> (defun make-out-cell (update-function dependencies)
>   "create a cell, which can be read, only"
>   (let ((symbol (gensym)))
>     (setf (get symbol 'update-function) update-function)
>     (loop for foreign-symbol in dependencies do
>           (let ((update-list (get foreign-symbol 'update-list)))
>             (if update-list
>                 (push symbol update-list)
>               (setf (get foreign-symbol 'update-list) (list symbol)))))
>     (setf (symbol-value symbol) (funcall update-function))
>     symbol))
>
> (defun cell-value (cell)
>   "get the value of a cell"
>   (symbol-value cell))
>
> (defun set-cell-value (cell value)
>   "set the value of a cell"
>   (when (get cell 'update-function)
>     (error "can't write to out cell"))
>   (setf (symbol-value cell) value)
>   (update-cell-dependencies cell))
>
> (defun set-cell-callback (cell callback-function)
>   "set a callback function for an output cell"
>   (setf (get cell 'callback-function) callback-function))
>
> ; define setf expander for cell-values
> (defsetf cell-value set-cell-value)
>
> ; sample
>
> (defparameter a (make-in-cell 0))
> (defparameter b (make-in-cell 0))
> (defparameter result
>   (make-out-cell
>    (lambda ()
>      (+ (cell-value a) (cell-value b)))
>    (list a b)))
>
> (cell-value result)
> (setf (cell-value a) 1)
> (setf (cell-value b) 3)
> (cell-value result)
>
> (set-cell-callback
>  result
>  (lambda ()
>    (format t "result changed to ~a~%" (cell-value result))))
>
> (setf (cell-value a) 2)
>
>
> (defun make-spreadsheet (x y)
>   (loop for i from 0 below x
>         with s =  (make-array (list x y))
>         finally (return s) do
>         (loop for j from 0 below y do
>               (setf (aref s i j) (make-in-cell 0)))))
>
> (defun print-filled (object &optional (length 8))
>   (let ((out (with-output-to-string (s)
>                (format s "~a" object))))
>     (format t "~a" out)
>     (loop while (< (length out) length) do
>           (princ " ")
>           (decf length))))
>
> (defun print-spreadsheet (s)
>   (let ((dimensions (array-dimensions s)))
>     (loop for y from 0 below (cadr dimensions) do
>           (loop for x from 0 below (car dimensions) do
>                 (let ((value (aref s x y)))
>                   (print-filled
>                    (if (symbolp value)
>                        (cell-value value)
>                      value))))
>           (terpri))))
>
> (defun create-tax-function (i)
>   (lambda ()
>     (* 1.19 (cell-value (aref s 0 i)))))
>
> (defparameter s (make-spreadsheet 2 12))
>
> (loop for i from 1 to 10 do
>       (setf (aref s 1 i)
>             (make-out-cell (create-tax-function i)
>                            (list (aref s 0 i)))))
>
> (setf (aref s 1 11)
>       (make-out-cell (lambda ()
>                        (loop for i from 1 to 10 sum
>                              (cell-value (aref s 1 i))))
>                      (loop for i from 1 to 10 collect (aref s 1 i))))
>
> (setf (cell-value (aref s 0 0)) 1.1)
> (setf (aref s 0 0) "net")
> (setf (aref s 1 0) "gross")
> (setf (aref s 0 11) "sum:")
>
> (setf (cell-value (aref s 0 1)) 20.00)
> (setf (cell-value (aref s 0 2)) 5.60)
>
> (print-spreadsheet s)
>
>
> --
> Frank Buss, ··@frank-buss.de
> http://www.frank-buss.de, http://www.it4-systems.de
From: Frank Buss
Subject: Re: dataflow basics
Date: 
Message-ID: <kv4h09hwdd4f.11b75eskmycb9.dlg@40tude.net>
levy wrote:

> You can do all of that with cells I think and also with computed-class
> too (see web pages) and even if you miss something I believe its
> easier to add it to those libraries than implement your own.
> 
> Computed class has computed variables, computed slots, computed
> standalone cells, computed whatever... per instance.
> 
> from http://common-lisp.net/project/computed-class/

Computed standalone cells looks very interesting. Are you one of the author
of the library? To quote the page "We had trouble with Cells  that we
couldn't easily fix" and if you think all can be done with Cells, what were
the problems you had that you don't fix Cells, but write your own
implementation, like I tried?

-- 
Frank Buss, ··@frank-buss.de
http://www.frank-buss.de, http://www.it4-systems.de
From: Ken Tilton
Subject: Re: dataflow basics
Date: 
Message-ID: <erwRh.3165$SJ5.2055@newsfe12.lga>
Frank Buss wrote:
> After playing a bit with Cells, I want to discuss the basic concepts of
> dataflow programming. The power of Lisp comes from using many of its
> constructs in an orthogonal way, e.g. you can mix defuns and CLOS in the
> way you want it. Cells is limited to CLOS, so in the following I'm
> developing a framework for playing with dataflow concepts without CLOS.

The first thing to do upon discovering Lisp is to fire up your C IDE and 
start writing a Lisp. In C. The first thing to do upon discovering Cells 
is to roll your own. I think we'll hold the annual dataflow conference 
in Monte Carlo, just before the Grand Prix. All talks must be in the 
form of karaoke sung to pop tunes under four minutes in length.


> 
> The implementation is not as fast and not as easy to use as Cells, but it
> is easy to understand and could be a base for testing some ideas.
> 
> You can use it like this:
> 
> (defparameter a (make-in-cell 0))
> (defparameter b (make-in-cell 0))
> (defparameter result
>   (make-out-cell
>    (lambda ()
>      (+ (cell-value a) (cell-value b)))
>    (list a b)))
> 
> #'make-in-cell and #'make-out-cell returns cell objects, the same way like
> #'make-array returns an array. The code above defines two in-cells, which
> are bound to a and b, and an out cell, which is updated, when the cell a or
> b is updated (second parameter) and with an update function (first
> parameter). #'cell-value returns the value of a cell (think of it like
> #'aref for arrays).
> 
> Now you can play with it like this:
> 
> CL-USER > (cell-value result)
> 0
> 
> CL-USER > (setf (cell-value a) 1)
> NIL
> 
> CL-USER > (setf (cell-value b) 3)
> NIL
> 
> CL-USER > (cell-value result)
> 4
> 
> Finally there is a #'set-cell-callback function, which is called whenever a
> cell was updated. We register a callback for the result cell:
> 
> (set-cell-callback
>  result
>  (lambda ()
>    (format t "result changed to ~a~%" (cell-value result))))
> 
> and when you change one input cell, the function is called:
> 
> CL-USER > (setf (cell-value a) 2)
> result changed to 5
> NIL
> 
> This is nothing special and Cells can it, too. But you could define it at
> runtime, too. A simple example of a spreadsheet:
> 
> First a function, which creates a spreadsheet, with any values initially
> defined as in-cells:
> 
> (defun make-spreadsheet (x y)
>   (loop for i from 0 below x
>         with s =  (make-array (list x y)) 
>         finally (return s) do
>         (loop for j from 0 below y do
>               (setf (aref s i j) (make-in-cell 0)))))
> 
> As an example, you want to calculate the gross values of 10 net values and
> sum it. First setup the spreadsheet:
> 
> (loop for i from 1 to 10 do
>       (setf (aref s 1 i)
>             (make-out-cell (create-tax-function i)
>                            (list (aref s 0 i)))))
> 
> (setf (aref s 1 11)
>       (make-out-cell (lambda ()
>                        (loop for i from 1 to 10 sum
>                              (cell-value (aref s 1 i))))
>                      (loop for i from 1 to 10 collect (aref s 1 i))))
> 
> (setf (cell-value (aref s 0 0)) 1.1)
> (setf (aref s 0 0) "net")
> (setf (aref s 1 0) "gross")
> (setf (aref s 0 11) "sum:")
> 
> and now you can use it like this:
> 
> CL-USER > (setf (cell-value (aref s 0 1)) 20.00)
> NIL
> 
> CL-USER > (setf (cell-value (aref s 0 2)) 5.60)
> NIL
> 
> CL-USER > (print-spreadsheet s)
> net     gross   
> 20.0    23.799999999999997
> 5.6     6.664   
> 0       0.0     
> 0       0.0     
> 0       0.0     
> 0       0.0     
> 0       0.0     
> 0       0.0     
> 0       0.0     
> 0       0.0     
> sum:    30.464  
> NIL
> 
> 
> I don't know how it is called, but I would name it "instance based
> dataflow", opposed to Cells, which is type based (you define a fixed set of
> rules per CLOS class, if I didn't missed a way to do instance based
> dataflow with Cells).

You missed it, and I have already corrected you on this in your original 
write-up, where you said only :default-initargs could be used to author 
a slot. Dude, if an initarg works in :default-initargs, how could it not 
work on make-instance? You also left out the initform.

btw, I already call this instance-oriented programming, and I played 
with (defparameter *it* (c? ....) and got it working a little but then 
had trouble and gave up, it was just to settle a bar bet. I have also 
toyed with Cells and defstruct.

> 
> Of course, instance based dataflow could be easily enhanced to type based
> dataflow by calling the binding functions after the instance was created.
> Some other improvements: Like in Cells, some macrology could extract all
> references from the update functions automaticly.
> 
> There are some open questions:
> 
> - how do you handle cyclic dependencies?

backtrace. Two things can depend on each other, just not at the same time.

> - is it a good idea to pass the old value to the callback function, too?

I do, along with a flag indicating if the old value is actually being 
supplied from a bound prior value.

> - do we need lazy evaluation of the out-cells?

TFB the Younger made me do it. I have lazy, lazy until evaluated (then 
eager), and lazy once evaluated.

Did you look at "synapses"? TFBtY will also want bounditude as 
meaningful dataflow state.

> 
> My goal is a dataflow theory, which provides a powerful base for all
> possible dataflow tasks and which is clean and consistent. Maybe this
> simple implementation can help testing it.

Or maybe I defined it here:

http://common-lisp.net/cgi-bin/viewcvs.cgi/cells/cells-manifesto.txt?root=cells&view=markup

Look for the bit defining data integrity. btw, Looking at that just now 
I have to think you did not spot that before attempting to "decrypt" 
Cells -- that is a pretty solid bit of doc.

> 
> 
> Appendix: the code which implements make-*-cell etc., including the
> examples:
> 
> 
> (defun make-in-cell (init-value)
>   "create a cell, to which values can be written and which can be read"
>   (let ((symbol (gensym)))
>     (setf (symbol-value symbol) init-value)
>     symbol))
> 
> (defun update-cell-dependencies (cell)
>   "internal function to update out cell values"
>   (loop for out-cell in (get cell 'update-list) do
>         (let ((new-value (funcall (get out-cell 'update-function)))
>               (callback-function (get out-cell 'callback-function)))
>           (setf (symbol-value out-cell) new-value)
>           (when callback-function
>             (funcall callback-function))
>           (update-cell-dependencies out-cell))))
> 
> (defun make-out-cell (update-function dependencies)
>   "create a cell, which can be read, only"
>   (let ((symbol (gensym)))
>     (setf (get symbol 'update-function) update-function)
>     (loop for foreign-symbol in dependencies do
>           (let ((update-list (get foreign-symbol 'update-list)))
>             (if update-list
>                 (push symbol update-list)
>               (setf (get foreign-symbol 'update-list) (list symbol)))))
>     (setf (symbol-value symbol) (funcall update-function))
>     symbol))
> 
> (defun cell-value (cell)
>   "get the value of a cell"
>   (symbol-value cell))
> 
> (defun set-cell-value (cell value)
>   "set the value of a cell"
>   (when (get cell 'update-function)
>     (error "can't write to out cell"))
>   (setf (symbol-value cell) value)
>   (update-cell-dependencies cell))
> 
> (defun set-cell-callback (cell callback-function)
>   "set a callback function for an output cell"
>   (setf (get cell 'callback-function) callback-function))
> 
> ; define setf expander for cell-values
> (defsetf cell-value set-cell-value)
> 
> ; sample
> 
> (defparameter a (make-in-cell 0))
> (defparameter b (make-in-cell 0))
> (defparameter result
>   (make-out-cell
>    (lambda ()
>      (+ (cell-value a) (cell-value b)))
>    (list a b)))
> 
> (cell-value result)
> (setf (cell-value a) 1)
> (setf (cell-value b) 3)
> (cell-value result)
> 
> (set-cell-callback
>  result
>  (lambda ()
>    (format t "result changed to ~a~%" (cell-value result))))
> 
> (setf (cell-value a) 2)
> 
> 
> (defun make-spreadsheet (x y)
>   (loop for i from 0 below x
>         with s =  (make-array (list x y)) 
>         finally (return s) do
>         (loop for j from 0 below y do
>               (setf (aref s i j) (make-in-cell 0)))))
> 
> (defun print-filled (object &optional (length 8))
>   (let ((out (with-output-to-string (s)
>                (format s "~a" object))))
>     (format t "~a" out)
>     (loop while (< (length out) length) do
>           (princ " ")
>           (decf length))))
> 
> (defun print-spreadsheet (s)
>   (let ((dimensions (array-dimensions s)))
>     (loop for y from 0 below (cadr dimensions) do
>           (loop for x from 0 below (car dimensions) do
>                 (let ((value (aref s x y)))
>                   (print-filled
>                    (if (symbolp value)
>                        (cell-value value)
>                      value))))
>           (terpri))))
> 
> (defun create-tax-function (i)
>   (lambda ()
>     (* 1.19 (cell-value (aref s 0 i)))))
> 
> (defparameter s (make-spreadsheet 2 12))
> 
> (loop for i from 1 to 10 do
>       (setf (aref s 1 i)
>             (make-out-cell (create-tax-function i)
>                            (list (aref s 0 i)))))
> 
> (setf (aref s 1 11)
>       (make-out-cell (lambda ()
>                        (loop for i from 1 to 10 sum
>                              (cell-value (aref s 1 i))))
>                      (loop for i from 1 to 10 collect (aref s 1 i))))
> 
> (setf (cell-value (aref s 0 0)) 1.1)
> (setf (aref s 0 0) "net")
> (setf (aref s 1 0) "gross")
> (setf (aref s 0 11) "sum:")
> 
> (setf (cell-value (aref s 0 1)) 20.00)
> (setf (cell-value (aref s 0 2)) 5.60)
> 
> (print-spreadsheet s)
> 
> 

Welcome to the club! Fun, right?

:)

ken


-- 

"As long as algebra is taught in school,
there will be prayer in school." - Cokie Roberts

"Stand firm in your refusal to remain conscious during algebra."
    - Fran Lebowitz

"I'm an algebra liar. I figure two good lies make a positive."
    - Tim Allen

"Algebra is the metaphysics of arithmetic." - John Ray

http://www.theoryyalgebra.com/
From: Frank Buss
Subject: Re: dataflow basics
Date: 
Message-ID: <dgwfctfriftn$.1ta2f7yoc4i81.dlg@40tude.net>
Ken Tilton wrote:

> You missed it, and I have already corrected you on this in your original 
> write-up, where you said only :default-initargs could be used to author 
> a slot. Dude, if an initarg works in :default-initargs, how could it not 
> work on make-instance? You also left out the initform.

Sorry, I really need to learn more CLOS :-)
Could you show me an example with Cells and how you can use
instance-oriented dataflow programming at runtime with an array, like you
can do it in every real spreadsheet application and with my framework?

>> My goal is a dataflow theory, which provides a powerful base for all
>> possible dataflow tasks and which is clean and consistent. Maybe this
>> simple implementation can help testing it.
> 
> Or maybe I defined it here:
> 
> http://common-lisp.net/cgi-bin/viewcvs.cgi/cells/cells-manifesto.txt?root=cells&view=markup
> 
> Look for the bit defining data integrity. btw, Looking at that just now 
> I have to think you did not spot that before attempting to "decrypt" 
> Cells -- that is a pretty solid bit of doc.

That's a good beginning and maybe you should provide a link to this
document on your Cells homepages. The "Dependency Discovery Axioms" looks
somewhat like I'm searching for, but I think it must be possible to
describe it with simpler axioms, and then maybe deriving some of the axioms
of the document from it.

-- 
Frank Buss, ··@frank-buss.de
http://www.frank-buss.de, http://www.it4-systems.de
From: Ken Tilton
Subject: Re: dataflow basics
Date: 
Message-ID: <hpQRh.1648$DT7.1222@newsfe12.lga>
Frank Buss wrote:
> Ken Tilton wrote:
> 
> 
>>You missed it, and I have already corrected you on this in your original 
>>write-up, where you said only :default-initargs could be used to author 
>>a slot. Dude, if an initarg works in :default-initargs, how could it not 
>>work on make-instance? You also left out the initform.
> 
> 
> Sorry, I really need to learn more CLOS :-)
> Could you show me an example with Cells and how you can use
> instance-oriented dataflow programming at runtime with an array, like you
> can do it in every real spreadsheet application and with my framework?

Were you sent by the makers of other Algebra software to wear me out 
with tedious misquestions? Your next reading assignment is "No Silver 
Bullet". Let me know when you understand why software is hard, then I 
can explain.

(Did you really say "like you can in every real spreadsheet 
application"? Oh, my. This could be tougher than I thought.)

> 
> 
>>>My goal is a dataflow theory, which provides a powerful base for all
>>>possible dataflow tasks and which is clean and consistent. Maybe this
>>>simple implementation can help testing it.
>>
>>Or maybe I defined it here:
>>
>>http://common-lisp.net/cgi-bin/viewcvs.cgi/cells/cells-manifesto.txt?root=cells&view=markup
>>
>>Look for the bit defining data integrity. btw, Looking at that just now 
>>I have to think you did not spot that before attempting to "decrypt" 
>>Cells -- that is a pretty solid bit of doc.
> 
> 
> That's a good beginning and maybe you should provide a link to this
> document on your Cells homepages.

You'll get over this habit of telling others what to do soon, right?

> The "Dependency Discovery Axioms" looks
> somewhat like I'm searching for, but I think it must be possible to
> describe it with simpler axioms, and then maybe deriving some of the axioms
> of the document from it.
> 

uhhh, does that mean you made it to Eby's stuff? Not sure, but it sounds 
like him. Anyway, we tried a more formal definition.

Me, I am smart enough to know that code is design (well, I got that from 
someone else) so when you get back from shopping, sorry, back to reading 
the code.

kt

-- 

"As long as algebra is taught in school,
there will be prayer in school." - Cokie Roberts

"Stand firm in your refusal to remain conscious during algebra."
    - Fran Lebowitz

"I'm an algebra liar. I figure two good lies make a positive."
    - Tim Allen

"Algebra is the metaphysics of arithmetic." - John Ray

http://www.theoryyalgebra.com/
From: Frank Buss
Subject: Re: dataflow basics
Date: 
Message-ID: <mnhz7cfleopt.1tujsrdakndfn$.dlg@40tude.net>
Ken Tilton wrote:

> You'll get over this habit of telling others what to do soon, right?

Maybe I should do it more subtle, like you do :-)

> Me, I am smart enough to know that code is design (well, I got that from 
> someone else) so when you get back from shopping, sorry, back to reading 
> the code.

Function names like "finish-business" and "just-do-it" don't help
understanding the code, but I've tried to analyze it in a debugger and
looks like you are storing the caller cells in "useds" when reading a cell,
with some caching optimization etc.

This sounds like a good idea, and I think it should work, if the rule
functions for calculating cells depends on other cells, only. Assuming only
directed graphs, a very simple implementation would be to collect all leafs
and then calling the update function on all leaf cells. While collecting, a
dirty flag is set in all cells and the update function is called only, if
the dirty flag is set and then it is reset, to avoid multiple calculations.

Together with Rainer's idea of using defstruct, now it looks like the code
below. I wonder if I could implement it without the *last-caller* variable.
Looks like in Cells you have used something similar. And it could be
optimized, because when a value doesn't change, it doesn't need to be
propagated (at any level). And the dependencies should be cleared at the
right time. Looks like it is not so easy as I thought.

BTW: Why did you change the Cells licence from BSD to LLGPL?

BTW2: nice 6502 assembler. Why is it in the Cells CVS? Do you think it is
difficult to implement a full C64 in Common Lisp (with some graphics and
sound libraries, like provided e.g. by lispbuilder-sdl) ?


(defstruct cell
  (internal-value nil)
  (update-function nil)
  (callback-function nil)
  (children-cells nil)
  (dirty nil))

(defun make-in-cell (init-value)
  "create a cell, to which values can be written and which can be read"
  (make-cell :internal-value init-value))

(defparameter *last-caller* nil)
(defparameter *leafs* nil)

(defun collect-leafs-and-set-dirty (cell)
  (when cell
    (when (cell-update-function cell)
      (setf (cell-dirty cell) t))
    (let ((children (cell-children-cells cell)))
      (if children
          (loop for child in children collect
                (collect-leafs-and-set-dirty child))
        (push cell *leafs*)))))

(defun update-cell (cell)
  "internal function to recursivly update one cell, until some root cell"
  (when (and *last-caller* (not (eql *last-caller* cell)))
    (unless (find *last-caller* (cell-children-cells cell))
      (push *last-caller* (cell-children-cells cell))))
  (when (cell-dirty cell)
    (setf (cell-dirty cell) nil)
    (let ((*last-caller* cell)
          (update-function (cell-update-function cell))
          (callback-function (cell-callback-function cell)))
      (when update-function
        (setf (cell-internal-value cell) (funcall update-function)))
      (when callback-function
        (funcall callback-function)))))

(defun cell-value (cell)
  "updates a cell, if necessary and returns the value of the cell"
  (update-cell cell)
  (cell-internal-value cell))

(defun update-children (cell)
  "internal function to recusivly update all children of cell"
  (setf *leafs* nil)
  (collect-leafs-and-set-dirty cell)
  (loop for leaf in *leafs* do
        (update-cell leaf)))

(defun make-out-cell (update-function)
  "create a read-only cell and call the update function initially"
  (let ((cell (make-cell :update-function update-function :dirty t)))
    (update-cell cell)
    cell))

(defun set-cell-value (cell value)
  "set the value of a cell"
  (when (cell-update-function cell)
    (error "can't write to out cell"))
  (setf (cell-internal-value cell) value)
  (update-children cell))

(defun set-cell-callback (cell callback-function)
  "set a callback function for an output cell"
  (setf (cell-callback-function cell) callback-function))

; define setf expander for cell-values
(defsetf cell-value set-cell-value)


-- 
Frank Buss, ··@frank-buss.de
http://www.frank-buss.de, http://www.it4-systems.de
From: Ken Tilton
Subject: Re: dataflow basics
Date: 
Message-ID: <OV6Sh.34$Ex6.31@newsfe12.lga>
Frank Buss wrote:
> Ken Tilton wrote:
> 
> 
>>Me, I am smart enough to know that code is design (well, I got that from 
>>someone else) so when you get back from shopping, sorry, back to reading 
>>the code.
> 
> 
> Function names like "finish-business" and "just-do-it" don't help
> understanding the code,...

Let's look at the code and see which is harder to understand, those 
names or why I bother with you.

Herein lies the only call to finish-business:

(defun call-with-integrity (opcode defer-info action)
   (if *within-integrity*
       (if opcode
           (ufb-add opcode (cons defer-info action))
         (funcall action opcode defer-info))
     (let ((*within-integrity* t)
           *unfinished-business*
           *defer-changes*)
       (when (or (zerop *data-pulse-id*)
               (eq opcode :change))
         (data-pulse-next (cons opcode defer-info)))
       (prog1
           (funcall action opcode defer-info)
         (finish-business)))))

with-integrity is the cornerston of dataflow, and that is its entire 
implementation, debugging stuff removed. Oh, look, *unfinished-business* 
is bound to nil before invoking the function body, then we call 
finish-business. Man, I'm stumped.

Oh, look, three functions for managing the unfinished business queue, 
all called ufb-something. Oh, look, ufb-add -- hey! ufb-add gets called 
up there, too, and it is the only place it gets called! And one 
parameter is "defer-info", and there is another parameter dynamically 
controlling whether or not to *defer-changes*! I'm lost.

Let's see. if *within-integrity* is t, we either queue the action in ufb 
or invoke it straight away if no opcode is provided. OK, that opcode 
allows us to shoot from the hip and bypass integrity enforcement. What a 
good lisper I am, supporting a fine, long traditon of allowing everyone 
to shoot ourselves in the feet. Otherwise I clear the intergity table 
and set the flag indicating this call is in charge. I have /never/ seen 
anything like that before. I may need a debugger to figure this out.

Maybe looking /inside/ finish-business will help, the name sure does not 
tell me anything....My God! Look at all the comments!

Tilton's Law of Working on OPC: First, we kill all the comments:

(defun finish-business ()
   (tagbody

     tell-dependents
     (just-do-it :tell-dependents)
      (just-do-it :awaken)
     (bwhen (uqp (fifo-peek (ufb-queue :tell-dependents)))
       (go tell-dependents))

     handle-clients
     (bwhen (clientq (ufb-queue :client))
       (if *client-queue-handler*
           (funcall *client-queue-handler* clientq)
         (just-do-it clientq))
       (when (fifo-peek (ufb-queue :client))
         (go handle-clients)))

     (just-do-it :ephemeral-reset)
     (bwhen (task-info (fifo-pop (ufb-queue :change)))
       (destructuring-bind (defer-info . task-fn) task-info
         (data-pulse-next (list :finbiz defer-info))
         (funcall task-fn :change defer-info)
         (go tell-dependents)))))

just-do-it?! You are right, what could that possibly do??!

(defun just-do-it (op-or-q &aux (q (if (keywordp op-or-q)
                                        (ufb-queue op-or-q)
                                      op-or-q)))
   (loop for (defer-info . task) = (fifo-pop q)
       while task
       do (funcall task op-or-q defer-info)))

I am afraid to ask what fifo-pop does! As far as I can tell this 
function just does the actions in the queue! Lost.

Now admit it, you did not even try, you just took a quick glance and 
decided to go shopping. That is fine, I am the same way. What is not 
fine is then publicly casting aspersions on the readability of my code. 
You were looking at internals code worked on by exactly one programmer 
ever and it is still clearer than anything you will ever write.

I wonder what would have happened if you had gotten to the stuff even I 
cannot understand. :)

hth,kt


-- 

"Algebra is the metaphysics of arithmetic." - John Ray

http://www.theoryyalgebra.com/
From: Frank Buss
Subject: Re: dataflow basics
Date: 
Message-ID: <v5g7snneb4y0$.2spgfm002117$.dlg@40tude.net>
Ken Tilton wrote:

> Now admit it, you did not even try, you just took a quick glance and 
> decided to go shopping. That is fine, I am the same way. What is not 
> fine is then publicly casting aspersions on the readability of my code. 
> You were looking at internals code worked on by exactly one programmer 
> ever and it is still clearer than anything you will ever write.

Sorry, I'm just a Lisp noob. You are right, I didn't spent much time
analyzing your code, which maybe is good Lisp code, I can't judge.

To summarize: Looks like there is no silver bullet. There are some good
ideas and concepts, but maybe no consistent, all-embracing and simple
theory of dataflow. Time for me to continue with some Java programming :-)

-- 
Frank Buss, ··@frank-buss.de
http://www.frank-buss.de, http://www.it4-systems.de
From: Dan Bensen
Subject: Re: dataflow basics
Date: 
Message-ID: <evbb58$74a$1@wildfire.prairienet.org>
Frank Buss wrote:
> Function names like "finish-business" and "just-do-it" don't help
> understanding the code

What about "go-for-it"?[1]  That's what I called the function in
my famous database app that fires up the ol' charts & graphs
after the user specifies the query options, data is extracted and
processed, etc..  There's something refreshing about indicating
that the details and preparations are all in place and it's time
for the program to go ahead and do what it was intended for, which
is usually well known once the basic design has been worked out.

[1] Actually "GoForIt" in C++.

-- 
Dan
www.prairienet.org/~dsb/
From: Ken Tilton
Subject: Re: dataflow basics
Date: 
Message-ID: <7xaSh.241$5M.156@newsfe12.lga>
Dan Bensen wrote:
> Frank Buss wrote:
> 
>> Function names like "finish-business" and "just-do-it" don't help
>> understanding the code
> 
> 
> What about "go-for-it"?[1]  That's what I called the function in
> my famous database app that fires up the ol' charts & graphs
> after the user specifies the query options, data is extracted and
> processed, etc..  There's something refreshing about indicating
> that the details and preparations are all in place and it's time
> for the program to go ahead and do what it was intended for, which
> is usually well known once the basic design has been worked out.
> 
> [1] Actually "GoForIt" in C++.
> 

There ya go. fact is, with all that deferred stuff, that name really 
helps me when I look at the code. No more filtering, no more queue 
manager maybe jumping in -- Just Do It! And....

Who says we cannot have fun with data names? One crucial variable out 
there in a business application somewhere is a flag indicating if 
something had been altered: ch-ch-ch-ch-changes. I actually polled a few 
people to determine how many ch's there were before Mr. Bowie got the 
word out. Not sure we got it right... ah, Google says "yes". Who says I 
do not write good code?

kt

-- 

"As long as algebra is taught in school,
there will be prayer in school." - Cokie Roberts

"Stand firm in your refusal to remain conscious during algebra."
    - Fran Lebowitz

"I'm an algebra liar. I figure two good lies make a positive."
    - Tim Allen

"Algebra is the metaphysics of arithmetic." - John Ray

http://www.theoryyalgebra.com/
From: Bob Felts
Subject: Re: dataflow basics
Date: 
Message-ID: <1hw9ije.3ojm1w19iau3gN%wrf3@stablecross.com>
Ken Tilton <···@theoryyalgebra.com> wrote:

> 
> Who says we cannot have fun with data names? One crucial variable out
> there in a business application somewhere is a flag indicating if 
> something had been altered: ch-ch-ch-ch-changes.

Sorry for this being in C:

   PUBLIC void Sed_quis_custodiet_ipsos_Custodes( void )

Its job is to examine a table downloaded from a server to determine if,
and how many, threads will be launched to monitor the health of the
system.

You've never heard such whining... "it's not in English."  "It doesn't
follow our coding standards."

Oh, wait, this is c.l.l.  Yes, you have.  
From: Ken Tilton
Subject: Re: dataflow basics
Date: 
Message-ID: <7TdSh.81$Ex6.26@newsfe12.lga>
Bob Felts wrote:
> Ken Tilton <···@theoryyalgebra.com> wrote:
> 
> 
>>Who says we cannot have fun with data names? One crucial variable out
>>there in a business application somewhere is a flag indicating if 
>>something had been altered: ch-ch-ch-ch-changes.
> 
> 
> Sorry for this being in C:
> 
>    PUBLIC void Sed_quis_custodiet_ipsos_Custodes( void )
> 
> Its job is to examine a table downloaded from a server to determine if,
> and how many, threads will be launched to monitor the health of the
> system.
> 
> You've never heard such whining... "it's not in English."  "It doesn't
> follow our coding standards."
> 
> Oh, wait, this is c.l.l.  Yes, you have.  

You left out the punch line: you renamed it sqcic and they all gathered 
round patting you on the back, lifted you on their shoulders... when in 
Rome?

kt

-- 

"As long as algebra is taught in school,
there will be prayer in school." - Cokie Roberts

"Stand firm in your refusal to remain conscious during algebra."
    - Fran Lebowitz

"I'm an algebra liar. I figure two good lies make a positive."
    - Tim Allen

"Algebra is the metaphysics of arithmetic." - John Ray

http://www.theoryyalgebra.com/
From: Bob Felts
Subject: Re: dataflow basics
Date: 
Message-ID: <1hwaukx.1em9cznfgjx2uN%wrf3@stablecross.com>
Ken Tilton <···@theoryyalgebra.com> wrote:

> Bob Felts wrote:
> > Ken Tilton <···@theoryyalgebra.com> wrote:
> > 
> > 
> >>Who says we cannot have fun with data names? One crucial variable out
> >>there in a business application somewhere is a flag indicating if 
> >>something had been altered: ch-ch-ch-ch-changes.
> > 
> > 
> > Sorry for this being in C:
> > 
> >    PUBLIC void Sed_quis_custodiet_ipsos_Custodes( void )
> > 
> > Its job is to examine a table downloaded from a server to determine if,
> > and how many, threads will be launched to monitor the health of the
> > system.
> > 
> > You've never heard such whining... "it's not in English."  "It doesn't
> > follow our coding standards."
> > 
> > Oh, wait, this is c.l.l.  Yes, you have.  
> 
> You left out the punch line: you renamed it sqcic and they all gathered
> round patting you on the back, lifted you on their shoulders... when in
> Rome?
> 

I left out the punch line because there wasn't one.  Life doesn't often
imitate c.l.l.
From: Rob Warnock
Subject: Re: dataflow basics
Date: 
Message-ID: <H9WdnRlEBs-IK4TbnZ2dnUVZ_oOknZ2d@speakeasy.net>
[ob. OT warning...]

Ken Tilton  <···@theoryyalgebra.com> wrote:
+---------------
| Who says we cannot have fun with data names? One crucial variable out 
| there in a business application somewhere is a flag indicating if 
| something had been altered: ch-ch-ch-ch-changes.
+---------------

Circa 1968, RCA came out with the Spectra 70 series of IBM System 360/370
compatible mainframes. This was in the days when "bus/tag"-style channel
I/O came in only two flavors: "byte multiplexor", and "selector". The
"byte-mux" could interleave traffic from a number of I/O devices, but
could only move one byte per entire I/O transfer, so the overhead was
high and the speed low -- it was very easy to overrun even a medium-speed
device if too many devices were active at once. The "selector", on the
other hand, could move large blocks at high speeds, but was locked to
a single device for the entire duration of the transfer, which sometimes
caused performance problems with other devices on the same channel if you
did an I/O that had *long* latency [like a non-detached tape rewind].

So the RCA Spectra 70 added this extra little wire to their byte-mux
channels that permitted the device to request a limited burst mode
of several bytes, which provided a compromise between the two types.
The wire was to be asserted when the device was in danger of falling
behind, in order to let it catch up with the data rate. And the name
they gave to the wire...

[wait for it...]

[wait...]

[wait...]

...was "CATSUP".


-Rob

p.s. There were lots of other fun wires deep inside the core of
the Spectra 70 as well, named for secretaries, babies, pets, etc.
Those engineers knew how to have fun.

-----
Rob Warnock			<····@rpw3.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607
From: Ken Tilton
Subject: Re: dataflow basics
Date: 
Message-ID: <SfmSh.2116$dC7.1496@newsfe12.lga>
Rob Warnock wrote:
> [ob. OT warning...]
> 
> Ken Tilton  <···@theoryyalgebra.com> wrote:
> +---------------
> | Who says we cannot have fun with data names? One crucial variable out 
> | there in a business application somewhere is a flag indicating if 
> | something had been altered: ch-ch-ch-ch-changes.
> +---------------
> 
> Circa 1968, RCA came out with the Spectra 70 series of IBM System 360/370
> compatible mainframes. This was in the days when "bus/tag"-style channel
> I/O came in only two flavors: "byte multiplexor", and "selector". The
> "byte-mux" could interleave traffic from a number of I/O devices, but
> could only move one byte per entire I/O transfer, so the overhead was
> high and the speed low -- it was very easy to overrun even a medium-speed
> device if too many devices were active at once. The "selector", on the
> other hand, could move large blocks at high speeds, but was locked to
> a single device for the entire duration of the transfer, which sometimes
> caused performance problems with other devices on the same channel if you
> did an I/O that had *long* latency [like a non-detached tape rewind].
> 
> So the RCA Spectra 70 added this extra little wire to their byte-mux
> channels that permitted the device to request a limited burst mode
> of several bytes, which provided a compromise between the two types.
> The wire was to be asserted when the device was in danger of falling
> behind, in order to let it catch up with the data rate. And the name
> they gave to the wire...
> 
> [wait for it...]
> 
> [wait...]
> 
> [wait...]
> 
> ...was "CATSUP".
> 
> 
> -Rob
> 
> p.s. There were lots of other fun wires deep inside the core of
> the Spectra 70 as well, named for secretaries, babies, pets, etc.
> Those engineers knew how to have fun.

And, come to think of it, we are stuck forever with one rather prominent 
product of engineer fun: Lisp. :(

kt

-- 

"As long as algebra is taught in school,
there will be prayer in school." - Cokie Roberts

"Stand firm in your refusal to remain conscious during algebra."
    - Fran Lebowitz

"I'm an algebra liar. I figure two good lies make a positive."
    - Tim Allen

"Algebra is the metaphysics of arithmetic." - John Ray

http://www.theoryyalgebra.com/
From: szergling
Subject: Re: dataflow basics
Date: 
Message-ID: <1176269229.501994.214110@l77g2000hsb.googlegroups.com>
> > p.s. There were lots of other fun wires deep inside the core of
> > the Spectra 70 as well, named for secretaries, babies, pets, etc.
> > Those engineers knew how to have fun.
>
> And, come to think of it, we are stuck forever with one rather prominent
> product of engineer fun: Lisp. :(
>
> kt
>
> --

Off-topic now, but my favourite is the assembly instructions mentioned
in one of my undergraduate classes...

Old MacDonald had a farm, EIEIO (couldn't remember what it was, but
Wikipedia had this, Enforce In-Order Execution of I/O, and more!
http://en.wikipedia.org/wiki/EIEIO).
From: Ken Tilton
Subject: Re: dataflow basics
Date: 
Message-ID: <NSARh.3179$SJ5.2141@newsfe12.lga>
Frank Buss wrote:

> (setf (aref s 1 11)
>       (make-out-cell (lambda ()
>                        (loop for i from 1 to 10 sum
>                              (cell-value (aref s 1 i))))
>                      (loop for i from 1 to 10 collect (aref s 1 i))))

Ewww, I have to state dependencies separately? First, it is likely to be 
a source of bugs and certainly will be boring to do. Second, it will not 
work. What if your rule is (if A B C) where all the caps are 
dependencies? You do not want to say the dependencies are A, B, and C 
when at any point in time they are A-B or A-C.

Your logic also has a problem where the dependency graph from some cell 
C leads to a dependent cell D1 thru two paths, one of them going thru D2 
on which D1 also depends. If when C changes D1 gets told to update 
before D2, it till transiently take on (and pass to any observer) a 
garbage value computed off an obsolete D2.

The good news is that your implementation still qualifies you for the 
Monaco conference.

:)

ken

ps. How is your other project coming, the wheel? :) k

-- 

"As long as algebra is taught in school,
there will be prayer in school." - Cokie Roberts

"Stand firm in your refusal to remain conscious during algebra."
    - Fran Lebowitz

"I'm an algebra liar. I figure two good lies make a positive."
    - Tim Allen

"Algebra is the metaphysics of arithmetic." - John Ray

http://www.theoryyalgebra.com/
From: Frank Buss
Subject: Re: dataflow basics
Date: 
Message-ID: <14mynkcyaulmh$.1lhcz1m19k2l8$.dlg@40tude.net>
Ken Tilton wrote:

> Ewww, I have to state dependencies separately? First, it is likely to be 
> a source of bugs and certainly will be boring to do. Second, it will not 
> work. What if your rule is (if A B C) where all the caps are 
> dependencies? You do not want to say the dependencies are A, B, and C 
> when at any point in time they are A-B or A-C.

I don't see a problem. For (if A B C), you can specify all 3 variables as
dependencies and it works, but of course not as fast as possible, because
the update function is called for every change, even if it would be not
necessary.

> Your logic also has a problem where the dependency graph from some cell 
> C leads to a dependent cell D1 thru two paths, one of them going thru D2 
> on which D1 also depends. If when C changes D1 gets told to update 
> before D2, it till transiently take on (and pass to any observer) a 
> garbage value computed off an obsolete D2.

Thanks, I didn't thought of this. Assuming only directed graphs with no
cycles, do you think I can fix it by starting from the leafs and whenever
an update function referenfences a cell, calling the update function of
this cell, first?

How did you solve the problem? I tried to read your source code, but looks
too complicated for me, because I don't know much about CLOS and nothing
about MOP. Some experiments suggests that you are building the dependendies
on the first read request of out-cells, because the callback functions are
not triggered, if you didn't read the out-cell at least once. E.g. with
this model:

(defmodel foo ()
  ((a :cell t :initarg :a :initform (c-in 0) :accessor a)
   (b :cell t :initarg :b :initform (c-in 0) :accessor b))
  (:default-initargs
   :b (c? (progn (format t "b changed~%") (^a)))))

and this callback function:

(def-c-output b ((self foo))
  (format t "current b: ~d" new-value))

But the behaviour is a bit strange. Looks like sometimes the out-cells are
updated, even not read, but the callback function is still not called:

CELLS > (setf (a *foo*) 2)
2

CELLS > (setf (a *foo*) 3)
b changed
3

CELLS > (b *foo*)
3

CELLS > (setf (a *foo*) 4)
b changed
current b: 4
4

CELLS > (setf (a *foo*) 5)
b changed
current b: 5
5

> ps. How is your other project coming, the wheel? :) k

I've read somewhere that for understanding Forth, the best thing to do is
to write your own Forth system. The same thing is true for many other
things, like dataflow concepts and reinventing the wheel is always fun :-)

-- 
Frank Buss, ··@frank-buss.de
http://www.frank-buss.de, http://www.it4-systems.de
From: Ken Tilton
Subject: Re: dataflow basics
Date: 
Message-ID: <PAPRh.259$Dh.147@newsfe12.lga>
Frank Buss wrote:
> Ken Tilton wrote:
> 
> 
>>Ewww, I have to state dependencies separately? First, it is likely to be 
>>a source of bugs and certainly will be boring to do. Second, it will not 
>>work. What if your rule is (if A B C) where all the caps are 
>>dependencies? You do not want to say the dependencies are A, B, and C 
>>when at any point in time they are A-B or A-C.
> 
> 
> I don't see a problem. For (if A B C), you can specify all 3 variables as
> dependencies and it works, but of course not as fast as possible, because
> the update function is called for every change, even if it would be not
> necessary.
> 

There is a reason for the Asian tradition of not arguing with your 
teacher: it is hard enough teaching/learning, but now the teacher has to 
/persuade you/ that he/she is right?!

That is why when you question a good Asian instructor they just....


...refer you to the PyCells archive for a protracted exchange between 
Kenzo and Philip Eby in which Philip slowly but surely worked his way to 
Enlightenment.

See you in Monte Carlo!

:)

ken

-- 

http://www.theoryyalgebra.com/
From: Ken Tilton
Subject: Re: dataflow basics
Date: 
Message-ID: <CNPRh.261$Dh.127@newsfe12.lga>
Frank Buss wrote:
> Ken Tilton wrote:
> 
> 
>>Ewww, I have to state dependencies separately? First, it is likely to be 
>>a source of bugs and certainly will be boring to do. Second, it will not 
>>work. What if your rule is (if A B C) where all the caps are 
>>dependencies? You do not want to say the dependencies are A, B, and C 
>>when at any point in time they are A-B or A-C.
> 
> 
> I don't see a problem. For (if A B C), you can specify all 3 variables as
> dependencies and it works, but of course not as fast as possible, because
> the update function is called for every change, even if it would be not
> necessary.
> 
> 
>>Your logic also has a problem where the dependency graph from some cell 
>>C leads to a dependent cell D1 thru two paths, one of them going thru D2 
>>on which D1 also depends. If when C changes D1 gets told to update 
>>before D2, it till transiently take on (and pass to any observer) a 
>>garbage value computed off an obsolete D2.
> 
> 
> Thanks, I didn't thought of this. Assuming only directed graphs with no
> cycles, do you think I can fix it by starting from the leafs and whenever
> an update function referenfences a cell, calling the update function of
> this cell, first?
> 
> How did you solve the problem? I tried to read your source code, but looks
> too complicated for me, because I don't know much about CLOS and nothing
> about MOP. Some experiments suggests that you are building the dependendies
> on the first read request of out-cells, because the callback functions are
> not triggered, if you didn't read the out-cell at least once. E.g. with
> this model:
> 
> (defmodel foo ()
>   ((a :cell t :initarg :a :initform (c-in 0) :accessor a)
>    (b :cell t :initarg :b :initform (c-in 0) :accessor b))
>   (:default-initargs
>    :b (c? (progn (format t "b changed~%") (^a)))))
> 
> and this callback function:
> 
> (def-c-output b ((self foo))
>   (format t "current b: ~d" new-value))
> 
> But the behaviour is a bit strange. Looks like sometimes the out-cells are
> updated, even not read, but the callback function is still not called:
> 
> CELLS > (setf (a *foo*) 2)

I find myself wondering how *foo* ever got bound, and what version of 
the code you have. Hint: I settled on defobserver after a long march 
from def-c-echo to def-c-output.

ie, you supplied an incomplete question about an obsolete version of the 
code... our tech support team needs a photo of you for their dart board.

thx,kxo

ps. try:

(progn (cells-reset) ;; in case you crashed in Cells last time
     (setf *foo* (to-be (make-instance 'foo....))

No guarantees, but that will be closer than what you are doing now.

k
From: Frank Buss
Subject: Re: dataflow basics
Date: 
Message-ID: <x8jf5v5dcbvt$.13uaeiuk74zja$.dlg@40tude.net>
Ken Tilton wrote:

> ie, you supplied an incomplete question about an obsolete version of the 
> code... our tech support team needs a photo of you for their dart board.

That's the full code:

(defmodel foo ()
  ((a :cell t :initarg :a :initform (c-in 0) :accessor a)
   (b :cell t :initarg :b :initform (c-in 0) :accessor b))
  (:default-initargs
   :b (c? (progn (format t "b changed~%") (^a)))))

(defobserver b ((self foo))
  (format t "current b: ~d" new-value))

(defparameter *foo* (make-instance 'foo))
  
(setf (a *foo*) 2)
(setf (a *foo*) 3)
(b *foo*)
(setf (a *foo*) 4)
(setf (a *foo*) 5)


And it works with the latest CVS version like I expect how it should work.
You should delete the link to the cells-2.0 archive from the page
http://common-lisp.net/project/cells/

-- 
Frank Buss, ··@frank-buss.de
http://www.frank-buss.de, http://www.it4-systems.de
From: Ken Tilton
Subject: Re: dataflow basics
Date: 
Message-ID: <kjQRh.1647$DT7.421@newsfe12.lga>
Frank Buss wrote:
> Ken Tilton wrote:
> 
> 
>>ie, you supplied an incomplete question about an obsolete version of the 
>>code... our tech support team needs a photo of you for their dart board.
> 
> 
> That's the full code:
> 
> (defmodel foo ()
>   ((a :cell t :initarg :a :initform (c-in 0) :accessor a)
>    (b :cell t :initarg :b :initform (c-in 0) :accessor b))
>   (:default-initargs
>    :b (c? (progn (format t "b changed~%") (^a)))))
> 
> (defobserver b ((self foo))
>   (format t "current b: ~d" new-value))
> 
> (defparameter *foo* (make-instance 'foo))
>   
> (setf (a *foo*) 2)
> (setf (a *foo*) 3)
> (b *foo*)
> (setf (a *foo*) 4)
> (setf (a *foo*) 5)
> 
> 
> And it works with the latest CVS version like I expect how it should work.
> You should delete the link to the cells-2.0 archive from the page
> http://common-lisp.net/project/cells/
> 

Oh, great, now he is giving me assignments. Now I know why tigers eat 
their young.*

kzo

* Caddy Shack

-- 


http://www.theoryyalgebra.com/
From: Ken Tilton
Subject: Re: dataflow basics
Date: 
Message-ID: <ZgQRh.1646$DT7.578@newsfe12.lga>
Frank Buss wrote:
> Ken Tilton wrote:
> 
> 
>>Ewww, I have to state dependencies separately? First, it is likely to be 
>>a source of bugs and certainly will be boring to do. Second, it will not 
>>work. What if your rule is (if A B C) where all the caps are 
>>dependencies? You do not want to say the dependencies are A, B, and C 
>>when at any point in time they are A-B or A-C.
> 
> 
> I don't see a problem. For (if A B C), you can specify all 3 variables as
> dependencies and it works, but of course not as fast as possible, because
> the update function is called for every change, even if it would be not
> necessary.

Can you say "combinatorial explosion"? Sher ya can. How about false 
cycles? How about "fixed set of places only"? Real-world models expand 
and contract. Oh, right, you just use global variables. Frank Buss, the 
man who found Lisp and cried, Eureka! A Better Basic!

> 
> 
>>Your logic also has a problem where the dependency graph from some cell 
>>C leads to a dependent cell D1 thru two paths, one of them going thru D2 
>>on which D1 also depends. If when C changes D1 gets told to update 
>>before D2, it till transiently take on (and pass to any observer) a 
>>garbage value computed off an obsolete D2.
> 
> 
> Thanks, I didn't thought of this. Assuming only directed graphs with no
> cycles, do you think I can fix it by starting from the leafs and whenever
> an update function referenfences a cell, calling the update function of
> this cell, first?

Even if it would not be necessary? Oh, right, you are just trying to 
waste my time, not build a real system. I must be on comp.lang.lisp.

> 
> How did you solve the problem? I tried to read your source code, but looks
> too complicated for me, 

Do what I do: go shopping!

> ..because I don't know much about CLOS and nothing
> about MOP. Some experiments suggests that you are building the dependendies
> on the first read request of out-cells, because the callback functions are
> not triggered, if you didn't read the out-cell at least once. E.g. with
> this model:
> 
> (defmodel foo ()
>   ((a :cell t :initarg :a :initform (c-in 0) :accessor a)
>    (b :cell t :initarg :b :initform (c-in 0) :accessor b))
>   (:default-initargs
>    :b (c? (progn (format t "b changed~%") (^a)))))
> 
> and this callback function:
> 
> (def-c-output b ((self foo))
>   (format t "current b: ~d" new-value))
> 
> But the behaviour is a bit strange. Looks like sometimes the out-cells are
> updated, even not read, but the callback function is still not called:
> 
> CELLS > (setf (a *foo*) 2)
> 2
> 
> CELLS > (setf (a *foo*) 3)
> b changed
> 3

I don't believe you.

> 
> CELLS > (b *foo*)
> 3
> 
> CELLS > (setf (a *foo*) 4)
> b changed
> current b: 4
> 4
> 
> CELLS > (setf (a *foo*) 5)
> b changed
> current b: 5
> 5
> 
> 
>>ps. How is your other project coming, the wheel? :) k
> 
> 
> I've read somewhere that for understanding Forth, the best thing to do is
> to write your own Forth system.

"...without pestering the author of Forth" is the bit you missed.

> The same thing is true for many other
> things, like dataflow concepts and reinventing the wheel is always fun :-)
> 

Absolutely.

kzo

-- 

"As long as algebra is taught in school,
there will be prayer in school." - Cokie Roberts

"Stand firm in your refusal to remain conscious during algebra."
    - Fran Lebowitz

"I'm an algebra liar. I figure two good lies make a positive."
    - Tim Allen

"Algebra is the metaphysics of arithmetic." - John Ray

http://www.theoryyalgebra.com/
From: Frank Buss
Subject: Re: dataflow basics
Date: 
Message-ID: <u6t1hds53cg1$.2htbb72zbfvl$.dlg@40tude.net>
Ken Tilton wrote:

> Can you say "combinatorial explosion"? Sher ya can. How about false 
> cycles? How about "fixed set of places only"? Real-world models expand 
> and contract. Oh, right, you just use global variables. Frank Buss, the 
> man who found Lisp and cried, Eureka! A Better Basic!

Basic is not that bad, many more people uses it than Lisp. But of course my
system can be used with local variables and multiple CLOS objects, too.
Just call a function with your CLOS objects to setup the dataflow between
it:

(defclass person ()
  ((name :accessor name :initarg :name)
   (velocity :accessor velocity :initarg :velocity)))

(defclass automobile ()
  ((velocity :accessor velocity :initarg :velocity)))

(defun init-cells-for-auto (auto)
  (setf (velocity auto) (make-in-cell 0)))

(defun put-person-in-auto (person auto)
  (setf (velocity person)
        (make-out-cell
         (lambda ()
           (cell-value (velocity auto))))))

(defparameter *auto* (make-instance 'automobile))

(defparameter *person1* (make-instance 'person :name "Kenny"))

(defparameter *person2* (make-instance 'person :name "Frank"))

(init-cells-for-auto *auto*)
(put-person-in-auto *person1* *auto*)
(put-person-in-auto *person2* *auto*)
(setf (cell-value (velocity *auto*)) 40)
(cell-value (velocity *person1*))
(cell-value (velocity *person2*))

But different instances of the same class can behave differently, if you
like:

(set-cell-callback
 (velocity *person1*)
 (lambda ()
   (when (> (cell-value (velocity *person2*)) 50)
     (format t "~a says: That's too fast!" (name *person2*)))))

(setf (cell-value (velocity *auto*)) 60)

> Oh, right, you are just trying to 
> waste my time, not build a real system. I must be on comp.lang.lisp.

I'm really not interested in building a real system, but it is nice to
explore some concepts and extract the basic principles. Maybe it helps me
when I have to implement another C++ or Java GUI.

-- 
Frank Buss, ··@frank-buss.de
http://www.frank-buss.de, http://www.it4-systems.de
From: Ken Tilton
Subject: Re: dataflow basics
Date: 
Message-ID: <pzVRh.523$Dh.350@newsfe12.lga>
Frank Buss wrote:
> Ken Tilton wrote:
> 
> 
>>Can you say "combinatorial explosion"? Sher ya can. How about false 
>>cycles? How about "fixed set of places only"? Real-world models expand 
>>and contract. Oh, right, you just use global variables. Frank Buss, the 
>>man who found Lisp and cried, Eureka! A Better Basic!
> 
> 
> Basic is not that bad, many more people uses it than Lisp.

Can we continue this over on comp.lang.turing.complete?

hth, ken

-- 

http://www.theoryyalgebra.com/
From: Matthew D Swank
Subject: Re: dataflow basics
Date: 
Message-ID: <pan.2007.04.10.06.34.03.10171@c.net>
On Sat, 07 Apr 2007 13:11:19 -0400, Ken Tilton wrote:

> 
> 
> Frank Buss wrote:
>> Ken Tilton wrote:
>> 
>> 
....
>>>ps. How is your other project coming, the wheel? :) k
>> 
>> 
>> I've read somewhere that for understanding Forth, the best thing to do is
>> to write your own Forth system.
> 
> "...without pestering the author of Forth" is the bit you missed.
> 

Who is pestering whom?

Matt 
-- 
"You do not really understand something unless you can 
 explain it to your grandmother." — Albert Einstein.
From: Ken Tilton
Subject: Re: dataflow basics
Date: 
Message-ID: <e8OSh.146$237.13@newsfe12.lga>
Matthew D Swank wrote:
> On Sat, 07 Apr 2007 13:11:19 -0400, Ken Tilton wrote:
> 
> 
>>
>>Frank Buss wrote:
>>
>>>Ken Tilton wrote:
>>>
>>>
> 
> ....
> 
>>>>ps. How is your other project coming, the wheel? :) k
>>>
>>>
>>>I've read somewhere that for understanding Forth, the best thing to do is
>>>to write your own Forth system.
>>
>>"...without pestering the author of Forth" is the bit you missed.
>>
> 
> 
> Who is pestering whom?

You me.

Frank and I were just having fun. If he had really been bothering me I 
would not have encouraged the thread or answered /all/ his questions 
including providing support on an ill-formed IR about obsolete software.

I was only pretending to be annoyed and to disrespect his intellect, and 
Frank was only pretending to have a clue about programming.

hth,kzo

ps. Similarly, no, you are not really bothering me. k

-- 

"I'm an algebra liar. I figure two good lies make a positive."
    - Tim Allen

http://www.theoryyalgebra.com/
From: Frank Buss
Subject: Re: dataflow basics
Date: 
Message-ID: <uazq92u2yktw$.1of6mzftpyaym$.dlg@40tude.net>
Ken Tilton wrote:

> Frank was only pretending to have a clue about programming.

I'm trying very hard and even if I think I know a bit of programming, it is
only a grain of sand at the turing beach of endless tapes.

-- 
Frank Buss, ··@frank-buss.de
http://www.frank-buss.de, http://www.it4-systems.de
From: Ken Tilton
Subject: Re: dataflow basics
Date: 
Message-ID: <V_SSh.167$237.102@newsfe12.lga>
Frank Buss wrote:
> Ken Tilton wrote:
> 
> 
>>Frank was only pretending to have a clue about programming.
> 
> 
> I'm trying very hard and even if I think I know a bit of programming, it is
> only a grain of sand at the turing beach of endless tapes.
> 

Sorry, I thought you had moved over to comp.lang.java.

Well, if you did not like Cells, maybe you will like cl-s3. I am having 
a ball with it, but cannot figure out how to teach it about delimiters 
and the developer is on a walkabout.

Help!!!!!!!!!!!!!!!!!!!!

kzo

ps. Anybody else using cl-s3 in anger? k

-- 

"As long as algebra is taught in school,
there will be prayer in school." - Cokie Roberts

"Stand firm in your refusal to remain conscious during algebra."
    - Fran Lebowitz

"I'm an algebra liar. I figure two good lies make a positive."
    - Tim Allen

"Algebra is the metaphysics of arithmetic." - John Ray

http://www.theoryyalgebra.com/
From: Rainer Joswig
Subject: Re: dataflow basics
Date: 
Message-ID: <joswig-35199E.01363607042007@news-europe.giganews.com>
In article <································@40tude.net>,
 Frank Buss <··@frank-buss.de> wrote:

> After playing a bit with Cells, I want to discuss the basic concepts of
> dataflow programming. The power of Lisp comes from using many of its
> constructs in an orthogonal way, e.g. you can mix defuns and CLOS in the
> way you want it. Cells is limited to CLOS, so in the following I'm
> developing a framework for playing with dataflow concepts without CLOS.
> 
> The implementation is not as fast and not as easy to use as Cells, but it
> is easy to understand and could be a base for testing some ideas.
> 
> You can use it like this:
> 
> (defparameter a (make-in-cell 0))
> (defparameter b (make-in-cell 0))
> (defparameter result
>   (make-out-cell
>    (lambda ()
>      (+ (cell-value a) (cell-value b)))
>    (list a b)))
> 
> #'make-in-cell and #'make-out-cell returns cell objects, the same way like
> #'make-array returns an array. The code above defines two in-cells, which
> are bound to a and b, and an out cell, which is updated, when the cell a or
> b is updated (second parameter) and with an update function (first
> parameter). #'cell-value returns the value of a cell (think of it like
> #'aref for arrays).
> 
> Now you can play with it like this:
> 
> CL-USER > (cell-value result)
> 0
> 
> CL-USER > (setf (cell-value a) 1)
> NIL
> 
> CL-USER > (setf (cell-value b) 3)
> NIL
> 
> CL-USER > (cell-value result)
> 4
> 
> Finally there is a #'set-cell-callback function, which is called whenever a
> cell was updated. We register a callback for the result cell:
> 
> (set-cell-callback
>  result
>  (lambda ()
>    (format t "result changed to ~a~%" (cell-value result))))
> 
> and when you change one input cell, the function is called:
> 
> CL-USER > (setf (cell-value a) 2)
> result changed to 5
> NIL
> 
> This is nothing special and Cells can it, too. But you could define it at
> runtime, too. A simple example of a spreadsheet:
> 
> First a function, which creates a spreadsheet, with any values initially
> defined as in-cells:
> 
> (defun make-spreadsheet (x y)
>   (loop for i from 0 below x
>         with s =  (make-array (list x y)) 
>         finally (return s) do
>         (loop for j from 0 below y do
>               (setf (aref s i j) (make-in-cell 0)))))
> 
> As an example, you want to calculate the gross values of 10 net values and
> sum it. First setup the spreadsheet:
> 
> (loop for i from 1 to 10 do
>       (setf (aref s 1 i)
>             (make-out-cell (create-tax-function i)
>                            (list (aref s 0 i)))))
> 
> (setf (aref s 1 11)
>       (make-out-cell (lambda ()
>                        (loop for i from 1 to 10 sum
>                              (cell-value (aref s 1 i))))
>                      (loop for i from 1 to 10 collect (aref s 1 i))))
> 
> (setf (cell-value (aref s 0 0)) 1.1)
> (setf (aref s 0 0) "net")
> (setf (aref s 1 0) "gross")
> (setf (aref s 0 11) "sum:")
> 
> and now you can use it like this:
> 
> CL-USER > (setf (cell-value (aref s 0 1)) 20.00)
> NIL
> 
> CL-USER > (setf (cell-value (aref s 0 2)) 5.60)
> NIL
> 
> CL-USER > (print-spreadsheet s)
> net     gross   
> 20.0    23.799999999999997
> 5.6     6.664   
> 0       0.0     
> 0       0.0     
> 0       0.0     
> 0       0.0     
> 0       0.0     
> 0       0.0     
> 0       0.0     
> 0       0.0     
> sum:    30.464  
> NIL
> 
> 
> I don't know how it is called, but I would name it "instance based
> dataflow", opposed to Cells, which is type based (you define a fixed set of
> rules per CLOS class, if I didn't missed a way to do instance based
> dataflow with Cells).
> 
> Of course, instance based dataflow could be easily enhanced to type based
> dataflow by calling the binding functions after the instance was created.
> Some other improvements: Like in Cells, some macrology could extract all
> references from the update functions automaticly.
> 
> There are some open questions:
> 
> - how do you handle cyclic dependencies?
> - is it a good idea to pass the old value to the callback function, too?
> - do we need lazy evaluation of the out-cells?
> 
> My goal is a dataflow theory, which provides a powerful base for all
> possible dataflow tasks and which is clean and consistent. Maybe this
> simple implementation can help testing it.
> 
> 
> Appendix: the code which implements make-*-cell etc., including the
> examples:
> 
> 
> (defun make-in-cell (init-value)
>   "create a cell, to which values can be written and which can be read"
>   (let ((symbol (gensym)))
>     (setf (symbol-value symbol) init-value)
>     symbol))

Why not use a structure or an object for cells.
You are using uninterned symbols with property-lists
as a basic datastructure.

(defstruct cell
  value
  update-function
  callback-function
  update-list)

You could then get rid of getting and setting data from
the property list.


Sketch:

(defun make-in-cell (init-value)
 "create a cell, to which values can be written and which can be read"
 (make-cell :value init-value))

(defun update-cell-dependencies (cell)
  "internal function to update out cell values"
  (loop for out-cell in (cell-update-list cell) do
       (let ((new-value (funcall (cell-update-function out-cell)))
             (callback-function (cell-callback-function out-cell)))
          (setf (cell-value out-cell) new-value)
          (when callback-function
            (funcall callback-function))
          (update-cell-dependencies out-cell))))

You could write you own printer for cell structures/objects.
Usually I'd use CLOS objects instead of (uninterned) symbols
unless need for speed would let me use structures.

> 
> (defun update-cell-dependencies (cell)
>   "internal function to update out cell values"
>   (loop for out-cell in (get cell 'update-list) do
>         (let ((new-value (funcall (get out-cell 'update-function)))
>               (callback-function (get out-cell 'callback-function)))
>           (setf (symbol-value out-cell) new-value)
>           (when callback-function
>             (funcall callback-function))
>           (update-cell-dependencies out-cell))))
> 
> (defun make-out-cell (update-function dependencies)
>   "create a cell, which can be read, only"
>   (let ((symbol (gensym)))
>     (setf (get symbol 'update-function) update-function)
>     (loop for foreign-symbol in dependencies do
>           (let ((update-list (get foreign-symbol 'update-list)))
>             (if update-list
>                 (push symbol update-list)
>               (setf (get foreign-symbol 'update-list) (list symbol)))))
>     (setf (symbol-value symbol) (funcall update-function))
>     symbol))
> 
> (defun cell-value (cell)
>   "get the value of a cell"
>   (symbol-value cell))
> 
> (defun set-cell-value (cell value)
>   "set the value of a cell"
>   (when (get cell 'update-function)
>     (error "can't write to out cell"))
>   (setf (symbol-value cell) value)
>   (update-cell-dependencies cell))
> 
> (defun set-cell-callback (cell callback-function)
>   "set a callback function for an output cell"
>   (setf (get cell 'callback-function) callback-function))
> 
> ; define setf expander for cell-values
> (defsetf cell-value set-cell-value)
> 
> ; sample
> 
> (defparameter a (make-in-cell 0))
> (defparameter b (make-in-cell 0))
> (defparameter result
>   (make-out-cell
>    (lambda ()
>      (+ (cell-value a) (cell-value b)))
>    (list a b)))
> 
> (cell-value result)
> (setf (cell-value a) 1)
> (setf (cell-value b) 3)
> (cell-value result)
> 
> (set-cell-callback
>  result
>  (lambda ()
>    (format t "result changed to ~a~%" (cell-value result))))
> 
> (setf (cell-value a) 2)
> 
> 
> (defun make-spreadsheet (x y)
>   (loop for i from 0 below x
>         with s =  (make-array (list x y)) 
>         finally (return s) do
>         (loop for j from 0 below y do
>               (setf (aref s i j) (make-in-cell 0)))))
> 
> (defun print-filled (object &optional (length 8))
>   (let ((out (with-output-to-string (s)
>                (format s "~a" object))))
>     (format t "~a" out)
>     (loop while (< (length out) length) do
>           (princ " ")
>           (decf length))))
> 
> (defun print-spreadsheet (s)
>   (let ((dimensions (array-dimensions s)))
>     (loop for y from 0 below (cadr dimensions) do
>           (loop for x from 0 below (car dimensions) do
>                 (let ((value (aref s x y)))
>                   (print-filled
>                    (if (symbolp value)
>                        (cell-value value)
>                      value))))
>           (terpri))))
> 
> (defun create-tax-function (i)
>   (lambda ()
>     (* 1.19 (cell-value (aref s 0 i)))))
> 
> (defparameter s (make-spreadsheet 2 12))
> 
> (loop for i from 1 to 10 do
>       (setf (aref s 1 i)
>             (make-out-cell (create-tax-function i)
>                            (list (aref s 0 i)))))
> 
> (setf (aref s 1 11)
>       (make-out-cell (lambda ()
>                        (loop for i from 1 to 10 sum
>                              (cell-value (aref s 1 i))))
>                      (loop for i from 1 to 10 collect (aref s 1 i))))
> 
> (setf (cell-value (aref s 0 0)) 1.1)
> (setf (aref s 0 0) "net")
> (setf (aref s 1 0) "gross")
> (setf (aref s 0 11) "sum:")
> 
> (setf (cell-value (aref s 0 1)) 20.00)
> (setf (cell-value (aref s 0 2)) 5.60)
> 
> (print-spreadsheet s)

-- 
http://lispm.dyndns.org