view 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
line wrap: on
line source
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
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))
31 ;;; Chief Data Structures
34 ;; Source Cell
36 ; The data of a source cell is directly set by a calling function. It
37 ; never depends on other cells.
39 (defstruct source-cell
40 :name ; The name, a symbol
41 :value ; Its value, a Ref
42 :cell-type) ; Should be ::source-cell
44 ;; Cell
46 ; A standard cell that computes its value from other cells.
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
56 (derive ::cell ::dependent-cell) ; A cell that has a dependents field
58 ;; Validator
60 ; A cell that has no value, but can throw an exception when run
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
69 (derive ::validator-cell ::dependent-cell)
72 ;; A sentinal value
74 (def *empty-value* (java.lang.Object.))
77 ;; Dataflow
79 ; A collection of cells and dependency information
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
88 ;;; Environment Access
90 (defn get-cells
91 "Get all the cells named by name"
92 [df name]
93 ((:cells-map @df) name))
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))))
104 (defn source-cell?
105 "Is this cell a source cell?"
106 [cell]
107 (isa? (:cell-type cell) ::source-cell))
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))
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)))
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)))
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)))
144 (defn get-value-from-cell
145 "Given a cell, get its value"
146 [cell]
147 (-> cell :value deref))
149 ;;; Build Dataflow Structure
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)))
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)))
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))))
187 (def initialize)
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)))
198 ;;; Displaying a dataflow
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))))
209 ;;; Modifying a Dataflow
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))))
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))))
228 ;;; Cell building
230 (def *meta* {:type ::dataflow-cell})
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*))
238 (defn- is-col-var?
239 [symb]
240 (let [name (name symb)]
241 (and (= \? (first name))
242 (= \* (second name)))))
244 (defn- is-old-var?
245 [symb]
246 (let [name (name symb)]
247 (and (= \? (first name))
248 (= \- (second name)))))
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))))
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))))
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))
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))))
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)))
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*))
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*))
305 (defmacro cell
306 "Build a standard cell, like this:
308 (cell fred
309 (* ?mary ?joe))
311 Which creates a cell named fred that is the product of a cell mary and cell joe
313 Or:
315 (cell joe
316 (apply * ?*sally))
318 Which creates a cell that applies * to the collection of all cells named sally
320 Or:
322 (cell :source fred 0)
324 Which builds a source cell fred with initial value 0
326 Or:
328 (cell :validator (when (< ?fred ?sally)
329 (throwf \"%s must be greater than %s\" ?fred ?sally))
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))))
347 ;;; Cell Display
349 (defmulti display-cell
350 "A 'readable' form of the cell"
351 :cell-type)
353 (defmethod display-cell ::source-cell
354 [cell]
355 (list 'cell :source (:name cell) (-> cell :value deref)))
357 (defmethod display-cell ::cell
358 [cell]
359 (list 'cell (:name cell) (:display cell) (-> cell :value deref)))
361 (defmethod display-cell ::validator-cell
362 [cell]
363 (list 'cell :validator (:display cell)))
365 (defmethod print-method ::dataflow-cell
366 [f ^Writer w]
367 (binding [*out* w]
368 (pr (display-cell f))))
371 ;;; Evaluation
373 (defmulti eval-cell
374 "Evaluate a dataflow cell. Return [changed, old val]"
375 (fn [df data old cell] (:cell-type cell)))
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])))
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])))
400 (defmethod eval-cell ::validator-cell
401 [df data old cell]
402 (do ((:fun cell) df old)
403 [false nil]))
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))))))
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)))))
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))))
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)))
459 ;;; Watchers
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)))))
472 (comment
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"))))]))
486 (do (println)
487 (print-dataflow df))
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)))
494 (update-values df {'fred 1 'mary 1})
495 (update-values df {'fred 5 'mary 1})
496 (update-values df {'fred 0 'mary 0})
498 (get-value df 'fred)
499 (get-values df 'joan)
500 (get-value df 'sally)
501 (get-value df 'greg)
503 (use :reload 'clojure.contrib.dataflow)
504 (use 'clojure.stacktrace) (e)
505 (use 'clojure.contrib.trace)
506 )
509 ;; End of file