1

I want to create a function (thunk) that will return successive elements in a list. What is the best way to do this? I wrote this code based on an apparently flawed understanding of how local variables in clojure work:

   (defn reader-for [commands]
      (with-local-vars
        [stream commands]
        (fn [] 
          (let
            [r (var-get stream)]
            (if (empty? r)
              nil
              (let
                [cur (first r)
                 _   (var-set stream (rest r))]
                cur))))))

In this code I get:

#<CompilerException java.lang.IllegalStateException: Var null/null is unbound. (Chapel.clj:1)>

which seems to suggest that with-local-vars is dynamically scoped. Is that true? Is there any lexically scoped alternative? Thanks for any help.

2
  • 3
    How do you mean successive? [1 2 3 4] => [1 2][3 4] or [1 2 3 4] => [1 2][2 3][3 4]? Give us an example of input and desired output Commented Dec 10, 2011 at 20:28
  • Do you want all the elements grouped together at the end of going through them? Commented Dec 12, 2011 at 17:34

1 Answer 1

4

If you require mutable state, use one of the clojure reference types:

user=> (defn reader-for [coll]
         (let [a (atom coll)] 
           (fn []
             (let [x (first @a)]
               (swap! a next)
               x))))
#'user/reader-for
user=> (def f (reader-for [1 2 3]))
#'user/f
user=> (f)
1
user=> (f)
2
user=> (f)
3
user=> (f)
nil

Also, let is for lexical scoping, binding is for dynamic scoping.

Edit: the thread-safe version as pointed out by Alan.

(defn reader-for [coll]
  (let [r (ref coll)] 
   #(dosync
      (let [x (first @r)]
        (alter r next)
        x))))

And just for fun, a thread-safe version with atoms (don't do this):

(defn reader-for [coll]
  (let [a (atom coll)] 
    (fn []
      (let [ret (atom nil)]
        (swap! a (fn [[x & xs]]
                   (compare-and-set! ret nil x)
                   xs))
        @ret))))
Sign up to request clarification or add additional context in comments.

5 Comments

This solution is good but isn't thread-safe. A ref is better here, though of course there are ways to shoehorn an atom into doing what you want.
Quite correct. An atom would seem to fit since there's only one "thing", but since the read and modify must be in separate steps, there is still coordination needed.
You actually only need one atom, if you keep both the return value and the "state" in separate slots of the atom: (let [a (atom [nil coll])] (fn [] (first (swap! a (fn [[ret coll]] ((juxt first rest) coll))))))
How is it not Thread safe? Coll is never modified right? And the atom is local, so two threads calling this function with the same coll would each be working on their own mutable reference to the coll, and a copy of the coll. What am I missing here?
@DidierA. The fn returned (by a single call to reader-for) could be called by multiple threads concurrently, e.g. thread A and B could both be getting the exact same first from the atom, before swapping next into the atom. Though I'd think it is probably a rare case for the returned fn to be used by multiple threads.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.