我明白他们是不同的,因为一个工程设置 *compile-path*和一个不。然而,我需要帮助来解释他们为什么不同。
*compile-path*
let使用给定的绑定创建一个新范围,但是 binding... ?
let
binding
binding将值绑定到每个线程全局环境中的名称
正如您所提到的,let为所述绑定创建了一个新的范围。
let为某个值创建一个词法作用域不变的别名。 binding为某个 Var创建一个动态作用域绑定。
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