与记录,协议和编译相关的令人惊讶的行为

Rag*_*gge 11 clojure

对我来说,我遇到了一个看似与clojure记录有关的略微令人惊讶的行为.

设置如下:

  1. 一个名称空间定义记录类型:

    (ns defrecordissue.arecord)
    
    (defrecord ARecord [])
    
    Run Code Online (Sandbox Code Playgroud)
  2. 另一个命名空间定义了一个协议,并将其扩展为1中定义的记录类型:

    (ns defrecordissue.aprotocol
      (:require [defrecordissue.arecord])
      (:import [defrecordissue.arecord ARecord]))
    
    (defprotocol AProtocol
      (afn [this]))
    
    (extend-protocol AProtocol
      ARecord
      (afn [this] 42))
    
    Run Code Online (Sandbox Code Playgroud)
  3. 第三个命名空间构造记录的实例并调用记录上的协议功能.

    (ns defrecordissue.aot1
      (:require [defrecordissue.aprotocol]
                [defrecordissue.arecord]))
    
    (defrecordissue.aprotocol/afn (defrecordissue.arecord/->ARecord))
    
    Run Code Online (Sandbox Code Playgroud)

编译defrecordissue.aot1命名空间时,在我使用的情况下 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 
Run Code Online (Sandbox Code Playgroud)

如果我改变3)直接构造记录类,如下:

(ns defrecordissue.aot2
  (:require [defrecordissue.aprotocol]
            [defrecordissue.arecord]))

(defrecordissue.aprotocol/afn (defrecordissue.arecord.ARecord.))
Run Code Online (Sandbox Code Playgroud)

编译成功.

我怀疑这与http://dev.clojure.org/jira/browse/CLJ-371有某种关系 ,但我不确切地知道发生了什么.

我还应该补充说,没有lein clean,第二次编译成功,因为类路径上现在可以使用记录的类.因此,我可以通过AOT编译定义记录类型的命名空间来解决这个问题.

我在GitHub上创建了一个简单的leiningen项目来说明问题,请参阅README用法:https: //github.com/ragnard/defrecordissue

为什么我会看到这种行为,以及避免它的正确方法是什么?

UPDATE

我在GitHub仓库中添加了一个新分支,更好地说明了核心问题:https: //github.com/ragnard/defrecordissue/tree/more-realistic/

无论在何处(即,在哪个命名空间中)构造记录实例,都会出现问题.

Mic*_*zyk 2

我可以用你的仓库重现问题。这是对我有用的三个解决方案:

  1. 告诉lein compile编译更多命名空间:

    lein compile defrecordissue.aprotocol defrecordissue.arecord defrecordissue.aot1
    
    Run Code Online (Sandbox Code Playgroud)
  2. :aot [defrecordissue.aprotocol defrecordissue.arecord defrecordissue.aot1]
    
    Run Code Online (Sandbox Code Playgroud)

    project.clj

  3. :aot :all
    
    Run Code Online (Sandbox Code Playgroud)

    project.clj

后两者完成了(在 2. 的情况下)以及两者的lein compile工作(在 3. 的情况下)。lein aot1lein aot1lein aot2