Attilio
Attilio

Reputation: 1722

Clojure with-local-vars in closure

In Clojure, are the variables defined through with-local-vars accessible through closure?

Consider the below example:

(defn foo []
      (with-local-vars [bar 10]
           (fn [] @bar)))

((foo))

Result is the following:

#object[clojure.lang.Var$Unbound 0x131f68d8 "Unbound: #<Var: --unnamed-->"]

(Instead, I was expecting to get 10.)

C.f. with the following:

(defn foo []
     (with-local-vars [bar 10] @bar))
(foo)

Result: 10.

Based on the documentation, it is not clear to me, if it is valid to use local vars in Clojure as above, but I would suspect the answer is no. Can you please confirm this (or refute, and explain what I'm doing wrong in the first example)? And if my supposition is clear (i.e., that local vars cannot be used in the closure), then explain what is the reason for that?

EDIT: for the records, this is the problem I was trying to solve.

Upvotes: 2

Views: 433

Answers (1)

DaoWen
DaoWen

Reputation: 33019

The documentation for with-local-vars doesn't seem particularly clear here. It just says it creates thread-local bindings for the variables, but doesn't say anything about what happens to them when you exit the with-local-vars scope.

In contrast, the with-bindings documentation explicitly says that the thread-local bindings are popped when leaving that scope.

If you look at the source code, you can see that both with-local-vars and with-bindings are implemented using the same basic mechanisms (pushThreadBindings and popThreadBindings), which suggests they should have almost identical behavior.

So yes, you're right, you cannot expect the with-local-vars values captured in a closure to work outside of the with-local-vars scope. However, Clojure provides the bound-fn mechanism specifically for building this type of closure, capturing all of the current thread-local bindings:

(def foo (with-local-vars [bar 10] (fn [] @bar)))
(foo)
; => #object[clojure.lang.Var$Unbound 0x60063e12 "Unbound: #<Var: --unnamed-->"]

(def baz (with-local-vars [bar 10] (bound-fn [] @bar)))
(baz)
; => 10

Upvotes: 9

Related Questions