2024 Clojure 状态调查!中分享你的想法。

欢迎!请查看关于页面,了解更多关于这个网站的信息。

0
编译器
重定标签

我明白下面的使用方法有些不规范,但当我尝试将变量设为动态时,我看到了一些奇怪的行为差异。如果我很惊讶这种方法根本不起作用,那我就不会感到惊讶了,但我确实对它的一致性不那么惊讶。

(def v :init)

(do
  (.setDynamic #'v true)
  (binding [v :new]
    (println v))
  (.setDynamic #'v false))
=> :new ; the value is changed as expected

(let []
  (.setDynamic #'v true)
  (binding [v :new]
    (println v))
  (.setDynamic #'v false))
=> :init ; the code is the same as above except for let

在深入研究编译器后,我发现在ObjExpr.emitVarValue中,它认为在let示例中v不是动态的,因此返回了根值。

更有趣的是,如果我像使用(println @#'v)那样取消引用变量,这两个情况都返回了::new 绑定。

2 个回答

+3

已选择
 
最佳答案

这是 let 和 do 之间的区别:对于顶层 do 表达式形式,主体被提升并进行编译和逐个执行。

所以对于

(do
  (.setDynamic #'v true) ;1
  (binding [v :new] ;2
    (println v))
  (.setDynamic #'v false) ; 3
 )

表达式被分解为三个表达式

(.setDynamic #'v true) ; 1

.

(binding [v :new] ; 2
    (println v))

.

(.setDynamic #'v false) ;3

然后每个表达式依次编译和执行。这意味着执行表达式 #1 的影响在编译表达式 #2 时是可见的。

动态检查变量发生在编译时而不是运行时,所以表达式 #1 的运行意味着表达式 #2 中对 v 的引用的编译方式不同,以处理可能的重绑定变量。

对于 let 表达式,整个表达式会一次编译然后执行。所以当 #2 进行编译时,#1 还未被执行,因此变量不会被标记为动态,所以编译器不会发出处理绑定变量的代码。

+2

Var.setDynamic() 不是 Clojure 的公共 API 的一部分,所以我不确定这个问题是否相关。

是的,这差不多就是我的猜测。

我正在尝试使用 `binding` 而不是最初将变量声明为动态的(因为我只想为了测试目的更改函数,而不在实际代码中这样做),发现了 .setDynamic(),所以我想试一试。
...