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.

Reply via email to