Reputation: 1722
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
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