Search code examples
clojurethread-safetylazy-evaluation

repeatedly vs. binding


(def ^:dynamic *d* 1)

(binding [*d* 2]
  (println *d*)
  (repeatedly 1 #(println *d*)))

Output:

2
1

Why? Why does the function inside the repeatedly see the value of the dynamic var from outside the binding?

By the way, I checked (.getId (java.lang.Thread/currentThread)) inside and outside the anonymous function: it's the same.


Solution

  • The lazy sequence created by repeatedly is returned from the form, and then realized only when printed via the REPL, after the binding has been "unwound," and it is at this point that the anonymous function is being called. To see that this is the case, try these two variations:

    (binding [*d* 2]
      (println *d*)
      (let [x (repeatedly 1 #(println *d*))]
        (println (realized? x))
        x))
    

    and

    (binding [*d* 2]
      (println *d*)
      (doall (repeatedly 1 #(println *d*))))
    

    The second variation forces the sequence to be fully realized while still within the scope of the binding.

    Note that another way to force the issue is to "capture" the binding by using bound-fn:

    (binding [*d* 2]
      (println *d*)
      (repeatedly 1 (bound-fn [] (println *d*))))