持久队列对象在编译器中没有遵循正确的评估路径。
最简单的例子
user=> (def q (conj clojure.lang.PersistentQueue/EMPTY 1 2 3))
'user/q
user=> q
user=> (eval q)
编译器异常 java.lang.ClassCastException: clojure.lang.PersistentQueue 不能转换为 java.util.List,编译:(NO_SOURCE_PATH:4:1)
当内嵌一个持久队列时,你会得到相同的异常
user=> (eval `(fn (link: ) ~q))
编译器异常 java.lang.ClassCastException: clojure.lang.PersistentQueue 不能转换为 java.util.List,编译:(NO_SOURCE_PATH:2:1)
而不是预期的
编译器异常 java.lang.RuntimeException: 无法嵌入不可读对象到代码中: #,编译:(NO_SOURCE_PATH:3:1)
由于持久队列实现了 IPersistentCollection 和 IPersistentList 接口,并且在编译器中没有明确列出,它陷入了与列表相同的编译路径。异常来自 FnExpr 发射路径中的 emitConstants 部分的 emitValue 调用。PersistentQueue 没有实现 java.util.List 接口,因此 emitListAsObjectArray (Compiler.java:4479) 中的转换抛出了异常。然而,实现 List 并不会解决这个问题,而只是掩盖它,导致所有评估的 PersistentQueue 都会被编译为 PersistentList。
第一个问题是通过向 Compiler.eval() (Compiler.java:6695-8) 中的 IPersistentCollection 分支添加 && !(form instanceof PersistentQueue)
来解决的,允许 PersistentQueue 跳到 analyze (Compiler.java:6459) 中的 ConstantExpr 情境。嵌入式情况是通过对 ObjExpr 的 emitValue (Compiler.java:4639) 中的 IPersistentList 分支添加 && !(value instanceof PersistentQueue)
来解决的。
此错误还阻止了为 PersistentQueue 定义数据读取器,因为读取对象在传递给编译器时抛出异常。
附带的补丁包括了上述提到的两个更改,并对每个情况进行了测试,以展示此错误。
Clojure 开发线程:https://groups.google.com/forum/#!topic/clojure-dev/LDUQfqjFg9w