What's the right way to handle lazy evaluation in a dynamic binding?

As an example, the following snipit returns (100 100 100) even though I’d like it to return (10 10 10)

(def ^:dynamic x 100)

(defn lazy-eval []
  (map identity [x]))

(take 3 (binding [x 10] (repeatedly lazy-eval)))

I know that this is technically correct, because the function call to lazy-eval happens outside of the binding’s scope, but:

  • it’s counterintuitive to what I actually want
  • the premise of lazy evaluation-by-default is performance optimization, i.e., if you call a pure function it doesn’t matter when/if you look at the value, but I don’t want lazy eval to impact behavior
  • I know that technically dynamic binding is implemented as a mutable operation (especially in cljs lol), but ideologically, I think of dynamic binding as a variable capture within the binding’s scope. i.e. within the scope of the binding lazy-eval is a function that returns (10 10 10)

Is there a correct way to get at this behavior without causing a bunch of weird other effects?

if you call a pure function it doesn’t matter when/if you look at the value

But lazy-eval is not pure.

Is there a correct way to get at this behavior without causing a bunch of weird other effects?

Capture the dynamic value deliberately OR bind the value where you’re going to realize the seq.

For example:

(def ^:dynamic x 100)

(defn lazy-eval [x]
  (map identity [x]))

(defn bind-lazy-eval [x]
  #(lazy-eval x))

(take 3 (binding [x 10] (repeatedly (bind-lazy-eval x))))
;; OR
(binding [x 10] (doall (take 3 (repeatedly #(lazy-eval x)))))
1 Like

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.