请在2024年Clojure状况调查中分享您的看法!

欢迎!请参阅关于页面获取更多关于如何使用本页面的信息。

0
命名空间和var
编辑

我们知道当我们将符号发送到REPL时,Clojure会将符号解析为var,并获取当前var绑定的值。我们还知道,当一个var对象被namespace内部引用时,它是稳定的,因此我们可以动态地修改程序的运行行为(通过重新定义var的绑定)。
但这里有一个我不理解的现象。
REPL处理过程的截图

user=> (ns ns-a)
ns-a=> (def a 42)
ns-a=> a
;;42
ns-a=> (ns ns-b)
ns-b=> (refer 'ns-a)
ns-b=> a
;;42
ns-b=> (var a)
#'ns-a/a
ns-b=> (in-ns 'ns-a)
ns-a=> a
;;42
ns-a=> (def a 99)
ns-a=> a
;;99
ns-a=> (in-ns 'ns-b)
ns-b=> a
;;99
;;nothing special so far
ns-b=> (in-ns 'ns-a)
ns-a=> (ns-publics *ns*)
;;{a #'ns-a/a}
;;see, it's interned
ns-a=> (ns-unmap *ns* 'a)
;;lets unmap the (var a) from ns-a
ns-a=> (ns-publics *ns*)
;;{}
ns-a=> a
;;Syntax error compiling at (REPL:0:0).
;;Unable to resolve symbol: a in this context
ns-a=> (var a)
;;Syntax error compiling var at (REPL:1:1).
;;Unable to resolve var: a in this context
;;(var a) is nowhere.
ns-a=> (in-ns 'ns-b)
ns-b=> a
;;99
;;the referred symbol a still evaluates to 99.
ns-b=> (var a)
;;#'ns-a/a
;;yeah, (var a) is still somewhere.
ns-b=> (deref (var a))
;;#object[clojure.lang.Var$Unbound 0x4a68135e "Unbound: #'ns-a/a"]
;;What? (var a) is being unbound. Is this a new (var a)?
ns-b=> a
;;99
;;it is not resolved from (var a)?
ns-b=> (in-ns 'ns-a)
ns-a=> a
;;#object[clojure.lang.Var$Unbound 0x4a68135e "Unbound: #'ns-a/a"]
;;What? it comes back?
ns-a=> (def a 53)
;;Lets redefine (var a) to bind 53
ns-a=> a
;;53
ns-a=> (in-ns 'ns-b)
;;Lets switch back and check.
ns-b=> a
;;99
;;not changed
ns-b=> (deref (var a))
;;53
;;so I'm sure 99 it is not resolved from (var a).

第一个问题:值99在哪里?它曾由#'a解析,但似乎在执行#'a后变成了词法绑定。是吗?
第二个问题:在另一个命名空间(该命名空间引用了#'a)执行(var a)后,为什么#'a会以未绑定状态回收到内部?这似乎创建了一个新的var来满足状态。

1 答案

+2

回到一些概念可能会有所帮助。命名空间包含一组映射到var的名字(symbols)。var是值的盒子。def将名字映射到当前命名空间中的var。refer将当前命名空间中的名字映射到不同命名空间中的var。

Q1: ns-unmap从符号到变量的映射移除ns-a中的映射,但不会销毁变量。变量仍然存在(并被ns-b中的引用所引用)。

Q2: var是一个特殊形式(在编译器中实现),可能做着与您预期不同的事情。var将会解析为一个变量对象(这里,ns-b中的符号a在编译时解析为变量#'ns-a/a(不是变量对象,而是这个标记!!)。然后,通过在ns-a中查找变量a来对其进行评估(实际上不存在)。我认为这可能是混淆的真正原因(这仅由于var而发生)。

...