Mercurial > lasercutter
diff 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 diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/clojure/contrib/dataflow.clj Sat Aug 21 06:25:44 2010 -0400 1.3 @@ -0,0 +1,509 @@ 1.4 +;; Copyright (c) Jeffrey Straszheim. All rights reserved. The use and 1.5 +;; distribution terms for this software are covered by the Eclipse Public 1.6 +;; License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) which can 1.7 +;; be found in the file epl-v10.html at the root of this distribution. By 1.8 +;; using this software in any fashion, you are agreeing to be bound by the 1.9 +;; terms of this license. You must not remove this notice, or any other, 1.10 +;; from this software. 1.11 +;; 1.12 +;; dataflow.clj 1.13 +;; 1.14 +;; A Library to Support a Dataflow Model of State 1.15 +;; 1.16 +;; straszheimjeffrey (gmail) 1.17 +;; Created 10 March 2009 1.18 + 1.19 + 1.20 +(ns 1.21 + ^{:author "Jeffrey Straszheim", 1.22 + :doc "A library to support a dataflow model of state"} 1.23 + clojure.contrib.dataflow 1.24 + (:use [clojure.set :only (union intersection difference)]) 1.25 + (:use [clojure.contrib.graph :only (directed-graph 1.26 + reverse-graph 1.27 + dependency-list 1.28 + get-neighbors)]) 1.29 + (:use [clojure.walk :only (postwalk)]) 1.30 + (:use [clojure.contrib.except :only (throwf)]) 1.31 + (:import java.io.Writer)) 1.32 + 1.33 + 1.34 +;;; Chief Data Structures 1.35 + 1.36 + 1.37 +;; Source Cell 1.38 + 1.39 +; The data of a source cell is directly set by a calling function. It 1.40 +; never depends on other cells. 1.41 + 1.42 +(defstruct source-cell 1.43 + :name ; The name, a symbol 1.44 + :value ; Its value, a Ref 1.45 + :cell-type) ; Should be ::source-cell 1.46 + 1.47 +;; Cell 1.48 + 1.49 +; A standard cell that computes its value from other cells. 1.50 + 1.51 +(defstruct standard-cell 1.52 + :name ; The name, a symbol 1.53 + :value ; Its value, a Ref 1.54 + :dependents ; The names of cells on which this depends, a collection 1.55 + :fun ; A closure that computes the value, given an environment 1.56 + :display ; The original expression for display 1.57 + :cell-type) ; Should be ::cell 1.58 + 1.59 +(derive ::cell ::dependent-cell) ; A cell that has a dependents field 1.60 + 1.61 +;; Validator 1.62 + 1.63 +; A cell that has no value, but can throw an exception when run 1.64 + 1.65 +(defstruct validator-cell 1.66 + :name ; Always ::validator 1.67 + :dependents ; The names of cells on which this depends, a collection 1.68 + :fun ; A clojure that can throw an exception 1.69 + :display ; The original exprssion for display 1.70 + :cell-type) ; Should be ::validator-cell 1.71 + 1.72 +(derive ::validator-cell ::dependent-cell) 1.73 + 1.74 + 1.75 +;; A sentinal value 1.76 + 1.77 +(def *empty-value* (java.lang.Object.)) 1.78 + 1.79 + 1.80 +;; Dataflow 1.81 + 1.82 +; A collection of cells and dependency information 1.83 + 1.84 +(defstruct dataflow 1.85 + :cells ; A set of all cells 1.86 + :cells-map ; A map of cell names (symbols) to collections of cells 1.87 + :fore-graph ; The inverse of the dependency graph, nodes are cells 1.88 + :topological) ; A vector of sets of independent nodes -- orders the computation 1.89 + 1.90 + 1.91 +;;; Environment Access 1.92 + 1.93 +(defn get-cells 1.94 + "Get all the cells named by name" 1.95 + [df name] 1.96 + ((:cells-map @df) name)) 1.97 + 1.98 +(defn get-cell 1.99 + "Get the single cell named by name" 1.100 + [df name] 1.101 + (let [cells (get-cells df name)] 1.102 + (cond 1.103 + (= (count cells) 1) (first cells) 1.104 + (> (count cells) 1) (throwf Exception "Cell %s has multiple instances" name) 1.105 + :otherwise (throwf Exception "Cell %s is undefined" name)))) 1.106 + 1.107 +(defn source-cell? 1.108 + "Is this cell a source cell?" 1.109 + [cell] 1.110 + (isa? (:cell-type cell) ::source-cell)) 1.111 + 1.112 +(defn get-source-cells 1.113 + "Returns a collection of source cells from the dataflow" 1.114 + [df] 1.115 + (for [cell (:cells @df) 1.116 + :when (source-cell? cell)] 1.117 + cell)) 1.118 + 1.119 +(defn get-value 1.120 + "Gets a value from the df matching the passed symbol. 1.121 + Signals an error if the name is not present, or if it not a single 1.122 + value." 1.123 + [df name] 1.124 + (let [cell (get-cell df name) 1.125 + result @(:value cell)] 1.126 + (do (when (= *empty-value* result) 1.127 + (throwf Exception "Cell named %s empty" name)) 1.128 + result))) 1.129 + 1.130 +(defn get-values 1.131 + "Gets a collection of values from the df by name" 1.132 + [df name] 1.133 + (let [cells (get-cells df name) 1.134 + results (map #(-> % :value deref) cells)] 1.135 + (do 1.136 + (when (some #(= % *empty-value*) results) 1.137 + (throwf Exception "At least one empty cell named %s found" name)) 1.138 + results))) 1.139 + 1.140 +(defn get-old-value 1.141 + "Looks up an old value" 1.142 + [df env name] 1.143 + (if (contains? env name) 1.144 + (env name) 1.145 + (get-value df name))) 1.146 + 1.147 +(defn get-value-from-cell 1.148 + "Given a cell, get its value" 1.149 + [cell] 1.150 + (-> cell :value deref)) 1.151 + 1.152 +;;; Build Dataflow Structure 1.153 + 1.154 +(defn- build-cells-map 1.155 + "Given a collection of cells, build a name->cells-collection map 1.156 + from it." 1.157 + [cs] 1.158 + (let [step (fn [m c] 1.159 + (let [n (:name c) 1.160 + o (get m n #{}) 1.161 + s (conj o c)] 1.162 + (assoc m n s)))] 1.163 + (reduce step {} cs))) 1.164 + 1.165 +(defn- build-back-graph 1.166 + "Builds the backward dependency graph from the cells map. Each 1.167 + node of the graph is a cell." 1.168 + [cells cells-map] 1.169 + (let [step (fn [n] 1.170 + (apply union (for [dep-name (:dependents n)] 1.171 + (cells-map dep-name)))) 1.172 + neighbors (zipmap cells (map step cells))] 1.173 + (struct-map directed-graph 1.174 + :nodes cells 1.175 + :neighbors neighbors))) 1.176 + 1.177 +(defn- build-dataflow* 1.178 + "Builds the dataflow structure" 1.179 + [cs] 1.180 + (let [cells (set cs) 1.181 + cells-map (build-cells-map cs) 1.182 + back-graph (build-back-graph cells cells-map) 1.183 + fore-graph (reverse-graph back-graph)] 1.184 + (struct-map dataflow 1.185 + :cells cells 1.186 + :cells-map cells-map 1.187 + :fore-graph fore-graph 1.188 + :topological (dependency-list back-graph)))) 1.189 + 1.190 +(def initialize) 1.191 + 1.192 +(defn build-dataflow 1.193 + "Given a collection of cells, build and return a dataflow object" 1.194 + [cs] 1.195 + (dosync 1.196 + (let [df (ref (build-dataflow* cs))] 1.197 + (initialize df) 1.198 + df))) 1.199 + 1.200 + 1.201 +;;; Displaying a dataflow 1.202 + 1.203 +(defn print-dataflow 1.204 + "Prints a dataflow, one cell per line" 1.205 + [df] 1.206 + (println) 1.207 + (let [f (fn [cell] (-> cell :name str))] 1.208 + (doseq [cell (sort-by f (:cells @df))] 1.209 + (prn cell)))) 1.210 + 1.211 + 1.212 +;;; Modifying a Dataflow 1.213 + 1.214 +(defn add-cells 1.215 + "Given a collection of cells, add them to the dataflow." 1.216 + [df cells] 1.217 + (dosync 1.218 + (let [new-cells (union (set cells) (:cells @df))] 1.219 + (ref-set df (build-dataflow* new-cells)) 1.220 + (initialize df)))) 1.221 + 1.222 +(defn remove-cells 1.223 + "Given a collection of cells, remove them from the dataflow." 1.224 + [df cells] 1.225 + (dosync 1.226 + (let [new-cells (difference (:cells @df) (set cells))] 1.227 + (ref-set df (build-dataflow* new-cells)) 1.228 + (initialize df)))) 1.229 + 1.230 + 1.231 +;;; Cell building 1.232 + 1.233 +(def *meta* {:type ::dataflow-cell}) 1.234 + 1.235 +(defn build-source-cell 1.236 + "Builds a source cell" 1.237 + [name init] 1.238 + (with-meta (struct source-cell name (ref init) ::source-cell) 1.239 + *meta*)) 1.240 + 1.241 +(defn- is-col-var? 1.242 + [symb] 1.243 + (let [name (name symb)] 1.244 + (and (= \? (first name)) 1.245 + (= \* (second name))))) 1.246 + 1.247 +(defn- is-old-var? 1.248 + [symb] 1.249 + (let [name (name symb)] 1.250 + (and (= \? (first name)) 1.251 + (= \- (second name))))) 1.252 + 1.253 +(defn- is-var? 1.254 + [symb] 1.255 + (let [name (name symb)] 1.256 + (and (= \? (first name)) 1.257 + (-> symb is-col-var? not) 1.258 + (-> symb is-old-var? not)))) 1.259 + 1.260 +(defn- cell-name 1.261 + [symb] 1.262 + `(quote ~(cond (is-var? symb) (-> symb name (.substring 1) symbol) 1.263 + (or (is-col-var? symb) 1.264 + (is-old-var? symb)) (-> symb name (.substring 2) symbol)))) 1.265 + 1.266 +(defn- replace-symbol 1.267 + "Walk the from replacing the ?X forms with the needed calls" 1.268 + [dfs ov form] 1.269 + (cond 1.270 + (-> form symbol? not) form 1.271 + (is-var? form) `(get-value ~dfs ~(cell-name form)) 1.272 + (is-col-var? form) `(get-values ~dfs ~(cell-name form)) 1.273 + (is-old-var? form) `(get-old-value ~dfs ~ov ~(cell-name form)) 1.274 + :otherwise form)) 1.275 + 1.276 +(defn- build-fun 1.277 + "Build the closure needed to compute a cell" 1.278 + [form] 1.279 + (let [dfs (gensym "df_") 1.280 + ov (gensym "old_")] 1.281 + `(fn [~dfs ~ov] ~(postwalk (partial replace-symbol dfs ov) form)))) 1.282 + 1.283 +(defn- get-deps 1.284 + "Get the names of the dependent cells" 1.285 + [form] 1.286 + (let [step (fn [f] 1.287 + (cond 1.288 + (coll? f) (apply union f) 1.289 + (-> f symbol? not) nil 1.290 + (is-var? f) #{(cell-name f)} 1.291 + (is-col-var? f) #{(cell-name f)} 1.292 + (is-old-var? f) #{(cell-name f)} 1.293 + :otherwise nil))] 1.294 + (postwalk step form))) 1.295 + 1.296 +(defn build-standard-cell 1.297 + "Builds a standard cell" 1.298 + [name deps fun expr] 1.299 + (with-meta (struct standard-cell name (ref *empty-value*) deps fun expr ::cell) 1.300 + *meta*)) 1.301 + 1.302 +(defn build-validator-cell 1.303 + "Builds a validator cell" 1.304 + [deps fun expr] 1.305 + (with-meta (struct validator-cell ::validator deps fun expr ::validator-cell) 1.306 + *meta*)) 1.307 + 1.308 +(defmacro cell 1.309 + "Build a standard cell, like this: 1.310 + 1.311 + (cell fred 1.312 + (* ?mary ?joe)) 1.313 + 1.314 + Which creates a cell named fred that is the product of a cell mary and cell joe 1.315 + 1.316 + Or: 1.317 + 1.318 + (cell joe 1.319 + (apply * ?*sally)) 1.320 + 1.321 + Which creates a cell that applies * to the collection of all cells named sally 1.322 + 1.323 + Or: 1.324 + 1.325 + (cell :source fred 0) 1.326 + 1.327 + Which builds a source cell fred with initial value 0 1.328 + 1.329 + Or: 1.330 + 1.331 + (cell :validator (when (< ?fred ?sally) 1.332 + (throwf \"%s must be greater than %s\" ?fred ?sally)) 1.333 + 1.334 + Which will perform the validation" 1.335 + [type & data] 1.336 + (cond 1.337 + (symbol? type) (let [name type ; No type for standard cell 1.338 + expr (first data) ; we ignore extra data! 1.339 + deps (get-deps expr) 1.340 + fun (build-fun expr)] 1.341 + `(build-standard-cell '~name ~deps ~fun '~expr)) 1.342 + (= type :source) (let [[name init] data] 1.343 + `(build-source-cell '~name ~init)) 1.344 + (= type :validator) (let [[expr] data 1.345 + deps (get-deps expr) 1.346 + fun (build-fun expr)] 1.347 + `(build-validator-cell ~deps ~fun '~expr)))) 1.348 + 1.349 + 1.350 +;;; Cell Display 1.351 + 1.352 +(defmulti display-cell 1.353 + "A 'readable' form of the cell" 1.354 + :cell-type) 1.355 + 1.356 +(defmethod display-cell ::source-cell 1.357 + [cell] 1.358 + (list 'cell :source (:name cell) (-> cell :value deref))) 1.359 + 1.360 +(defmethod display-cell ::cell 1.361 + [cell] 1.362 + (list 'cell (:name cell) (:display cell) (-> cell :value deref))) 1.363 + 1.364 +(defmethod display-cell ::validator-cell 1.365 + [cell] 1.366 + (list 'cell :validator (:display cell))) 1.367 + 1.368 +(defmethod print-method ::dataflow-cell 1.369 + [f ^Writer w] 1.370 + (binding [*out* w] 1.371 + (pr (display-cell f)))) 1.372 + 1.373 + 1.374 +;;; Evaluation 1.375 + 1.376 +(defmulti eval-cell 1.377 + "Evaluate a dataflow cell. Return [changed, old val]" 1.378 + (fn [df data old cell] (:cell-type cell))) 1.379 + 1.380 +(defmethod eval-cell ::source-cell 1.381 + [df data old cell] 1.382 + (let [name (:name cell) 1.383 + val (:value cell) 1.384 + ov @val] 1.385 + (if (contains? data name) 1.386 + (let [new-val (data name)] 1.387 + (if (not= ov new-val) 1.388 + (do (ref-set val new-val) 1.389 + [true ov]) 1.390 + [false ov])) 1.391 + [false ov]))) 1.392 + 1.393 +(defmethod eval-cell ::cell 1.394 + [df data old cell] 1.395 + (let [val (:value cell) 1.396 + old-val @val 1.397 + new-val ((:fun cell) df old)] 1.398 + (if (not= old-val new-val) 1.399 + (do (ref-set val new-val) 1.400 + [true old-val]) 1.401 + [false old-val]))) 1.402 + 1.403 +(defmethod eval-cell ::validator-cell 1.404 + [df data old cell] 1.405 + (do ((:fun cell) df old) 1.406 + [false nil])) 1.407 + 1.408 +(defn- perform-flow 1.409 + "Evaluate the needed cells (a set) from the given dataflow. Data is 1.410 + a name-value mapping of new values for the source cells" 1.411 + [df data needed] 1.412 + (loop [needed needed 1.413 + tops (:topological @df) 1.414 + old {}] 1.415 + (let [now (first tops) ; Now is a set of nodes 1.416 + new-tops (next tops)] 1.417 + (when (and (-> needed empty? not) 1.418 + (-> now empty? not)) 1.419 + (let [step (fn [[needed old] cell] 1.420 + (let [[changed ov] (try 1.421 + (eval-cell df data old cell) 1.422 + (catch Exception e 1.423 + (throw (Exception. (str cell) e)))) 1.424 + nn (disj needed cell)] 1.425 + (if changed 1.426 + [(union nn (get-neighbors (:fore-graph @df) cell)) 1.427 + (assoc old (:name cell) ov)] 1.428 + [nn old]))) 1.429 + [new-needed new-old] (reduce step 1.430 + [needed old] 1.431 + (intersection now needed))] 1.432 + (recur new-needed new-tops new-old)))))) 1.433 + 1.434 +(defn- validate-update 1.435 + "Ensure that all the updated cells are source cells" 1.436 + [df names] 1.437 + (let [scns (set (map :name (get-source-cells df)))] 1.438 + (doseq [name names] 1.439 + (when (-> name scns not) 1.440 + (throwf Exception "Cell %n is not a source cell" name))))) 1.441 + 1.442 +(defn update-values 1.443 + "Given a dataflow, and a map of name-value pairs, update the 1.444 + dataflow by binding the new values. Each name must be of a source 1.445 + cell" 1.446 + [df data] 1.447 + (dosync 1.448 + (validate-update df (keys data)) 1.449 + (let [needed (apply union (for [name (keys data)] 1.450 + (set ((:cells-map @df) name))))] 1.451 + (perform-flow df data needed)))) 1.452 + 1.453 +(defn- initialize 1.454 + "Apply all the current source cell values. Useful for a new 1.455 + dataflow, or one that has been updated with new cells" 1.456 + [df] 1.457 + (let [needed (:cells @df) 1.458 + fg (:fore-graph @df)] 1.459 + (perform-flow df {} needed))) 1.460 + 1.461 + 1.462 +;;; Watchers 1.463 + 1.464 +(defn add-cell-watcher 1.465 + "Adds a watcher to a cell to respond to changes of value. The is a 1.466 + function of 4 values: a key, the cell, its old value, its new 1.467 + value. This is implemented using Clojure's add-watch to the 1.468 + underlying ref, and shared its sematics" 1.469 + [cell key fun] 1.470 + (let [val (:value cell)] 1.471 + (add-watch val key (fn [key _ old-v new-v] 1.472 + (fun key cell old-v new-v))))) 1.473 + 1.474 + 1.475 +(comment 1.476 + 1.477 + (def df 1.478 + (build-dataflow 1.479 + [(cell :source fred 1) 1.480 + (cell :source mary 0) 1.481 + (cell greg (+ ?fred ?mary)) 1.482 + (cell joan (+ ?fred ?mary)) 1.483 + (cell joan (* ?fred ?mary)) 1.484 + (cell sally (apply + ?*joan)) 1.485 + (cell :validator (when (number? ?-greg) 1.486 + (when (<= ?greg ?-greg) 1.487 + (throwf Exception "Non monotonic"))))])) 1.488 + 1.489 + (do (println) 1.490 + (print-dataflow df)) 1.491 + 1.492 + (add-cell-watcher (get-cell df 'sally) 1.493 + nil 1.494 + (fn [key cell o n] 1.495 + (printf "sally changed from %s to %s\n" o n))) 1.496 + 1.497 + (update-values df {'fred 1 'mary 1}) 1.498 + (update-values df {'fred 5 'mary 1}) 1.499 + (update-values df {'fred 0 'mary 0}) 1.500 + 1.501 + (get-value df 'fred) 1.502 + (get-values df 'joan) 1.503 + (get-value df 'sally) 1.504 + (get-value df 'greg) 1.505 + 1.506 + (use :reload 'clojure.contrib.dataflow) 1.507 + (use 'clojure.stacktrace) (e) 1.508 + (use 'clojure.contrib.trace) 1.509 +) 1.510 + 1.511 + 1.512 +;; End of file