Difference between using “def” to update a var and “alter-var-root”

前端 未结 3 936
梦毁少年i
梦毁少年i 2020-12-15 17:12

What\'s the difference between using \"def\" to update a var and using \"alter-var-root\"? e.g.

(def x 3)
(def x (inc x))

vs



        
3条回答
  •  借酒劲吻你
    2020-12-15 17:37

    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)
    # 
    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 ...

提交回复
热议问题