> > which brings up the limitation of implementing dynamically scoped vars > with ThreadLocal: It would be reasonable to expect that the bindings of > dynamic vars propagate to all code inside the same function, even if > executed by a different thread >
This is not a limitation, this was done on purpose. Traditional dynamic vars are not thread safe and don't play well inside threaded environments. Clojure purposely restricted the scope to single threads. You can propagate it explicitly by using (bound-fn). Dynamic vars in Clojure were designed as a concurrency primitive, not as an implicit parameter, though it is fine to use them for that if you know what you are doing. You can see that from Rich Hickey's Clojure slides: https://i.imgur.com/jx2vtPb.png A Clojure var is not only an object, it is a language construct. And when > you make it dynamic, you can bind and re-bind it with different values as > your functions are invoked within each other. This is something a > ThreadLocal can't do, and it makes the dynamic var a different kind of > beast, used for different purposes. A dynamic var emulates a local var that > you don't need to pass as a parameter to functions down the stack. > You don't need to make them dynamic to re-bind them. The symbol is immutably bound to the Var, so to allow re-binding of def in Clojure, all symbols are mapped to Vars instead of the value being defed, which are mutable data structures with thread safety features. The first thread safety feature is volatile access across threads of a root pointer with automatic deref. Volatile is needed to guarantee that as soon as you re-def, all access will see the new def and not the old. The second thread safety feature is thread isolated value overrides. This is useful when you don't need threads to cooperate on the same data, but you need all of them to have their own copy to work with. Each feature is optional. So if you only want thread isolation, you create a dynamic with no root. If you only want volatile root, you create a non dynamic var. If you want both, you can do that too. So yes, it is not exactly like ThreadLocal, but I guess this is the biggest thing I disagree about, it's that for every use case Var can be used for, there is nothing wrong with using it, even if a ThreadLocal could also be used. In fact, I'd frown upon someone using ThreadLocal when Var would work. Dynamic vars are required to be global in Clojure, because Clojure will > check that your symbols have been defined, but they wouldn't need to. Dynamic vars are not required to be global in Clojure, you can use (with-local-vars) if you only need them to be local. I know it is a bit complicated to refer to their instance directly, and there's no constructor for them, only indirect ones like def and (with-local-vars), but they can be used as a simple instance if you want. You can refer to the instance by using (var). You can pass this instance around. You can create local versions of them. You can even call into their java constructor if you want more control on them, at which point, you can fine tune when to push and pop on them. Now, I don't advise you use them through their Java interface, but you can if you want. This is not unimportant, and indicates that vars and ThreadLocals are meant > for different purposes. A ThreadLocal will guarantee a new, different value > for each thread. For Vars, you need to manually do that at thread creation, > and it may be tricky for threads that you don't create, if possible. > This I agree with, and that's the only use case I can think of where ThreadLocal would be better. Slight correction though, ThreadLocals won't guarantee that, but can be made too, by overriding their init method. By default they return null otherwise. Vars and ThreadLocal are meant to be tools in my opinion, and they overlap quite a bit in functionality, and where it overlaps, I see no reason to use ThreadLocal over Vars, especially when considering that in most cases, you'll put your ThreadLocal inside a Var when using it from Clojure. Regression: The reason that I brought up this discussion is that I didn't > understand why clojure.tools.logging uses a dynamic var for enforcing the > use of a specific *logger-factory*. Does anybody have an explanation for > that? > This lets you override the factory used to create the logger if you need too, within a particular thread and binding block. I'm not sure when that would come in handy for tools.logging, but I assume it sometimes is. By the way, as I revisit your initial question: Clojure encourages avoiding the use of vars as global thread-local storage, > by restricting their use to dynamic binding only. > I can see now what you were asking. The "dynamic" part is the idea that the value is pushed and poped within entry and exit of a binding block. I doubt Clojure has anything against global thread-local storage, but it wanted to provide a "dynamic" behavior that was also thread safe. I think this is what you meant by they are designed for different purpose. In that sense, yes, you're correct 100%. Clojure would have implemented it with other things if ThreadLocal did not exist. But, it would have implemented it to still behave as it does no matter what, so there's no accidental abstraction leak here from having used the ThreadLocal. Having said that, if you look at the details, you might realize that in most cases, ThreadLocal should have been designed the way Vars have, and it would have been a better construct. In my opinion, in all cases except when you want default initialization. The reason I say that is because it is not possible to re-use a thread in Java. So effectively, a Thread == A single run through a call stack. In that case, it would be way better if the ThreadLocal value for that thread would be automatically removed for you when exiting. The fact it is not can cause issues in certain scenarios. Now if using Thread Pools, the Threads are actually special Threads which contain a loop within them, so the "Call Stack" is infinite. The Threads in the pool are then assigned to pick up execution when some are available. So the Thread is not really re-used, but is simply never done being used. Once again, if you use a ThreadLocal in java, because that Thread is never done, the value for the ThreadLocal remains on it infinitely. This also causes issues most of the time, either it creates leaks, because references are not garbage collected, or it causes the next execution to not get a fresh value. Because of these details, I think Clojure did it better by restricting it to be dynamic. P.S.: Thanks for all the healthy back and forth, it forced me to really refresh and solidify my understanding of Clojure Vars. On Tuesday, 28 March 2017 14:44:58 UTC-7, Ernesto Garcia wrote: > > Right, except each thread gets its own binding. So it's not necessarily >> that you'll get the value of the last binding up the call stack. This will >> only be true if you are in the same thread also. > > > The last binding up in the call stack implies that you are in the same > thread, but I think I know what you mean, which brings up the limitation of > implementing dynamically scoped vars with ThreadLocal: It would be > reasonable to expect that the bindings of dynamic vars propagate to all > code inside the same function, even if executed by a different thread. > > >> ThreadLocal is an object, and so is a Clojure Var. >> > > A Clojure var is not only an object, it is a language construct. And when > you make it dynamic, you can bind and re-bind it with different values as > your functions are invoked within each other. This is something a > ThreadLocal can't do, and it makes the dynamic var a different kind of > beast, used for different purposes. A dynamic var emulates a local var that > you don't need to pass as a parameter to functions down the stack. > > >> In both, you'll probably want to store the instance through a globally >> accessible name, like with def in Clojure or a static in Java. You don't >> have too, but I don't see the use case for a local reference to the >> ThreadLocal or the Var. >> > > Dynamic vars are required to be global in Clojure, because Clojure will > check that your symbols have been defined, but they wouldn't need to. > > ThreadLocals don't need to be global either, you can define them in the > smaller scope where they are used. > > >> Then there's the details, like Vars have a default global scope value, >> while ThreadLocal has a default init method if you get before a set. >> > > This is not unimportant, and indicates that vars and ThreadLocals are > meant for different purposes. A ThreadLocal will guarantee a new, different > value for each thread. For Vars, you need to manually do that at thread > creation, and it may be tricky for threads that you don't create, if > possible. > > > Regression: The reason that I brought up this discussion is that I didn't > understand why clojure.tools.logging uses a dynamic var for enforcing the > use of a specific *logger-factory*. Does anybody have an explanation for > that? > > Thanks, > Ernesto > >> -- 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.