Clojure is a practical language that recognizes the occasional need to maintain a persistent reference to a changing value and provides 4 distinct mechanisms for doing so in a controlled manner - Vars, Refs, Agents and Atoms. Vars provide a mechanism to refer to a mutable storage location that can be dynamically rebound (to a new storage location) on a per-thread basis.
Using def to modify the root value of a var at other than the top level is usually an indication that you are using the var as a mutable global, and is considered bad style. Consider either using binding to provide a thread-local value for the var, or putting a ref or agent in the var and using transactions or actions for mutation.
https://clojure.org/reference/vars saysClojure is a practical language that recognizes the occasional need to maintain a persistent reference to a changing value and provides 4 distinct mechanisms for doing so in a controlled manner - Vars, Refs, Agents and Atoms. Vars provide a mechanism to refer to a mutable storage location that can be dynamically rebound (to a new storage location) on a per-thread basis.Using def to modify the root value of a var at other than the top level is usually an indication that you are using the var as a mutable global, and is considered bad style. Consider either using binding to provide a thread-local value for the var, or putting a ref or agent in the var and using transactions or actions for mutation.
Clojure encourages avoiding the use of vars as global thread-local storage, by restricting their use to dynamic binding only.
What is so bad about global thread-locals?
It can't be the fact that they are global, as refs are typically made global.
They also have a good thread-safe behavior.
Thanks,Ernesto
Dynamic scoping and Java ThreadLocals gives you equal functionality, so I'd use them equally. This is because Clojure supports thread bound dynamic scope.
(def ^:dynamic *dd*)
(binding [*dd* (atom "John")] (let [a (future (reset! *dd* "Bob")) b (future (reset! *dd* "Mike"))] @*dd*))
(def ^:dynamic *id*)
(defmacro with-id [& body] `(binding [*id* (rand-int 10000)] ~@body))
@(future (with-id (println (str "My request id is: " *id*)) "Success"))
(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*)))))
It is also possible to compile your code with aot and direct linking. In this case var lookups are replaced with a direct invocation (no var lookup).
But just to clarify, Java's ThreadLocal is an implementation of dynamic scoping. The scope is determined not by the source code, but by the runtime circumstances, in this case, the running Thread. While it's not the classic kind of dynamic scoping, it is still dynamic and not lexical.
It differs from Clojure's dynamic scope in that, Clojure's scope is determined by the runtime stack plus the current thread.
Clojure relies on ThreadLocal for the per-thread scoping implementation, and it uses its own implementation for the runtime stack based scoping.
This means that if you want to re-enter a thread after having executed in its context before, and still have access to its previously bound ThreadLocal, you can only do it with a ThreadLocal. With Clojure's dynamic Vars, I don't think this is possible, since Clojure restores the previous binding when you exit from the thread. Now, be careful with this, I've seen lots of bugs in Java due to people not realizing that the binding is permanently attached to the thread, unless explicitly unbound, especially with the use of thread pools.
So I'd consider Clojure dynamic Vars safer to use in most cases, and still would recommend you use them instead of ThreadLocal most of the time. Unless what you want to do is attach state to the thread, dynamic Vars will probably be better and easier.
Just my 2 cents.
But just to clarify, Java's ThreadLocal is an implementation of dynamic scoping.
I'm not sure if we agree on the rest, but explain it differently or not. ThreadLocal is an object, and so is a Clojure Var. When I say Clojure Var, I don't mean a variable, but an instance of the class clojure.lang.Var.
So you have two classes, ThreadLocal and Var. Each one gives you variable like behaviour. You'll need a true variable to point to the instance of each, in Clojure you can attach a local symbol to it, or a global symbol, same in java. 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.
So up to now, there's no difference between the two. Now, where the difference appears, is in their specific features, when working with the instances themselves. The ThreadLocal scope values by thread, and so does the Var. The only difference is that in Clojure, you can not set and get as you like, you must do it through a binding block, which will set on entry and unset on exit. In Java, you have to manually unset, and most probably should, as you exit.
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.
The access scope to the Var instance or the ThreadLocal instance is up to you, put it in a local or a global variable, neither prevents you from doing either. The scope of the values returned by Var and ThreadLocal are both Thread scope, with Clojure adding an extra binding scope to Var on top of it.
I think we're saying the same thing at this point. In practice, you can think of ThreadLocal as thread scoped variables, and Vars as binding block scoped variables per thread.
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.
ThreadLocal is an object, and so is a Clojure Var.
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.
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.
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
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.
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.
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?
Clojure encourages avoiding the use of vars as global thread-local storage, by restricting their use to dynamic binding only.