这是我从电子邮件列表上https://groups.google.com/forum/#!topic/clojure/Ozt5HQyM36I上的帖子。基于那里的回复,我不确定这是应该记录为增强功能还是错误。请改为适当的标签。
我在使用ns-unmap时发现AOT编译的Clojure中似乎有一个错误;这个根本原因可能影响到其他重新定义Vars的代码。以下是我的简化示例:
(ns unmap-test.core)
(def a :test-1)
(ns-unmap 'unmap-test.core 'a)
(def a :test-2)
结果是,当这个命名空间加载时,a无法解析。当我查看编译的字节码时,
出现以下操作:
- 调用 RT.varwith 'unmap-test.core 和 'a 返回一个 Var,该 Var 绑定到一个常量。
在此调用期间将此 Var 添加到命名空间的映射中。
- 与1相同。
- 将第一个步骤中的 Var 绑定到 :test-1。
- 调用ns-unmap。
- 将第二个步骤中的 Var 绑定到 :test-2。
免责声明:这是我第一次直接查看字节码,我可能遗漏了什么。
这里的基本问题是 Var 可从加载方法访问,但当执行第五步时,Var 就无法从命名空间映射中访问了。
因此,Var 的根设置为 :test-2,但该 Var 未从命名空间映射。
在没有 AOT 编译的情况下,这也可以工作,以及当我使用
(ns unmap-test.core)
(def a :test-1)
(ns-unmap 'unmap-test.core 'a)
(intern 'unmap-test.core 'a :test-2)
时。我意识到在 Clojure 中创建定义、取消映射并重新创建它们是一种不良的实践。
我们有这样一个怪异的情况:我们需要在同一命名空间中有一个接口和一个同名 Var。简单
地对definterface 和def 做出definterface 和然后定义将导致编译失败
user=> (definterface abc)
user.abc
user=> (def abc 1)
CompilerException java.lang.RuntimeException: 期望 Var,但 abc 被映射到接口 user.abc,编译:(private/var/folders/3m/tvc28b5d7p50v5_8q5ntj0pmflbdh9/T/form-init4734176956271823921.clj:1:1)
不深入细节,这是一个基本上的漏洞,允许我们对内部框架代码进行重构
而不立即更改大量下游消费者代码。我们消除了宏展开期间的使用接口
但它仍然需要存在于类路径中,以便由下游命名空间导入。
有几种其他方法可以实现这一点,因此这对我们来说不是一个大问题,但我认为这个问题值得提出。
这只是我尝试的第一件事,对此不起作用,我感到惊讶。
请注意,我在我的示例项目中使用了Clojure的1.8.0 RC4版本,但我在1.7.0版本上也观察到了相同的行为。
相关链接
- 初始化类的load方法的字节码: https://gist.github.com/WilliamParker/d8ef4c0555a30135f35a
- init0方法的字节码: https://gist.github.com/WilliamParker/dc606ad086670915efd9
- 初始化类的反汇编Java代码。请注意,据我所知,这与字节码不完全一致,
但与字节码相比,它是一种更快地了解发生了什么的方法。
https://gist.github.com/WilliamParker/4cc47939f613d4595d94
- 包含上述代码的简单项目: https://github.com/WilliamParker/unmap-test
请注意,如果您不使用AOT编译尝试,则应删除包含任何先前编译类的目标文件夹。