#+title: Sensitive Setq
#+author: screwtape
* Description
We would like to use ==#:trivial-sensitivities== in the way Reichenbacher seems to think
is great. Basically this means replacing eager function evaluation deferment to a queue.

This queue is the list ==*deferred*==. The macro ==(dsetq foo 'bar)== defers assigning
=='bar== to variable ==foo== to the end of ==*deferred*==: It then also nconcs the
==#'find-sensitives== of ==foo== to ==*deferred*==. A list in *deferred* is always
==(foo 'bar)== resulting like (setq foo 'bar), whereas atoms refer to programs to
call with no arguments, so everything operates by side effects, propagating via
==#'dsetq==.

The gist is that in contrast to highly rigid languages and programming systems, a
lisp package can be grown and debugged organically in and as a lisp system.

There's an example at the end of this file of some trivial "pipelining".

I guess worth noting are ==#'advance== which does a number of steps in ==*deferred*==
and ==#'keep-advancing== which is similar to waiting for a new rising_edge.

There's a utility for defining multiple, sensitive signals and signal-setting
processes. It's assumed that a signal (symbol-value 'foo) is set by a
(symbol-function 'foo) after a signal it is sensitive to is resolved from the queue,
the macro ==#'sdefineq==

* Exports
| deferred-setq  | 'symbol value &optional pkg                         |
| dsetq          | symbol value                                        |
| *deferred*     | list                                                |
| sdefineq       | {(name initial-value (&rest sensitivities) lambda)} |
| advance        | n-steps                                             |
| keep-advancing | ()                                                  |

* ASD
#+name: sensitive-setq-system
#+HEADER: :tangle ~/common-lisp/sensitive-setq/sensitive-setq.asd
#+begin_src lisp
 (defsystem "sensitive-setq"
   :class :package-inferred-system
   :depends-on (:sensitive-setq/deferment))

 (register-system-packages "sensitive-setq/deferment" '(:sensitive-setq))
#+end_src
* Deferment
#+name: deferment
#+HEADER: :tangle ~/common-lisp/sensitive-setq/deferment.lisp
#+begin_src lisp
     (uiop:define-package :sensitive-setq
       (:mix :cl)
       (:mix-reexport :trivial-sensitivities)
       (:export #:deferred-setq #:dsetq #:*deferred* #:sdefineq #:advance #:keep-advancing)
       (:nicknames :sssetq))
     (in-package :sssetq)
     (defvar *deferred* (list))

     (defun deferred-setq (symbol value &optional (pkg (symbol-package symbol))) "
     nconcs ((symbol value)) onto *deferred*
     then nconcs (find-sensitives symbol pkg) onto *deferred*
     Args: symbol, value &optional (pkg (symbol-package symbol))
     do what you might imagine.
     "
       (setf *deferred*
             (nconc *deferred* (list (list symbol value))))
       (setf *deferred*
             (nconc *deferred* (find-sensitives symbol pkg)))
       (values (last *deferred*)))

     (defmacro dsetq (name value &rest pkg)
       `(deferred-setq ',name ,value ,@pkg))

     (defmacro sdefineq (&rest sensitive-defineqs) "
               defvars, setfs symbol-function and #'make-sensitive s from
               (name initially sensitivities lambda)
               Lambda should take no arguments:
               It works via deferred side effects and sensitivities (dsetq)
               sensitivities is an unquoted list of unquoted symbols
               name is similarly unquoted
               initially will be evaluated I think
               "
       `(progn
          ,@(loop for definition in sensitive-defineqs
                  for (name initially senss lambda) = definition
                  collect
                  `(progn
                     (defvar ,name ,initially)
                     (setf (symbol-function ',name) ,lambda)
                     (apply #'make-sensitive ',name ',senss)))))

     (defun execute-deferred-set (item)
       (set (car item) (cadr item)))
     (defun execute-deferred-funcall (item)
       (funcall (symbol-function item)))

     (defun advance (n-steps)
       (loop repeat n-steps
             for item = (pop *deferred*)
             when (consp item)
               do (execute-deferred-set item)
             when (atom item)
               do (execute-deferred-funcall item)))

 (defun keep-advancing () "
   basically get to the next clock tick
 " (loop while *deferred*
         for len = (length *deferred*)
         do (advance len)))

#+end_src
* Smoke

#+name: smoke
#+HEADER: :results output verbatim
#+begin_src lisp
 (asdf:load-system :sensitive-setq)
 (use-package :sensitive-setq)

 (sdefineq (*a-in*  #16(U U U U U U U U U U U U U U U U)
                   () (lambda ()))
           (*a-out* #16(U U U U U U U U U U U U U U U U)
                    (*b-in*)
                    (lambda () (dsetq *a-out* *a-in*)))
           (*b-in* #16(U U U U U U U U U U U U U U U U)
                   (*a-in*)
                   (lambda () (dsetq *b-in* *a-out*)))
           (*b-out* #16(U U U U U U U U U U U U U U U U)
                    (*c-in*)
                    (lambda () (dsetq *b-out* *b-in*)))
           (*c-in* #16(U U U U U U U U U U U U U U U U)
                   (*b-in*)
                   (lambda () (dsetq *c-in* *b-out*)))
           (*c-out* #16(U U U U U U U U U U U U U U U U)
                 (*d-in*)
                    (lambda () (dsetq *c-out* *c-in*)))
           (*d-in* #16(U U U U U U U U U U U U U U U U)
                   (*c-in*)
                   (lambda () (dsetq *d-in* *c-out*))))

 (dolist (in (list #(U U U U U U U U U U U U U U U 1)
                   #(U U U U U U U U U U U U U U 1 0)
                   #(U U U U U U U U U U U U U U 1 1)
                   #(U U U U U U U U U U U U U 1 0 0)
                   #(U U U U U U U U U U U U U 1 0 1)
                   #(U U U U U U U U U U U U U 1 1 1)))
   (dsetq *a-in* in)
   (keep-advancing)
   (print "*a-in* ")
   (princ *a-in*)
   (print "*c-out* ")
   (princ *c-out*)
   (terpri))
#+end_src

#+RESULTS: smoke
#+begin_example

"*a-in* " #(U U U U U U U U U U U U U U U 1)
"*c-out* " #(U U U U U U U U U U U U 1 1 0 0)

"*a-in* " #(U U U U U U U U U U U U U U 1 0)
"*c-out* " #(U U U U U U U U U U U U 1 1 0 1)

"*a-in* " #(U U U U U U U U U U U U U U 1 1)
"*c-out* " #(U U U U U U U U U U U U U U U 1)

"*a-in* " #(U U U U U U U U U U U U U 1 0 0)
"*c-out* " #(U U U U U U U U U U U U U U 1 0)

"*a-in* " #(U U U U U U U U U U U U U 1 0 1)
"*c-out* " #(U U U U U U U U U U U U U U 1 1)

"*a-in* " #(U U U U U U U U U U U U U 1 1 1)
"*c-out* " #(U U U U U U U U U U U U U 1 0 0)
#+end_example