clojure和^:动态

clojure和^:动态,clojure,dynamic-scope,Clojure,Dynamic Scope,我试图理解动态变量和绑定函数,因此我尝试了以下方法(clojure 1.3): 困惑的是,我尝试了这个稍微简单一些的代码: 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 这两段代码之间的区别是什么?为什么第二个示例有效,但第一个不起作用

我试图理解动态变量和绑定函数,因此我尝试了以下方法(clojure 1.3):

困惑的是,我尝试了这个稍微简单一些的代码:

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
这两段代码之间的区别是什么?为什么第二个示例有效,但第一个不起作用

提示:我刚刚意识到以下方法有效(仍然不完全理解原因):

当我在Clojure 1.4中运行您的第一个示例时,我得到了3(正如您所期望的那样)。。。。你用新的回复试过这个吗

^:dynamic
是向Clojure编译器发出的一条指令,指示一个符号(定义为
def
)要动态反弹(绑定为

例如:

(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

最后,您可能会发现一些有帮助的风格点:

  • 通常认为将
    def
    defn
    放在函数中是不好的,因为它们会影响封闭的名称空间。在函数中,您应该使用
    (让[foo-bar]…)
  • <> LI>当你发现自己想使用<代码>绑定< /代码>时,通常应该考虑是否可以使用更高阶函数来实现相同的结果。代码>绑定
在某些上下文中很有用,但它通常不是传递参数的好方法-从长远来看,函数组合通常更好。原因是
绑定
创建了执行函数所需的隐式上下文,这可能很难测试/调试
我理解绑定的优点/缺点。我还意识到第一个代码示例是不同寻常的clojure代码。我不明白的是为什么它不工作(使用1.3,fresh repl)。我很难看到您何时需要绑定!从功能的角度看,这是一种令人憎恶的方式。“我错过了什么?”亨德卡贡-也许自己就应该有一个这样的问题。但我发现它是在调试/使用REPL时传递上下文的一种额外方式,非常有用-如果您以纯功能性的方式执行此操作,那么您将需要通过一个(可能很长的)调用图遍历新参数。我在1.4中尝试了此方法,它工作正常(如预期),并在1.3中重试,但失败了。我只能断定1.3中有一些bug已经修复。@Kevin
^:dynamic
在1.3中还不是语言的一部分,所以不,不是bug,只是还没有存在;在那个时候,动态变量被命名为
*杠铃*
,而不仅仅是作为惯例。
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=> 
(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