Clojure 的让与约束

我明白他们是不同的,因为一个工程设置 *compile-path*和一个不。然而,我需要帮助来解释他们为什么不同。

let使用给定的绑定创建一个新范围,但是 binding... ?

20283 次浏览

binding将值绑定到每个线程全局环境中的名称

正如您所提到的,let为所述绑定创建了一个新的范围。

let为某个值创建一个词法作用域不变的别名。 binding为某个 Var创建一个动态作用域绑定。

动态绑定意味着 binding表单中的代码以及该代码调用的任何代码(即使不在本地词法范围内)将看到新的绑定。

给出:

user> (def ^:dynamic x 0)
#'user/x

binding实际上为 Var创建了一个动态绑定,但是 let只用一个局部别名来隐藏 var:

user> (binding [x 1] (var-get #'x))
1
user> (let [x 1] (var-get #'x))
0

binding可以使用限定名(因为它在 Var上运行) ,而 let不能:

user> (binding [user/x 1] (var-get #'x))
1
user> (let [user/x 1] (var-get #'x))
; Evaluation aborted.
;; Can't let qualified name: user/x

引入 let的绑定是不可变的。引入 binding的绑定是线程局部可变的:

user> (binding [x 1] (set! x 2) x)
2
user> (let [x 1] (set! x 2) x)
; Evaluation aborted.
;; Invalid assignment target

词法与动态绑定:

user> (defn foo [] (println x))
#'user/foo
user> (binding [x 1] (foo))
1
nil
user> (let [x 1] (foo))
0
nil

参见 瓦尔斯

Let 与 bind 的另一个语法差异是:

对于绑定,在将所有初始值绑定到 var 之前,将对所有初始值进行计算。这不同于 let,在 let 中,可以在后续定义中使用前一个“ alias”的值。

user=>(let [x 1 y (+ x 1)] (println y))
2
nil


user=>(def y 0)
user=>(binding [x 1 y (+ x 1)] (println y))
1
nil