clojure和^:dynamic [英] clojure and ^:dynamic
问题描述
我试图了解动态变量和绑定函数,所以尝试了这一点(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
很困惑,我尝试了一些更简单的代码:
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
这两段代码有什么区别?为什么第二个示例有效,而第一个无效?
提示:我只是意识到以下工作原理(仍然不完全理解为什么):
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(如您所料). ?
^:dynamic
是对Clojure编译器的一条指令,该符号(用def
定义)旨在动态反弹(使用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./p>
最后,您可能会发现一些样式点很有帮助:
- 通常将
def
和defn
放入函数中是一个坏主意,因为它们会影响封闭的名称空间.在函数中,应改用(let [foo bar] ...)
. - 当您发现自己想使用
binding
时,通常应考虑是否可以使用高阶函数来达到相同的结果.binding
在某些情况下很有用,但通常不是传递参数的好方法-从长远来看,函数组合通常更好.原因是binding
创建一个隐式上下文,该隐式上下文是执行函数所必需的,并且这可能很难测试/调试.
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=>
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
is an instruction to the Clojure compiler that a symbol (as defined with def
) is intended to be dynamically rebound (with binding
).
Example:
(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
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:
- 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屋!