clojure 和 ^:dynamic [英] clojure and ^:dynamic
问题描述
我试图理解动态变量和绑定函数,所以我尝试了这个(clojure 1.3):
I tried to understand dynamic variables and binding function so I tried this (clojure 1.3):
user=> (defn f []
(def ^:dynamic x 5)
(defn g [] (println x))
(defn h [] (binding [x 3] (g)))
(h))
#'user/f
user=> (f)
5
nil
困惑,我尝试了这个稍微简单的代码:
Confused, I tried this somewhat simpler code:
user=> (def ^:dynamic y 5)
#'user/y
user=> (defn g [] (println y))
#'user/g
user=> (defn h [] (binding [y 3] (g)))
#'user/h
user=> (h)
3
nil
这两段代码有什么区别?为什么第二个例子可以,而第一个不行?
What is the difference between the two pieces of code? Why does the second example work but the first does not?
提示:我刚刚意识到以下有效(仍然不完全理解为什么):
Hint: I just realized that the following works (still don't fully understand why):
user=> (def ^:dynamic y 5)
#'user/y
user=> (defn f [] (defn g [] (println y)) (defn h [] (binding [y 3] (g))) (h))
#'user/f
user=> (f)
3
nil
user=>
推荐答案
当我在 Clojure 1.4 中运行您的第一个示例时,我得到了 3 个结果(正如您所料)....?
I get 3 as a result (as you would expect) when I run your first example in Clojure 1.4.... have you tried this with a fresh REPL?
^:dynamic
是对 Clojure 编译器的一条指令,即一个符号(由 def
定义)旨在动态重新绑定(使用 binding
代码>).
^:dynamic
is an instruction to the Clojure compiler that a symbol (as defined with def
) is intended to be dynamically rebound (with binding
).
示例:
(def foo 1)
(binding [foo 2] foo)
=> IllegalStateException Can't dynamically bind non-dynamic var: ...
(def ^:dynamic bar 10)
(binding [bar 20] bar) ;; dynamically bind bar within the scope of the binding
=> 20
bar ;; check underlying value of bar (outside the binding)
=> 10
请注意,binding
在调用线程中具有动态范围 - 在绑定中调用的任何函数都将看到 bar
(20) 的修改值,但任何其他线程将仍然看到 10 不变的根值.
Note that binding
has dynamic scope within the calling thread - any functions called within the binding will see the modified value of bar
(20), but any other threads will still see the unchanged root value of 10.
最后几个可能对您有帮助的风格要点:
Finally a couple of style points that you may find helpful:
- 将
def
和defn
放在函数中通常被认为是个坏主意,因为它们会影响封闭的命名空间.在函数中,您应该使用(let [foo bar] ...)
代替. - 当您发现自己想要使用
binding
时,您通常应该考虑是否可以使用高阶函数来实现相同的结果.binding
在某些情况下很有用,但它通常不是传递参数的好方法 - 从长远来看,函数组合通常更好.这样做的原因是binding
创建了执行函数所需的隐式上下文,这可能难以测试/调试.
- It's generally considered bad idea to put
def
anddefn
within functions as they affect the enclosing namespace. Within functions you should use(let [foo bar] ...)
instead. - When you find yourself wanting to use
binding
you should normally consider whether you can achieve the same result using higher order functions instead.binding
is useful in some contexts but it is not in general a good way to pass parameters around - function composition is usually better in the long run. The reason for this is thatbinding
creates an implicit context that is required for the execution of your function and this can be difficult to test/debug.
这篇关于clojure 和 ^:dynamic的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!