Etherplex

Rick Dillon's home on the net…

Using refs in Clojure to Scope State

with 2 comments

I mentioned a couple of posts ago about a technique for scoping state I used when I programmed in Scheme more often than I do now.  As I’ve been picking up Clojure as my hacking language of preference, I was surprised to find that it doesn’t support the same idioms.  In Clojure, any variables referenced when  a function is defined in a local scope are unbound when the function is returned to an outer scope.  This means the code I wrote in Scheme doesn’t have a direct analogue in Clojure.  This was a deliberate decision made by Rich Hickey, Clojure’s designer/author.  As he says:

If locals were variables, i.e. mutable, then closures could close over mutable state, and, given that closures can escape (without some extra prohibition on same), the result would be thread-unsafe. And people would certainly do so, e.g. closure-based pseudo-objects. The result would be a huge hole in Clojure’s approach.

The end result is that when you first pick up Clojure, if you try to close a function definition over a variable, you’ll find it is no longer bound when you leave the enclosing scope, and if you try to close over let-bound value (local), you’ll find that is immutable, and can therefore not be used to store state.  So how do we accomplish the same result as we did in Scheme?

You use Clojure’s shared transactional memory and create a mutable ref that is modified in a transaction to preserve thread-safety:

(let [z (ref 0)] (defn add [] (dosync (ref-set z (inc @z)))))

We still make use of local binding, but we bind z to a ref, which we can then modify inside of a dosync call.  Naturally, an arbitrary number of functions could be defined inside of the let that all shared a reference to z.  Once outside of the let, z is no longer accessible to the casual programmer, but the functions still have full access to it, creating a sort of data hiding/state-preserving closure that protects the “private” variable z.

I don’t use this idiom often, but when I want it, it’s exactly what I want, and avoids the need for more complex constructs to simply have a pair of functions share a variable that isn’t modifiable in other parts of the program.

Written by Rick

October 25th, 2008 at 11:21 pm

Posted in Programming

2 Responses to 'Using refs in Clojure to Scope State'

Subscribe to comments with RSS

  1. I am in a bit of a crusade trying to clarify some points about closures and Clojure (lol!). I ran into a series of posts last night, like this one, which while technically true, sound misleading. What Rich is trying to say, is that allowing a closure to have a mutable value trapped inside is a ticking bomb.. in ANY language.. Clojure forces you to stop and think before providing you with the gun to shoot yourself in the foot (and yes, you can but you have to try hard to do so :-) . So, yes you can have closures, yes they can be closed over a mutable something, but, like with any other mutable state in Clojure it should be done with an atom or ref. Like this:

    (defn accumulator
    []
    (let [x (atom 0)]
    (fn []
    (swap! x inc))))

    user> (def my-accumulator (accumulator))
    #’user/my-accumulator
    user> (my-accumulator)
    1
    user> (my-accumulator)
    2
    user> (my-accumulator)
    3

    That is a vanilla 101 closure in Clojure. What is forced or magic about that. I don’t know about your favourite LISP, but if it allows you to define closures over mutable things that are not thread safe, it might be prettier to write, but sure as hell it will be a lot more “fun” to debug ;-)

    Erick

    29 Aug 11 at 21:47

  2. Sorry, just to clarify, your posting is not the problem (I just realized my comment might make it sound like I am implying that), Your example is perfectly fine and thread safe of course, etc.. but others are linking to it stating things like “see.. Clojure has got a problem with thread safety.. look at the explanation here”. OMG, that is not what the guy said!!! sigh!. Just wanted to provide a more “classic LISPy looking” example to dispel the FUD floating around that “you can do closures in Clojure but it is hard/a hack/forced”.. oh man, it is the only language I know of which FORCES you to have thread safe mutable closures!

    Erick

    29 Aug 11 at 21:58

Leave a Reply