【问题标题】:what is the practical purpose of clojure's dynamic vars and binding?clojure 的动态变量和绑定的实际目的是什么?
【发布时间】:2012-10-07 14:31:51
【问题描述】:

我查看了参考资料:http://clojure.org/vars#Vars%20and%20the%20Global%20Environmenthttp://clojuredocs.org/clojure_core/clojure.core/binding

还有clojure and ^:dynamicClojure Dynamic Binding

我仍然不明白为什么需要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))

没有宏,没有动态绑定...仍然很干净。

【问题讨论】:

标签: clojure dynamic-scope


【解决方案1】:

严格来说不需要它们:正如您正确地观察到的那样,您可以在没有binding 的情况下做任何您想做的事情,而且实际上如果binding 不存在,那么您可以重新-使用宏和 Java 的 ThreadLocals 相对容易地实现它。

然而,绑定作为一种将动态上下文传递给函数而无需显式传递参数的方式很有用。

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

以您的示例为基础:

(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"

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

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

【讨论】:

  • 为什么您更喜欢绑定来为深度嵌套函数中的每个函数添加额外的参数?如果我的函数调用(speak dog)(close-door),我怎么知道*loud-noises*对应的是speak而不是close-door
  • @ToBeReplaced 我认为您准确地强调了动态绑定的危险以及为什么应该非常仔细地考虑使用它的设计。动态绑定确实允许在您控制中间的层之间传输上下文。例如,即使在第三方函数(没有输出流参数)内部使用时,clojure 打印函数也允许重新绑定输出流。这也允许设置线程特定的行为,但我怀疑动态绑定在大多数 API 中弊大于利。
【解决方案2】:

只是跟进上面 mikera 的示例.. 我可以理解为什么你会用一种表达力较低的语言来做,但是因为 clojure 的表达力很强,我宁愿重写它...... loud-noise 函数可以稍微改变一下,再次通过添加额外的参数来达到相同的效果...

(defn speak [animal & opts]
  (let [sound (:sound animal)
        sound (if (some #(= % :louder) opts)
                 (.toUpperCase sound) sound)]
    (str (:name animal) " says " sound)))


> (speak dog)
;;=> "Dog says Woof"
> (speak dog :louder)
;;=> "Dog says WOOF"

如果您无法更改原始代码,绑定只是一种破解快速而肮脏的解决方案的方法吗?

【讨论】:

  • 您并不总是能够添加额外的参数 - 例如,如果您将函数传递到库代码中怎么办?或者如果这些函数是通用的 HOF,您不想被仅在一种特殊情况下使用的参数污染?
猜你喜欢
  • 2010-09-20
  • 1970-01-01
  • 2011-04-01
  • 2013-08-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多