On Fri, Dec 12, 2008 at 7:10 AM, Mark Engelberg <mark.engelb...@gmail.com> wrote: > > Sometimes you use mutable state inside of a function to do some local > temporary computation, and this state will never be visible outside of > the function. So externally speaking, the function is still a pure > function. Vars, Refs, and Atoms all seem to be reasonable choices for > representing this kind of state. What is the preferred style? > > For example, imagine you want to write the factorial function the > imperative way. Here are some options: > > ; functional way for reference > (defn fact0 [n] > (reduce * 1 (range 1 (inc n)))) > > ; use var > (defn fact1 [n] > (with-local-vars [total 1] > (doseq [i (range 1 (inc n))] (var-set total (* @total i))) > @total)) > > ;use ref > (defn fact2 [n] > (let [total (ref 1)] > (doseq [i (range 1 (inc n))] (dosync (ref-set total (* @total i)))) > @total)) > > ;use atom > (defn fact3 [n] > (let [total (atom 1)] > (doseq [i (range 1 (inc n))] (swap! total #(* % i))) > @total)) > > Now I don't have a sufficiently recent build to test the atom version > myself, but I tested the others. The var version has a running time > comparable to the functional version. The ref version starts out > noticeably slower, but after a few runs, hotspot gets it to where it > runs almost as fast as the other two versions. I imagine the atom > performance would be somewhere between the var and ref performance. > I'd be interested in hearing the details of atom performance from > someone who can test that, but really, it seems like the performance > is fairly similar here. It becomes a question of style, mostly. What > do you guys think is the best style (assuming you really want to code > something internally in an imperative way)? > > It is also instructive to compare the syntax of the three imperative > versions. I find it odd how similar, and yet different they each are. > I find it especially odd how you need with-local-vars to create a > local var, rather than something like (let [total (var 1)]....) which > would be more consistent with the other methods. Granted, vars have > some pretty bizarre semantics (for example, if you DID try to return a > var outside of the function, or use it in a lazy-cons, you get weird > results because the var essentially loses its binding outside of its > lexical scope). I'm guessing that's the point of with-local-vars > syntax -- to flag that it's invalid outside of that block. But still, > it seems rather quirky. I think I'd prefer local vars to be more > consistent with ref syntax. > > That said, it seems to me like the var version is the best fit for > hidden imperative behavior. The transactional nature of ref is > clearly unnecessary here, and the swapping behavior of atom only makes > sense when multiple threads might be competing to alter atom. Any > other opinions? >
Please don't do it. If you really miss your loops, use loop/recur. Rich --~--~---------~--~----~------------~-------~--~----~ 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 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 -~----------~----~----~----~------~----~------~--~---