Hi Didier, Thanks for your response, it helps for continuing to learn about the language.
As dynamic vars are implemented by using ThreadLocals, ThreadLocal is in this case a more primitive construct than dynamic vars, so I find it ok to use it if one just needs the ThreadLocal aspect, and not the dynamic scoping. As a side-topic, when looking into the Var class implementation, I have seen that some fields are volatile, in particular the root value of the var. Does that mean that every time we access a var's root value, we are implicitly crossing a memory barrier, so a cross-processor synchronization primitive. Doesn't this hit performance? Or is access to vars not considered to happen frequently? Ernesto On Saturday, March 4, 2017 at 10:59:12 PM UTC+1, Didier wrote: > Hum, you're having me question myself. > > See, I don't think dynamically scoped Vars are intended for anything in > particular, they are what they are. Yes, they are useful when you need to > pass configuration down a call stack, as long as you make sure you handle > the thread-boundaries along the way. They can also be used to share per > thread data throughout a thread's execution, which is what ThreadLocal in > java does. So basically, I see two use cases. One, you want shared read > only data accessible throughout a call stack (for that, they aren't as good > as true dynamics, because you'll need to manually handle the thread > boundaries). Or you want shared writable state per-thread, making this not > per-thread would be dangerous, and can be done if you re-bind it manually > across threads or when using constructs that does so like future. > > The reason they are made per-thread, and not truly dynamic, as in, taking > on the value down the stack no matter what, is because threads could > content on it, and it could cause bugs, so its made per-thread for safety. > Here's an example of such caveat: > > (def ^:dynamic *dd*) > > (binding [*dd* (atom "John")] > (let [a (future (reset! *dd* "Bob")) > b (future (reset! *dd* "Mike"))] > @*dd*)) > > Run this many times, and *dd*'s value could be either Mike or Bob based on > which threads wins the race. > > So the limitation of dynamic scope are inherent to dynamic scoping where > you have threads. Clojure's attempt at solving this limitation is to make > them per-thread, with explicit cross thread management through (bound-fn). > After a few releases, Clojure decided to make future and a few other > constructs implicitly propagate the dynamic var across threads as that was > often what people were expecting. All to say, when using dynamic Vars in > Clojure, you must be thread aware and understand the caveats. There is no > better way that I'm aware of to handle dynamic scope in a multi-threaded > environment. > > Clojure has root bindings, which Java ThreadLocals does not have. Clojure > lets you say, if this thread does not have a value for the dynamic Var, > fetch the root value instead. This is really useful in the use case of read > only data, like configuration, because you can set a default config. You > can simulate this in Java with initialValue. > > Now, the initialValue lets you do one more thing, it can let you generate > per-thread values on get, without needing to set it. Such as setting a > random int on the first get, and subsequent get from that thread will from > then on return that same generated random int. In Clojure, this is what > I've seen for such use case: > > (def ^:dynamic *id*) > > (defmacro with-id [& body] > `(binding [*id* (rand-int 10000)] > ~@body)) > > @(future (with-id (println (str "My request id is: " *id*)) > "Success")) > > It's not as convenient per-say, since you have to be explicit and call the > macro from each Thread before getting the value, but it solves the use case. > > Remember that ThreadLocals are more inconvenient to get though, since you > need to get the ThreadLocal and then get the value out of it. So in some > ways, Clojure dynamics can also be seen as more convenient. > > Up to now, I feel like there is nothing I can not do with a Clojure > Dynamic Var that I could with a ThreadLocal. They're not identical, but to > me, the two fulfills equal use cases. And since under the hood, Clojure > dynamic Vars are implemented with ThreadLocal, they should perform > similarly too. > > You say you can pass around references of ThreadLocals, but I'm not sure > what the point of that would be, like what use case would it allow? In > general, and even the Java doc page says so, you'll put the ThreadLocal in > a static variable, i.e., private static final ThreadLocal<Integer> > threadId. At that point, it's equal to (def ^:dynamic threadId). > > Anyways, you can also do that in Clojure, you can pass around the Var such > as: > > (defn printVar [v] > (println @v)) > > (with-local-vars [*threadId* 0] > (.start (Thread. (bound-fn [] (var-set *threadId* 1) (printVar > *threadId*)))) > (.start (Thread. (bound-fn [] (var-set *threadId* 2) (printVar > *threadId*))))) > > I have a function printVar that takes a Var and prints it. Then I create a > new Var called *threadId* and I set it to two different values, one for > each Thread. I pass the Var to my Var printing function, and as you can see > if you run this, it works like it would in Java if you did the same with > ThreadLocal, printing different values based on the thread. > > I hope this helps. > > > On Friday, 3 March 2017 07:02:21 UTC-8, Ernesto Garcia wrote: >> >> On Sunday, February 26, 2017 at 6:23:28 AM UTC+1, Didier wrote: >>> >>> Dynamic scoping and Java ThreadLocals gives you equal functionality, so >>> I'd use them equally. This is because Clojure supports thread bound dynamic >>> scope. >>> >> >> I wouldn't say that. While dynamically scoped Vars are meant to be >> context that you implicitly pass down to your call stack, ThreadLocals are >> references that you can pass around explicitly at will. >> >> Java ThreadLocals also provide an .initialValue() callback to override, >> which is not provided by Vars. Vars can't even be (idiomatically) >> initialized to a thread-bound value. >> >> I think that the fact that dynamically scoped Vars are implemented by >> ThreadLocals is an implementation detail, and it is not a feature, it is >> more of a limitation. (I guess implementing it otherwise is difficult, if >> viable at all). >> >> Thanks >> > -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en --- You received this message because you are subscribed to the Google Groups "Clojure" group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.