On Sat, Jan 22, 2011 at 11:26 AM, Eric Schulte <schulte.e...@gmail.com> wrote: > Nice concise example,
Thanks. > A while back I implemented something similar; a propagator system using > agents fed with `send', coming in at a slightly more verbose ~35 lines > of code. > > http://cs.unm.edu/~eschulte/research/propagator/ Interesting. But I have some questions ... > (defn set-cell "Set the value of a cell" [cell value] > (send cell (fn [_] value))) Why (fn [_] value) instead of (constantly value)? OK, actually (constantly value) is textually longer, but I'd argue it might be clearer. And it works; (constantly foo) accepts all arities. It's something like (fn [& _] foo). A question for Clojure's developers: why does restart-agent require the agent to be in a failed state? That seems to be a superfluous requirement. Atoms have reset! and refs ref-set to clobber the old value; agents would have something analogous if restart-agent worked on agents that weren't failed too. > (defmacro propagator "Define a new propagator." > [name in-cells out-cells & body] > `(do > (defn ~(with-meta name > (assoc (meta name) > :in-cells in-cells :out-cells out-cells)) Whoa. Why not (defmacro propagator "Define a new propagator." [name in-cells out-cells & body] `(do (defn ~(vary-meta name assoc :in-cells in-cells :out-cells out-cells) ? > (doseq [cell# ~in-cells] (add-neighbor cell# ~name)) There's no forward-declaration of add-neighbor before this. > ~name)) Usually a deffoo form returns the new var, not just a symbol. This can be fixed with (declare add-neighbor) (defmacro propagator "Define a new propagator." [name in-cells out-cells & body] `(let [v# (defn ~(vary-meta name assoc :in-cells in-cells :out-cells out-cells) ~in-cells ~@body) (doseq [cell# ~in-cells] (add-neighbor cell# ~name)) v#)) which captures the return value of the defn and returns it after the doseq. > (defmacro run-propagator > "Run a propagator, first collect the most recent values from all > cells associated with the propagator, then evaluate." > [propagator] > `(let [results# (apply ~propagator (map deref (:in-cells ^#'~propagator)))] > (doseq [cell# (:out-cells ^#'~propagator)] > (when (not (= @cell# results#)) > (send cell# (fn [_#] results#)))) > results#)) Why not use your already-defined set-cell function on that second-to-last-line? Also your syntax for accessing the metadata seems a bit odd. Why not (meta ~propagator)? Good use of metadata, though. > (defmacro add-neighbor "Add a neighbor to the given cell." > [cell neighbor] > `(add-watcher > ~cell :send > (agent nil :validator (fn [_#] (do (future (run-propagator ~neighbor)) > true))) > (fn [_# _#]))) What is "add-watcher"? I'm somewhat familiar with add-watch, whose syntax would be somewhat different. There'd be nothing between the watcher key :send and the (fn ...), and that function would take four arguments. I'd have probably used something like: (defmacro add-neighbor "Add a neighbor to the given cell." [cell neighbor] `(add-watch ~cell :send (fn [_# _# _# _#] (future (run-propagator ~neighbor)))) You could also get rid of the need to forward-declare add-neighbor by moving the propagator macro last. Convention also is that you'd call cell defcell and propagator defpropagator, since they expand to def forms. Now some more general notes: Having multiple out cells is interesting, but maybe redundant. Even with only a single out cell, the same effect can be had if you have an in cell, a bunch of out cells, and for each out cell a propagator that just returns the in cell's value. At that point the distinction between cells and propagators can go away: every cell has a propagator that sets its own value if any of its in-cells change. A cell that's intended to be an input can have an empty set of in-cells, which will result in it never doing anything since it will never be triggered. Of course the effect of that is to turn it into an almost-spreadsheet. Giving the cells a spatial organization is the last step to making it a spreadsheet. :) I hope you don't think all of that was too critical. It's very interesting work. -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en