There are several somewhat old blog posts out there advising caution when mixing dynamic variables, binding
, and pmap
, e.g. here, where we get the following code snippet:
user=> (def *foo* 5)
#'user/*foo*
user=> (defn adder
[param]
(+ *foo* param))
#'user/adder
user=> (binding [*foo* 10]
(doseq [v (pmap adder (repeat 3 5))]
(println v)))
10
10
10
nil
But that's not what happens when I run that code (changing the first line to (def ^:dynamic *foo* 5)
). I get three 15
s as output (using Clojure 1.4), just as you would naïvely expect—that is, with the change in the binding form seen by the function passed to pmap. Have the way thread-local bindings and pmap interact changed? I can't find this documented anywhere.
starting in 1.3 the set of local bindings are sent to pmap along with the function. So as long as you mark the var ^:dynamic this is no longer a problem. This feature it called Binding Conveyance and is included in the 1.3 changelog:
from: https://github.com/clojure/clojure/blob/1.3.x/changes.txt
Clojure APIs that pass work off to other threads (e.g. send, send-off, pmap, future) now convey the dynamic bindings of the calling thread: (def ^:dynamic *num* 1) (binding [*num* 2] (future (println *num*))) ;; prints "2", not "1"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With