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? --Mark --~--~---------~--~----~------------~-------~--~----~ 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 -~----------~----~----~----~------~----~------~--~---