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.

Reply via email to