I find alter-var-root very rarely comes up in idiomatic Clojure code; not that there is anything wrong with it, it's just intended for corner cases. If you find yourself using it to build loops and such it's a sign something needs a different approach. I mostly see it in initialization routines for setting access credentials or loggers and such.
alter-var-root
uses a function to mechanically change the value of a var while def
just sets it to a new value. In your example they are equivalent.
hello.exp> (def foo 4)
#'hello.exp/foo
hello.exp> (alter-var-root #'foo inc)
5
hello.exp> foo
5
alter-var-root
is also unwilling to create a new var:
hello.exp> (alter-var-root #'foo1 inc)
CompilerException java.lang.RuntimeException: Unable to resolve var: foo1 in this context, compiling:(NO_SOURCE_PATH:1)
alter-var-root
can work on other namespaces as well:
hello.exp> (in-ns 'user)
#<Namespace user>
user> (alter-var-root #'hello.exp/foo inc)
6
user> (def hello.exp/foo 4)
CompilerException java.lang.RuntimeException: Can't create defs outside of current ns, compiling:(NO_SOURCE_PATH:1)
user>
This last use case is the only one I have ever needed in practice. For instance forcing clojure.logging
to use the correct slf4j logger as an example from the Pallet project:
(defn force-slf4j
"The repl task brings in commons-logging, which messes up our logging
configuration. This is an attempt to restore sanity."
[]
(binding [*ns* (the-ns 'clojure.tools.logging.slf4j)]
(alter-var-root
#'clojure.tools.logging/*logger-factory*
(constantly (clojure.tools.logging.slf4j/load-factory)))))
Which is just using alter-var-root
to reset a var in another namespace regardless of its content on initialization. I suppose it's a bit of a hack ...
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…