propagator web page actually working
[propagator.git] / propagator.org
blob32acaed76591ab05f3a928a82f2fc89e5cd9cedd
1 #+TITLE: Concurrent Propagator System
2 #+OPTIONS: num:nil ^:nil
3 #+LaTeX_CLASS: normal
5 * code
6   :PROPERTIES:
7   :tangle:   src/propagator.clj
8   :END:
9 #+begin_src clojure
10   (ns
11       #^{:author "Eric Schulte",
12          :license "GPLV3",
13          :doc "Simple concurrent propagator system."}
14     propagator
15     (:use clojure.contrib.repl-utils clojure.contrib.math))
16   
17   (defmacro cell "Define a new cell."
18     [name state]
19     `(def ~name (agent ~state)))
20   
21   (def prop-graph {})
22   
23   (defmacro remember-prop [name in-cells out-cells]
24     `(def prop-graph
25           (assoc prop-graph
26             (quote ~name)
27             {:in-cells (map (fn [x#] (name x#)) (quote ~in-cells))
28              :out-cells (map (fn [x#] (name x#)) (quote ~out-cells))})))
29   
30   (defmacro propagator "Define a new propagator."
31     [name in-cells out-cells & body]
32     `(do
33        (remember-prop ~name ~in-cells ~out-cells)
34        (defn ~(with-meta name
35                 (assoc (meta name)
36                   :in-cells in-cells :out-cells out-cells))
37          ~in-cells ~@body)
38        (doseq [cell# ~in-cells] (add-neighbor cell# ~name))
39        ~name))
40   
41   (defn set-cell "Set the value of a cell" [cell value]
42     (send cell (fn [_] value)))
43   
44   (defmacro run-propagator
45     "Run a propagator, first collect the most recent values from all
46   cells associated with the propagator, then evaluate."
47     [propagator]
48     `(let [results# (apply ~propagator (map deref (:in-cells ^#'~propagator)))]
49        (doseq [cell# (:out-cells ^#'~propagator)]
50          (when (not (= @cell# results#))
51            (send cell# (fn [_#] results#))))
52        results#))
53   
54   (defmacro add-neighbor "Add a neighbor to the given cell."
55     [cell neighbor]
56     `(add-watcher
57       ~cell :send
58       (agent nil :validator (fn [_#] (do (future (run-propagator ~neighbor)) true)))
59       (fn [_# _#])))
60 #+end_src
61 * extensions
62 ** graphs of the propagator network
63 draw a graph in a JFrame
64 #+begin_src clojure
65   (use 'vijual)
66   (import '(javax.swing JFrame JPanel)
67           '(java.awt Color Graphics)
68           '(java.awt.image BufferedImage))
69   
70   (def dim [300 300])
71   
72   (def frame (JFrame.))
73   
74   (defn view "Display a graph generated by vijual" [img]
75     (let [mult 1.5
76           width (* (.getWidth img) mult)
77           height (* (.getHeight img) mult)
78           offset 50
79           panel (doto (proxy [JPanel] []
80                         (paint [g]
81                                (.drawImage g img offset offset nil))))]
82       (doto frame (.add panel) .pack (.setSize (+ offset width) (+ offset height)).show)))
83 #+end_src
85 pull a graph from a the propagators saved in the =prop-graph= variable
86 #+begin_src clojure
87   (defn graph-propagators []
88     (vec (set
89           (apply concat
90                  (map (fn [key]
91                         (let [hsh (get prop-graph key)]
92                           (concat
93                            (map #(vec (list % (name key))) (get hsh :in-cells))
94                            (map #(vec (list (name key) %)) (get hsh :out-cells)))))
95                       (keys prop-graph))))))
96 #+end_src
98 * usage
99 ** doubling a number -- simplest
100 #+begin_src clojure
101   (cell in-c 0)
102   (cell out-c 0)
103   (propagator doubler [in-c] [out-c] (* 2 in))
104   ;; then any updates to in
105   (set-cell in-c 1)
106   ;; will propagate to out
107   @out-c
108 #+end_src
110 ** square roots -- heron
111 #+begin_src clojure
112   (cell guess 1)
113   (cell x 9)
114   (cell done false)
115   (cell margin 0.1)
116   
117   (propagator enough [x guess] [done]
118     (if (< (abs (- (* guess guess) x)) @margin) true false))
119   
120   (propagator heron [x done guess] [guess]
121     (if done
122       guess
123       (/ (+ guess (/ x guess)) 2.0)))
124 #+end_src
126 ** web server
127 Alright, this will use Clojure's [[http://github.com/mmcgrana/ring][Ring]] web server middle-ware.
129 So, I guess the best demo here would be some reading/writing through a
130 MVC setup.
132 The =app= will dispatch the incoming data to input cell by the route
133 at the end of the url, then there can be a couple of output cells
134 which will render different views of the related data.
136 #+begin_src clojure
137   (in-ns 'propagator)
138   (use 'ring.adapter.jetty)
139   (use 'clojure.contrib.seq-utils)
140   (import 'java.util.Date 'java.text.SimpleDateFormat)
141   
142   ;; cells
143   (cell names '())
144   (cell input "")
145   
146   ;; propagator -- adds value of input to names
147   (propagator adder [input names] [names]
148      (when (> (count (seq input)) 0) (set (cons input names))))
149   
150   ;; web wrapping
151   (let [out *out*]
152     (defn app [req]
153       (binding [*out* out] (println (:uri req)))
154       (or
155        ;; dump value into "input" cell
156        (when-let [matched (re-matches #"/add/(.+)" (:uri req))]
157          (set-cell input (second matched))
158          {:status 303 :headers {"Location" "../list"}})
159        ;; render the value of the "list" cell
160        (when-let [matched (re-matches #"/list" (:uri req))]
161          {:status  200
162           :headers {"Content-Type" "text/html"}
163           :body    (apply str (flatten (list "<ul>" (map #(str "<li>"%"</li>") @names) "</ul>")))}))))
164   
165   (run-jetty app {:port 3000})
166 #+end_src
168 * notes
169 ** look at mutable data stores
170 - http://clojure.org/agents
171 - http://clojure.org/atoms
172 - http://clojure.org/refs
174 most definitely will use agents, functions to set their values are
175 applied to them with send (or send-off if potentially I/O bound), they
176 support validators, and they can be set to run actions (i.e. alert
177 propagators) as part of their update cycle (with add-watcher).
179 ** design to permit distribution across multiple machines
180 it should be possible to wrap these mutable items including
181 - network connections from other machines
182 - hardware (timers, I/O devices, etc...)
184 in cells, so that the propagator system remains "pure"