On Wednesday, February 8, 2017 at 8:34:06 AM UTC-6, Ernesto Garcia wrote: > > https://clojure.org/reference/vars says > > 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. >> > > https://clojure.org/reference/special_forms says > > 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. > > This is saying that if you are doing something like this:
(def my-value 10) (defn inc-value [] (def my-value (inc my-value))) Then you are not using vars as intended. Here you have an identity (my-value) that refers to a var, that holds the value 10. The inc-value function is going to locate the existing var and reset its root value. This is not following the Clojure update model for identities which expects that you update an identity by providing a function that can be applied to the old value to produce a new value. If you really wanted to do this, you could do so by using alter-var-root explicitly: (defn inc-value [] (alter-var-root #'my-value inc)) However, the point here is that keeping your state in a global var and bashing on it is generally bad style (particularly using def). Using a dynamic var and bindings for thread-local values effectively switches to a model where you are not sharing state across threads. Probably if you're doing the code above, that's not really satisfying your needs. Dynamic bindings are most commonly used to create per-thread ambient context that can be passed down the call stack without explicitly passing it as arguments. This should be used sparingly as you are creating implicit state - any code that calls into it (like tests) has to be aware of the ambient state and properly set it up. Alternately, you can make the value held by your var a stateful thing (atom or ref) itself and thus avoid altering the global var ever: (def my-value (atom 0)) (defn inc-value [] (swap! my-value inc)) This is strongly preferred over the var-bashing in the first example. > Clojure encourages avoiding the use of vars as global thread-local > storage, by restricting their use to dynamic binding only. > I don't think that's really the message here. It's saying that while vars are global stateful constructs, it is not idiomatic to modify them unless they are dynamic and you are doing so in a thread-local context. If you want a global thread-local state, then do that. If you want a global stateful construct, it's better to use an atom or ref held in a non-dynamic var. Generally my advice would be to avoid global stateful constructs as much as possible and not do any of these. :) Global state has all the same problems of implicit state I mentioned above with bindings. > What is so bad about global thread-locals? > Nothing - dynamic vars are effectively global thread locals (in combination with a global root value). > It can't be the fact that they are global, as refs are typically made > global. > As I said above, atoms/refs can and often are created as part of your application state and passed around. Generally, I think you should strongly prefer this over creating a (global) var holding a ref. > They also have a good thread-safe behavior. > Agreed. :) > > 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.