clojure.core has the macros bindings and with-redefs. Looking at the docstrings and the examples on clojuredocs.org, they seem to do something very similar. What is the difference and which one should I use in which situations?
Asked
Active
Viewed 3,844 times
32
-
3Also see this question: http://stackoverflow.com/questions/15747774/whats-the-point-of-defining-something-as-dynamic-when-you-dont-need-to-define/15748334#15748334 – Alex Nov 22 '13 at 14:52
1 Answers
36
Clojure Vars can have thread-local bindings. binding
uses these, while with-redefs
actually alters the root binding (which is someting like the default value) of the var.
Another difference is that binding
only works for :dynamic
vars while with-redefs
works for all vars.
Examples:
user=> (def ^:dynamic *a* 1)
#'user/*a*
user=> (binding [*a* 2] *a*)
2
user=> (with-redefs [*a* 2] *a*)
2
user=> (binding [*a* 2] (doto (Thread. (fn [] (println "*a* is " *a*))) (.start) (.join)))
*a* is 1
#<Thread Thread[Thread-2,5,]>
user=> (with-redefs [*a* 2] (doto (Thread. (fn [] (println "*a* is " *a*))) (.start) (.join)))
*a* is 2
#<Thread Thread[Thread-3,5,]>
You can use the (undocumented) binding-conveyor-fn
to convey thread-local bindings into new threads:
user=> (binding [*a* 2] (doto (Thread. (#'clojure.core/binding-conveyor-fn (fn [] (println "*a* is " *a*)))) (.start) (.join)))
*a* is 2
#<Thread Thread[Thread-5,5,]>

opqdonut
- 5,119
- 22
- 25
-
6All of which is why `with-redefs` is meant for use in tests (where you may want to reach in and stub out a function), which `binding` can be useful in production code as well. – Peeja Sep 29 '14 at 14:43
-
1@Peeja thanks, so in other words, `with-redefs` should never be used in multi-threaded contexts? – Erik Kaplun Jun 11 '19 at 11:47
-
@opqdonut as of 2019 june, the Vars documentation has a [Binding conveyance](https://clojure.org/reference/vars#conveyance) section which highlights `future`, `send`, `send-off` and `pmap` as having the capabilities for binding conveyance — does it mean that `binding-conveyor-fn` is no longer needed? – Erik Kaplun Jun 11 '19 at 11:50
-
1@ErikKaplun, `binding-conveyor-fn` is not needed if you use the functions you mentioned. However, if you create threads with `(Thread. ...)`, you still need it. – Miikka Jan 14 '20 at 09:27