Wednesday, August 25, 2010

Clojure Counter

One of the first programs I wrote in Clojure was a simple counter.
I am quite interested in the stateful/stateless mapping and that is the easiest possible example with refs. Refs are the simplest threadsafe state primitive.

This is a counter:
(defn  make-counter []
  (let [counter (ref 0)]
    (fn [] (dosync (alter counter inc)))))

Ok, this is a bit too schemish that it should have been. I encapsulated the actual counter in the local counter variable, which is accessible only from the function I return when one makes a counter. So this counter is basically a function which always return the next number.

In scheme that would have been something like:
(define make-counter
  (lambda ()
    (let ([counter 0])
      (lambda ()
        (let ([result counter])
          (set! counter (+ counter 1))
          result))))

The clojure variant (using dosync and alter) is thread-safe. Essentially modification to the ref is allowed only in a dosync context. Modification happens through the alter function (alter what update-function).

In order to try the code, I wrote this:

(defn print-some [get-next-function how-many]
  (dotimes [_ how-many]
    (print (.getName (Thread/currentThread)))
    (print ": ")
    (println (get-next-function))))

(let [counter (make-counter)]
  (dotimes [x 10]
    (.start (Thread.
             (fn [] (print-some counter 10))
             (Integer/toString x)))))

I particularly like it (even though it is obviously a mistake) because of the way it messes the output. In fact the three print functions (well, one is a println) are not atomic: the output is not locked. This means that the output is completely mixed.

This is good because it shows that threads are really running in parallel (well, I have an 8 virtual core machine, after all). On the other hand, the counter is always perfect: numbers can be swapped (but that is natural) but no number is repeated twice or skipped.

No comments: