Mercurial > lasercutter
comparison src/clojure/contrib/dataflow.clj @ 10:ef7dbbd6452c
added clojure source goodness
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Sat, 21 Aug 2010 06:25:44 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
9:35cf337adfcf | 10:ef7dbbd6452c |
---|---|
1 ;; Copyright (c) Jeffrey Straszheim. All rights reserved. The use and | |
2 ;; distribution terms for this software are covered by the Eclipse Public | |
3 ;; License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) which can | |
4 ;; be found in the file epl-v10.html at the root of this distribution. By | |
5 ;; using this software in any fashion, you are agreeing to be bound by the | |
6 ;; terms of this license. You must not remove this notice, or any other, | |
7 ;; from this software. | |
8 ;; | |
9 ;; dataflow.clj | |
10 ;; | |
11 ;; A Library to Support a Dataflow Model of State | |
12 ;; | |
13 ;; straszheimjeffrey (gmail) | |
14 ;; Created 10 March 2009 | |
15 | |
16 | |
17 (ns | |
18 ^{:author "Jeffrey Straszheim", | |
19 :doc "A library to support a dataflow model of state"} | |
20 clojure.contrib.dataflow | |
21 (:use [clojure.set :only (union intersection difference)]) | |
22 (:use [clojure.contrib.graph :only (directed-graph | |
23 reverse-graph | |
24 dependency-list | |
25 get-neighbors)]) | |
26 (:use [clojure.walk :only (postwalk)]) | |
27 (:use [clojure.contrib.except :only (throwf)]) | |
28 (:import java.io.Writer)) | |
29 | |
30 | |
31 ;;; Chief Data Structures | |
32 | |
33 | |
34 ;; Source Cell | |
35 | |
36 ; The data of a source cell is directly set by a calling function. It | |
37 ; never depends on other cells. | |
38 | |
39 (defstruct source-cell | |
40 :name ; The name, a symbol | |
41 :value ; Its value, a Ref | |
42 :cell-type) ; Should be ::source-cell | |
43 | |
44 ;; Cell | |
45 | |
46 ; A standard cell that computes its value from other cells. | |
47 | |
48 (defstruct standard-cell | |
49 :name ; The name, a symbol | |
50 :value ; Its value, a Ref | |
51 :dependents ; The names of cells on which this depends, a collection | |
52 :fun ; A closure that computes the value, given an environment | |
53 :display ; The original expression for display | |
54 :cell-type) ; Should be ::cell | |
55 | |
56 (derive ::cell ::dependent-cell) ; A cell that has a dependents field | |
57 | |
58 ;; Validator | |
59 | |
60 ; A cell that has no value, but can throw an exception when run | |
61 | |
62 (defstruct validator-cell | |
63 :name ; Always ::validator | |
64 :dependents ; The names of cells on which this depends, a collection | |
65 :fun ; A clojure that can throw an exception | |
66 :display ; The original exprssion for display | |
67 :cell-type) ; Should be ::validator-cell | |
68 | |
69 (derive ::validator-cell ::dependent-cell) | |
70 | |
71 | |
72 ;; A sentinal value | |
73 | |
74 (def *empty-value* (java.lang.Object.)) | |
75 | |
76 | |
77 ;; Dataflow | |
78 | |
79 ; A collection of cells and dependency information | |
80 | |
81 (defstruct dataflow | |
82 :cells ; A set of all cells | |
83 :cells-map ; A map of cell names (symbols) to collections of cells | |
84 :fore-graph ; The inverse of the dependency graph, nodes are cells | |
85 :topological) ; A vector of sets of independent nodes -- orders the computation | |
86 | |
87 | |
88 ;;; Environment Access | |
89 | |
90 (defn get-cells | |
91 "Get all the cells named by name" | |
92 [df name] | |
93 ((:cells-map @df) name)) | |
94 | |
95 (defn get-cell | |
96 "Get the single cell named by name" | |
97 [df name] | |
98 (let [cells (get-cells df name)] | |
99 (cond | |
100 (= (count cells) 1) (first cells) | |
101 (> (count cells) 1) (throwf Exception "Cell %s has multiple instances" name) | |
102 :otherwise (throwf Exception "Cell %s is undefined" name)))) | |
103 | |
104 (defn source-cell? | |
105 "Is this cell a source cell?" | |
106 [cell] | |
107 (isa? (:cell-type cell) ::source-cell)) | |
108 | |
109 (defn get-source-cells | |
110 "Returns a collection of source cells from the dataflow" | |
111 [df] | |
112 (for [cell (:cells @df) | |
113 :when (source-cell? cell)] | |
114 cell)) | |
115 | |
116 (defn get-value | |
117 "Gets a value from the df matching the passed symbol. | |
118 Signals an error if the name is not present, or if it not a single | |
119 value." | |
120 [df name] | |
121 (let [cell (get-cell df name) | |
122 result @(:value cell)] | |
123 (do (when (= *empty-value* result) | |
124 (throwf Exception "Cell named %s empty" name)) | |
125 result))) | |
126 | |
127 (defn get-values | |
128 "Gets a collection of values from the df by name" | |
129 [df name] | |
130 (let [cells (get-cells df name) | |
131 results (map #(-> % :value deref) cells)] | |
132 (do | |
133 (when (some #(= % *empty-value*) results) | |
134 (throwf Exception "At least one empty cell named %s found" name)) | |
135 results))) | |
136 | |
137 (defn get-old-value | |
138 "Looks up an old value" | |
139 [df env name] | |
140 (if (contains? env name) | |
141 (env name) | |
142 (get-value df name))) | |
143 | |
144 (defn get-value-from-cell | |
145 "Given a cell, get its value" | |
146 [cell] | |
147 (-> cell :value deref)) | |
148 | |
149 ;;; Build Dataflow Structure | |
150 | |
151 (defn- build-cells-map | |
152 "Given a collection of cells, build a name->cells-collection map | |
153 from it." | |
154 [cs] | |
155 (let [step (fn [m c] | |
156 (let [n (:name c) | |
157 o (get m n #{}) | |
158 s (conj o c)] | |
159 (assoc m n s)))] | |
160 (reduce step {} cs))) | |
161 | |
162 (defn- build-back-graph | |
163 "Builds the backward dependency graph from the cells map. Each | |
164 node of the graph is a cell." | |
165 [cells cells-map] | |
166 (let [step (fn [n] | |
167 (apply union (for [dep-name (:dependents n)] | |
168 (cells-map dep-name)))) | |
169 neighbors (zipmap cells (map step cells))] | |
170 (struct-map directed-graph | |
171 :nodes cells | |
172 :neighbors neighbors))) | |
173 | |
174 (defn- build-dataflow* | |
175 "Builds the dataflow structure" | |
176 [cs] | |
177 (let [cells (set cs) | |
178 cells-map (build-cells-map cs) | |
179 back-graph (build-back-graph cells cells-map) | |
180 fore-graph (reverse-graph back-graph)] | |
181 (struct-map dataflow | |
182 :cells cells | |
183 :cells-map cells-map | |
184 :fore-graph fore-graph | |
185 :topological (dependency-list back-graph)))) | |
186 | |
187 (def initialize) | |
188 | |
189 (defn build-dataflow | |
190 "Given a collection of cells, build and return a dataflow object" | |
191 [cs] | |
192 (dosync | |
193 (let [df (ref (build-dataflow* cs))] | |
194 (initialize df) | |
195 df))) | |
196 | |
197 | |
198 ;;; Displaying a dataflow | |
199 | |
200 (defn print-dataflow | |
201 "Prints a dataflow, one cell per line" | |
202 [df] | |
203 (println) | |
204 (let [f (fn [cell] (-> cell :name str))] | |
205 (doseq [cell (sort-by f (:cells @df))] | |
206 (prn cell)))) | |
207 | |
208 | |
209 ;;; Modifying a Dataflow | |
210 | |
211 (defn add-cells | |
212 "Given a collection of cells, add them to the dataflow." | |
213 [df cells] | |
214 (dosync | |
215 (let [new-cells (union (set cells) (:cells @df))] | |
216 (ref-set df (build-dataflow* new-cells)) | |
217 (initialize df)))) | |
218 | |
219 (defn remove-cells | |
220 "Given a collection of cells, remove them from the dataflow." | |
221 [df cells] | |
222 (dosync | |
223 (let [new-cells (difference (:cells @df) (set cells))] | |
224 (ref-set df (build-dataflow* new-cells)) | |
225 (initialize df)))) | |
226 | |
227 | |
228 ;;; Cell building | |
229 | |
230 (def *meta* {:type ::dataflow-cell}) | |
231 | |
232 (defn build-source-cell | |
233 "Builds a source cell" | |
234 [name init] | |
235 (with-meta (struct source-cell name (ref init) ::source-cell) | |
236 *meta*)) | |
237 | |
238 (defn- is-col-var? | |
239 [symb] | |
240 (let [name (name symb)] | |
241 (and (= \? (first name)) | |
242 (= \* (second name))))) | |
243 | |
244 (defn- is-old-var? | |
245 [symb] | |
246 (let [name (name symb)] | |
247 (and (= \? (first name)) | |
248 (= \- (second name))))) | |
249 | |
250 (defn- is-var? | |
251 [symb] | |
252 (let [name (name symb)] | |
253 (and (= \? (first name)) | |
254 (-> symb is-col-var? not) | |
255 (-> symb is-old-var? not)))) | |
256 | |
257 (defn- cell-name | |
258 [symb] | |
259 `(quote ~(cond (is-var? symb) (-> symb name (.substring 1) symbol) | |
260 (or (is-col-var? symb) | |
261 (is-old-var? symb)) (-> symb name (.substring 2) symbol)))) | |
262 | |
263 (defn- replace-symbol | |
264 "Walk the from replacing the ?X forms with the needed calls" | |
265 [dfs ov form] | |
266 (cond | |
267 (-> form symbol? not) form | |
268 (is-var? form) `(get-value ~dfs ~(cell-name form)) | |
269 (is-col-var? form) `(get-values ~dfs ~(cell-name form)) | |
270 (is-old-var? form) `(get-old-value ~dfs ~ov ~(cell-name form)) | |
271 :otherwise form)) | |
272 | |
273 (defn- build-fun | |
274 "Build the closure needed to compute a cell" | |
275 [form] | |
276 (let [dfs (gensym "df_") | |
277 ov (gensym "old_")] | |
278 `(fn [~dfs ~ov] ~(postwalk (partial replace-symbol dfs ov) form)))) | |
279 | |
280 (defn- get-deps | |
281 "Get the names of the dependent cells" | |
282 [form] | |
283 (let [step (fn [f] | |
284 (cond | |
285 (coll? f) (apply union f) | |
286 (-> f symbol? not) nil | |
287 (is-var? f) #{(cell-name f)} | |
288 (is-col-var? f) #{(cell-name f)} | |
289 (is-old-var? f) #{(cell-name f)} | |
290 :otherwise nil))] | |
291 (postwalk step form))) | |
292 | |
293 (defn build-standard-cell | |
294 "Builds a standard cell" | |
295 [name deps fun expr] | |
296 (with-meta (struct standard-cell name (ref *empty-value*) deps fun expr ::cell) | |
297 *meta*)) | |
298 | |
299 (defn build-validator-cell | |
300 "Builds a validator cell" | |
301 [deps fun expr] | |
302 (with-meta (struct validator-cell ::validator deps fun expr ::validator-cell) | |
303 *meta*)) | |
304 | |
305 (defmacro cell | |
306 "Build a standard cell, like this: | |
307 | |
308 (cell fred | |
309 (* ?mary ?joe)) | |
310 | |
311 Which creates a cell named fred that is the product of a cell mary and cell joe | |
312 | |
313 Or: | |
314 | |
315 (cell joe | |
316 (apply * ?*sally)) | |
317 | |
318 Which creates a cell that applies * to the collection of all cells named sally | |
319 | |
320 Or: | |
321 | |
322 (cell :source fred 0) | |
323 | |
324 Which builds a source cell fred with initial value 0 | |
325 | |
326 Or: | |
327 | |
328 (cell :validator (when (< ?fred ?sally) | |
329 (throwf \"%s must be greater than %s\" ?fred ?sally)) | |
330 | |
331 Which will perform the validation" | |
332 [type & data] | |
333 (cond | |
334 (symbol? type) (let [name type ; No type for standard cell | |
335 expr (first data) ; we ignore extra data! | |
336 deps (get-deps expr) | |
337 fun (build-fun expr)] | |
338 `(build-standard-cell '~name ~deps ~fun '~expr)) | |
339 (= type :source) (let [[name init] data] | |
340 `(build-source-cell '~name ~init)) | |
341 (= type :validator) (let [[expr] data | |
342 deps (get-deps expr) | |
343 fun (build-fun expr)] | |
344 `(build-validator-cell ~deps ~fun '~expr)))) | |
345 | |
346 | |
347 ;;; Cell Display | |
348 | |
349 (defmulti display-cell | |
350 "A 'readable' form of the cell" | |
351 :cell-type) | |
352 | |
353 (defmethod display-cell ::source-cell | |
354 [cell] | |
355 (list 'cell :source (:name cell) (-> cell :value deref))) | |
356 | |
357 (defmethod display-cell ::cell | |
358 [cell] | |
359 (list 'cell (:name cell) (:display cell) (-> cell :value deref))) | |
360 | |
361 (defmethod display-cell ::validator-cell | |
362 [cell] | |
363 (list 'cell :validator (:display cell))) | |
364 | |
365 (defmethod print-method ::dataflow-cell | |
366 [f ^Writer w] | |
367 (binding [*out* w] | |
368 (pr (display-cell f)))) | |
369 | |
370 | |
371 ;;; Evaluation | |
372 | |
373 (defmulti eval-cell | |
374 "Evaluate a dataflow cell. Return [changed, old val]" | |
375 (fn [df data old cell] (:cell-type cell))) | |
376 | |
377 (defmethod eval-cell ::source-cell | |
378 [df data old cell] | |
379 (let [name (:name cell) | |
380 val (:value cell) | |
381 ov @val] | |
382 (if (contains? data name) | |
383 (let [new-val (data name)] | |
384 (if (not= ov new-val) | |
385 (do (ref-set val new-val) | |
386 [true ov]) | |
387 [false ov])) | |
388 [false ov]))) | |
389 | |
390 (defmethod eval-cell ::cell | |
391 [df data old cell] | |
392 (let [val (:value cell) | |
393 old-val @val | |
394 new-val ((:fun cell) df old)] | |
395 (if (not= old-val new-val) | |
396 (do (ref-set val new-val) | |
397 [true old-val]) | |
398 [false old-val]))) | |
399 | |
400 (defmethod eval-cell ::validator-cell | |
401 [df data old cell] | |
402 (do ((:fun cell) df old) | |
403 [false nil])) | |
404 | |
405 (defn- perform-flow | |
406 "Evaluate the needed cells (a set) from the given dataflow. Data is | |
407 a name-value mapping of new values for the source cells" | |
408 [df data needed] | |
409 (loop [needed needed | |
410 tops (:topological @df) | |
411 old {}] | |
412 (let [now (first tops) ; Now is a set of nodes | |
413 new-tops (next tops)] | |
414 (when (and (-> needed empty? not) | |
415 (-> now empty? not)) | |
416 (let [step (fn [[needed old] cell] | |
417 (let [[changed ov] (try | |
418 (eval-cell df data old cell) | |
419 (catch Exception e | |
420 (throw (Exception. (str cell) e)))) | |
421 nn (disj needed cell)] | |
422 (if changed | |
423 [(union nn (get-neighbors (:fore-graph @df) cell)) | |
424 (assoc old (:name cell) ov)] | |
425 [nn old]))) | |
426 [new-needed new-old] (reduce step | |
427 [needed old] | |
428 (intersection now needed))] | |
429 (recur new-needed new-tops new-old)))))) | |
430 | |
431 (defn- validate-update | |
432 "Ensure that all the updated cells are source cells" | |
433 [df names] | |
434 (let [scns (set (map :name (get-source-cells df)))] | |
435 (doseq [name names] | |
436 (when (-> name scns not) | |
437 (throwf Exception "Cell %n is not a source cell" name))))) | |
438 | |
439 (defn update-values | |
440 "Given a dataflow, and a map of name-value pairs, update the | |
441 dataflow by binding the new values. Each name must be of a source | |
442 cell" | |
443 [df data] | |
444 (dosync | |
445 (validate-update df (keys data)) | |
446 (let [needed (apply union (for [name (keys data)] | |
447 (set ((:cells-map @df) name))))] | |
448 (perform-flow df data needed)))) | |
449 | |
450 (defn- initialize | |
451 "Apply all the current source cell values. Useful for a new | |
452 dataflow, or one that has been updated with new cells" | |
453 [df] | |
454 (let [needed (:cells @df) | |
455 fg (:fore-graph @df)] | |
456 (perform-flow df {} needed))) | |
457 | |
458 | |
459 ;;; Watchers | |
460 | |
461 (defn add-cell-watcher | |
462 "Adds a watcher to a cell to respond to changes of value. The is a | |
463 function of 4 values: a key, the cell, its old value, its new | |
464 value. This is implemented using Clojure's add-watch to the | |
465 underlying ref, and shared its sematics" | |
466 [cell key fun] | |
467 (let [val (:value cell)] | |
468 (add-watch val key (fn [key _ old-v new-v] | |
469 (fun key cell old-v new-v))))) | |
470 | |
471 | |
472 (comment | |
473 | |
474 (def df | |
475 (build-dataflow | |
476 [(cell :source fred 1) | |
477 (cell :source mary 0) | |
478 (cell greg (+ ?fred ?mary)) | |
479 (cell joan (+ ?fred ?mary)) | |
480 (cell joan (* ?fred ?mary)) | |
481 (cell sally (apply + ?*joan)) | |
482 (cell :validator (when (number? ?-greg) | |
483 (when (<= ?greg ?-greg) | |
484 (throwf Exception "Non monotonic"))))])) | |
485 | |
486 (do (println) | |
487 (print-dataflow df)) | |
488 | |
489 (add-cell-watcher (get-cell df 'sally) | |
490 nil | |
491 (fn [key cell o n] | |
492 (printf "sally changed from %s to %s\n" o n))) | |
493 | |
494 (update-values df {'fred 1 'mary 1}) | |
495 (update-values df {'fred 5 'mary 1}) | |
496 (update-values df {'fred 0 'mary 0}) | |
497 | |
498 (get-value df 'fred) | |
499 (get-values df 'joan) | |
500 (get-value df 'sally) | |
501 (get-value df 'greg) | |
502 | |
503 (use :reload 'clojure.contrib.dataflow) | |
504 (use 'clojure.stacktrace) (e) | |
505 (use 'clojure.contrib.trace) | |
506 ) | |
507 | |
508 | |
509 ;; End of file |