On Dec 30, 2008, at 6:29 PM, Mark Engelberg wrote:

> On Tue, Dec 30, 2008 at 5:53 AM, Rich Hickey <richhic...@gmail.com>  
> wrote:
>> Could you provide an example of when you would need/use that?
>>
>
> Sure.
>
> Use Case #1: Implementing classic imperative algorithms
>
> Consider the binary gcd algorithm on page 338 of The Art of Computer
> Programmiing, volume 2.  This is a very clever implementation of gcd
> using only bit shifting and parity checking.  Like many classic
> algorithms, it is written in a very imperative style.  The following
> is a direct translation to Clojure:
>
> (defn binary-gcd [a b]
>  (let [k (atom 0), u (atom a), v (atom b), t (atom 0),
>       algsteps {:B1 (fn [] (if (and (even? @u) (even? @v))
>                              (do (atom-set k (inc @k))
>                                  (atom-set u (bit-shift-right @u 1))
>                                  (atom-set v (bit-shift-right @v 1))
>                                  :B1)
>                              :B2)),
>                 :B2 (fn [] (if (odd? @u)
>                              (do (atom-set t (- @v)) :B4)
>                              (do (atom-set t @u) :B3))),
>                 :B3 (fn [] (atom-set t (bit-shift-right @t 1)) :B4),
>                 :B4 (fn [] (if (even? @t) :B3 :B5))
>                 :B5 (fn [] (if (> @t 0)
>                              (atom-set u @t)
>                              (atom-set v (- @t)))
>                       :B6),
>                 :B6 (fn [] (atom-set t (- @u @v))
>                       (if (not (zero? @t)) :B3 (bit-shift-left @u @k)))}]
>    (loop [step :B1]
>      (let [next ((algsteps step))]
>       (if (number? next) next (recur next))))))
>
> To test this code, I used the following implementation of atom-set:
> (defn atom-set [a val]
>  (swap! a (constantly val)))
>
> Now, the code would certainly be cleaner if Clojure had tail-call
> optimization and letrec, because you could set each algorithmic step
> up as a mutually recursive function, rather than storing the steps in
> a hash table, and setting up a driver loop to handle the state changes
> as in a state machine.  Or if Clojure just had letrec, this could be
> expressed using the new trampoline construct.
>
> But that's not really the point.  The point here is that this code
> took me no effort to translate from the Knuth book, it worked on the
> first run with no debugging needed, and anyone can easily compare this
> code against the Knuth pseudocode, and see at a glance that this is a
> correct implementation of that algorithm.  There may be ways to
> convert this to a functional style (and I encourage others to try it
> as an interesting exercise), but with any such transformation, it will
> be significantly more difficult to confirm that the program correctly
> implements the algorithm.
>
> About half of the atom-sets are updates that could use the swap!
> function, but many of these are replacing the atom's contents with
> something completely unrelated to its existing contents.
>
> Use Case #2: setting up mutually referential data structures
>
> A common way to set up mutually referential data structures is to
> start them off with links that are set to nil, and then use imperative
> setting to direct the links at the right things.  As a quick example,
> consider the cyclic-linked-list solution to the classic Josephus
> problem.  Here is one way to set up a cyclic ring of people:
>
> (defn make-person [n]
>  {:id n, :link (atom nil)})
>
> (defn link-people [p1 p2]
>  (atom-set (p1 :link) p2))
>
> (defn ring-people [n]
>  (let [vec-people (vec (map make-person (range n)))]
>    (dotimes [i (dec n)] (link-people (vec-people i) (vec-people (inc  
> i))))
>    (link-people (vec-people (dec n)) (vec-people 0))
>    (vec-people 0)))
>
> Now depending on how this is going to be used, you could argue that
> maybe you're better served by refs.  But if the whole point of atoms
> is to give power-users another, faster choice when no coordination is
> needed, why not give the programmer a choice here?  If I know for sure
> that I'm just going to use this ring within a single thread, or use it
> internally in a function that generates and uses the ring without ever
> exposing the ring outside that function, then an atom may be the right
> tool for the job.
>
>

I don't see these as being compelling arguments for atom-set. You can  
do these things with Java arrays or other Java things like  
clojure.lang.Box.

OTOH, atom-set just invites the read-modify-write race conditions  
swap! was meant to avoid.

There's simply no value for Clojure to add to a simple mutable box.  
Clojure does provide the tools for low-level mutation - access to  
Java. You can wrap that in whatever functions/macros you like.

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

Reply via email to