我遇到了一个对我来说有点令人惊讶的行为,似乎与 clojure 记录有关。
设置如下:
一个命名空间定义了一种记录类型:
(ns defrecordissue.arecord) (defrecord ARecord [])
另一个命名空间定义了一个协议,并将其扩展为 1 中定义的记录类型:
(ns defrecordissue.aprotocol (:require [defrecordissue.arecord]) (:import [defrecordissue.arecord ARecord])) (defprotocol AProtocol (afn [this])) (extend-protocol AProtocol ARecord (afn [this] 42))
第三个命名空间构造记录的一个实例并调用记录上的协议函数。
(ns defrecordissue.aot1 (:require [defrecordissue.aprotocol] [defrecordissue.arecord])) (defrecordissue.aprotocol/afn (defrecordissue.arecord/->ARecord))
编译 defrecordissue.aot1 命名空间时,在我的情况下 using
lein compile defrecordissue.aot1
,编译失败并出现以下异常:
Exception in thread "main" java.lang.IllegalArgumentException: No implementation of method: :afn of protocol: #'defrecordissue.aprotocol/AProtocol found for class: defrecordissue.arecord.ARecord, compiling:(aot1.clj:5:1)
at clojure.lang.Compiler$InvokeExpr.eval(Compiler.java:3463)
at clojure.lang.Compiler.compile1(Compiler.java:7153)
at clojure.lang.Compiler.compile(Compiler.java:7219)
at clojure.lang.RT.compile(RT.java:398)
at clojure.lang.RT.load(RT.java:438)
at clojure.lang.RT.load(RT.java:411)
at clojure.core$load$fn__5018.invoke(core.clj:5530)
at clojure.core$load.doInvoke(core.clj:5529)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at clojure.core$load_one.invoke(core.clj:5336)
at clojure.core$compile$fn__5023.invoke(core.clj:5541)
at clojure.core$compile.invoke(core.clj:5540)
at user$eval7.invoke(NO_SOURCE_FILE:1)
at clojure.lang.Compiler.eval(Compiler.java:6619)
at clojure.lang.Compiler.eval(Compiler.java:6609)
at clojure.lang.Compiler.eval(Compiler.java:6582)
at clojure.core$eval.invoke(core.clj:2852)
at clojure.main$eval_opt.invoke(main.clj:308)
at clojure.main$initialize.invoke(main.clj:327)
at clojure.main$null_opt.invoke(main.clj:362)
at clojure.main$main.doInvoke(main.clj:440)
at clojure.lang.RestFn.invoke(RestFn.java:421)
at clojure.lang.Var.invoke(Var.java:419)
at clojure.lang.AFn.applyToHelper(AFn.java:163)
at clojure.lang.Var.applyTo(Var.java:532)
at clojure.main.main(main.java:37)
Caused by: java.lang.IllegalArgumentException: No implementation of method: :afn of protocol: #'defrecordissue.aprotocol/AProtocol found for class: defrecordissue.arecord.ARecord
at clojure.core$_cache_protocol_fn.invoke(core_deftype.clj:541)
at defrecordissue.aprotocol$fn__40$G__35__45.invoke(aprotocol.clj:5)
at clojure.lang.AFn.applyToHelper(AFn.java:161)
at clojure.lang.AFn.applyTo(AFn.java:151)
at clojure.lang.Compiler$InvokeExpr.eval(Compiler.java:3458)
... 25 more
如果我更改 3) 直接构造记录类,如下所示:
(ns defrecordissue.aot2
(:require [defrecordissue.aprotocol]
[defrecordissue.arecord]))
(defrecordissue.aprotocol/afn (defrecordissue.arecord.ARecord.))
编译成功。
我怀疑这与 http://dev.clojure.org/jira/browse/CLJ-371有某种关系,但我不明白到底发生了什么。
我还应该补充一点,没有lein clean
, 编译第二次成功,因为记录的类现在在类路径上可用。因此,我可以通过 AOT 编译定义记录类型的命名空间来解决这个问题。
我在 GitHub 上创建了一个简单的 leiningen 项目来说明问题,请参阅 README 以了解用法: https ://github.com/ragnard/defrecordissue
为什么我会看到这种行为,避免这种行为的正确方法是什么?
更新
我在 GitHub 存储库中添加了一个新分支,以更好地说明核心问题: https ://github.com/ragnard/defrecordissue/tree/more-realistic/
无论在何处(即在哪个命名空间中)构造记录实例,都会出现问题。