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中,vlet示例中不是动态的,因此返回了根值。

有趣的是,如果我使用类似(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

每个表达式随后依次编译执行。这意味着在编译表达式#2时,表达式#1的执行效果是可见的。

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

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

+2

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

是的,我就是这样猜测的。

我在尝试使用`binding`时没有最初将变量声明为动态的(因为我只想在测试用例中更改一个函数,而不是在实际代码中),遇到了.setDynamic(),所以我想尝试一下。
...