Clojure中的^:dynamic是什么?

6
我已经搜索了“clojure dynamic”和“clojure dynamic scope”并阅读了10多篇文章,但我仍然不清楚^:dyanmic的作用。我认为这篇文章可能回答了我的问题,但代码示例似乎“丢失”了,所以我甚至不确定它是否指的是我困惑的同一件事情。
我正在尝试修复clj-http项目中的问题,但首先我必须理解代码。它定义了如下函数:
(defn ^:dynamic parse-html
  "Resolve and apply crouton's HTML parsing."
  [& args]
  {:pre [crouton-enabled?]}
  (apply (ns-resolve (symbol "crouton.html") (symbol "parse")) args))

但是我不明白^:dynamic的含义和作用。有人能够以简单易懂的方式解释一下吗?
1个回答

8

这是将函数定义为动态作用域。

换句话说,这允许某人在给定函数调用中重新绑定parse-html,并且新的绑定仅适用于从该特定调用调用的函数。

如果parse-html不是动态作用域,则重新绑定它将导致任何使用parse-html的代码都可以看到新绑定,而不仅仅是由进行重新绑定的函数调用激活的代码。

动态作用域对于全局作用域变量的替代非常有用。函数可以说“let current_numeric_base = 16; call other functions;”,那么其他函数都将以十六进制打印。然后当它们返回,并且设置基础的函数返回时,基础将返回到任何它所在的状态。 http://c2.com/cgi/wiki?DynamicScoping


正如下面的评论所指出的那样,在Clojure中,您实际上无法重新绑定非动态作用域的变量。如果可以的话,更新词法作用域的变量将影响所有执行的代码,即使它正在运行与重新绑定发生的调用堆栈不同的地方。
因此,也许一些伪代码将清楚地说明动态作用域和词法作用域之间的区别。
使用动态作用域变量的示例:
(def ^:dynamic a 0)

(defn some-func [x] (+ x 1))

; re-binds a to 1 for everything in the callstack from the (binding)
; call and down
(binding [a 1] 
   (print (some-func a)))

 ; a was only re-bound for anything that was called from
 ; within binding (above) so at this point a is bound to 0.
(print (some-func a))

将会打印: 2 1

词法作用域变量的示例:

(def a 0)

(defn some-func [x] (+ x 1))

; re-binds a to 1 for everyone, not just things that 
; are in the callstack created at this line
(set-var [a 1]  ; set-var is a made up function that can re-bind lexically scoped variables
   (print (some-func a)))

; a was lexically scoped so changing it changed
; it globally and not just for the callstack that
; contained the set-var.
(print (some-func a))

将会打印: 2 2


1
很棒,这是一个相当不错的答案,但如果您使用^:dynamic并演示了您所解释的区别,我会更好地理解它。您介意这样做吗? - Daniel Kaplan
1
如果 parse-html 不是动态作用域的,那么重新绑定它将是非法的。 - amalloy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接