为什么我可以设置!内置动态(?)Clojure vars?

Ole*_*Cat 6 clojure dynamic-variables

为什么我能这样做:

> (set! *unchecked-math* true)
true
> (set! *warn-on-reflection* false)
false
Run Code Online (Sandbox Code Playgroud)

但不能这样做:

> (def ^:dynamic *x*)
#'user/*x*
> (set! *x* 1) ;; no luck, exception!
Run Code Online (Sandbox Code Playgroud)

Kev*_*vin 5

内置动态是否可能被运行时隐式包装在绑定形式中?因为这有效,例如:

user=> (def ^:dynamic *x*)
user=> (binding [*x* false] (set! *x* true))
true
user=> 
Run Code Online (Sandbox Code Playgroud)

需要注意的一点是,文档明确指出尝试修改根绑定的错误set!,请参阅:

http://clojure.org/reference/vars

也可能异常处理内置函数,例如,如果您查看x的元数据:

user=> (meta #'*x*)
{:dynamic true, :line 1, :column 1, :file "/private/var/folders/8j/ckhdsww161xdwy3cfddjd01d25k_1q/T/form-init5379741350621280680.clj", :name *x*, :ns #object[clojure.lang.Namespace 0x6b8f00 "user"]}
Run Code Online (Sandbox Code Playgroud)

它被标记为动态,*warn-on-reflection*但未标记为动态但仍以绑定形式工作:

user=> (meta #'*warn-on-reflection*)
{:added "1.0", :ns #object[clojure.lang.Namespace 0x377fc927 "clojure.core"], :name *warn-on-reflection*, :doc "When set to true, the compiler will emit warnings when reflection is\n  needed to resolve Java method calls or field accesses.\n\n  Defaults to false."}
user=> (binding [*warn-on-reflection* true] (set! *warn-on-reflection* false))
false
user=>
Run Code Online (Sandbox Code Playgroud)

据推测这是为了向后兼容,因为在早期版本的具有耳罩(每侧的星星)的clojure变量中,按照惯例是动态的.但无论如何,这只是表明内置物的处理方式略有不同.

现在,我决定更进一步,以及grepclojure的源代码,寻找warn-on-reflection,这导致我的常量WARN_ON_REFLECTION,这导致我在这样的代码行RT.java:

https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/RT.java#L467

Var.pushThreadBindings(
        RT.mapUniqueKeys(CURRENT_NS, CURRENT_NS.deref(),
               WARN_ON_REFLECTION, WARN_ON_REFLECTION.deref()
                ,RT.UNCHECKED_MATH, RT.UNCHECKED_MATH.deref()));
Run Code Online (Sandbox Code Playgroud)

这让我相信我最初的假设是正确的,某些特殊的全局变量隐式包装在线程局部绑定中.

编辑:

正如评论中所提到的,你可以使用clojure.core/push-thread-bindings,但一定要遵循文档的建议,并pop-thread-bindings在finally块中包装try/catch/finally .在那一点上你将重新实现binding(例如(source binding)在repl上运行),这可能是doc明确警告这push-thread-bindings是一个低级函数并且binding应该是首选的原因.