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

Reply via email to