clojure的动态变量和绑定的实际目的是什么?

zca*_*ate 8 clojure dynamic-scope

我看了参考文献:http://clojure.org/vars#Vars%20and%20the%20Global%20Environment,http://clojuredocs.org/clojure_core/clojure.core/binding

以及clojure和^:动态Clojure动态绑定

我仍然不明白为什么总是需要,binding因为我写的每个程序都没有它们,我可以找到以常规方式编写示例的方法 - 我觉得这更容易理解.有没有使用这个的项目/编程范例的例子?

例如......在动物说话例子中,你可以得到类似的效果:

(def dog {:name "Dog" :sound "Woof"})
(def cat {:name "Cat" :sound "Meow"})

(defn speak [animal]
   (str (:name animal) " says " (:sound animal))

(println (speak dog))
(println (speak cat))
Run Code Online (Sandbox Code Playgroud)

没有宏,没有动态绑定......还是很干净.

mik*_*era 11

对它们没有严格的要求:正如你正确地观察到你可以做任何你喜欢的事情binding,如果binding不存在那么你可以使用宏和Java相对容易地重新实现它ThreadLocal.

但是,绑定有助于将动态上下文传递给函数,而无需显式传递参数.

当您编写深层嵌套的高阶函数并且不希望为调用堆栈中的每个函数添加额外参数时,它特别有用,以便将一些值传递给深入嵌入的较低级函数.

以你的榜样为基础:

(def ^:dynamic *loud-noises* false)

(defn speak [animal]
     (str (:name animal) " says " 
          (let [sound (:sound animal)]
            (if *loud-noises* (.toUpperCase sound) sound))))

(speak dog)
=> "Dog says Woof"

(binding [*loud-noises* true]
  (speak dog))
=> "Dog says WOOF"
Run Code Online (Sandbox Code Playgroud)

注意我不需要在speak函数中添加额外的参数来获得不同的行为.在这种情况下,添加一个额外的参数将是微不足道的,但想象一下,如果speak函数深埋在复杂的高阶函数中.....

尽管如此,我认为总体上最好的建议是避免动态绑定,除非你真的需要它.如果可以的话,通常最好添加显式参数:直接参数可以更容易地测试和推理函数.

  • @ToBeReplaced我认为你要强调动态绑定的危险性以及为什么应该非常仔细地考虑使用它的设计.动态绑定确实允许在您_don't_控制中间的层之间传输上下文.例如,即使在第三方功能(没有输出流参数)内使用时,clojure打印功能也允许输出流反弹.这也允许设置线程特定的行为,但我怀疑动态绑定在大多数API中通常比有益的更有害. (2认同)