Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/clojure/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Clojure 与记录、协议和汇编相关的惊人行为_Clojure - Fatal编程技术网

Clojure 与记录、协议和汇编相关的惊人行为

Clojure 与记录、协议和汇编相关的惊人行为,clojure,Clojure,在我看来,我遇到了一个稍微令人惊讶的行为 与clojure记录相关 设置如下所示: 一个命名空间定义记录类型: (ns defrecordissue.arecord) (defrecord ARecord []) 另一个名称空间定义了一个协议,并将其扩展到记录 1中定义的类型: (ns defrecordissue.aprotocol (:require [defrecordissue.arecord]) (:import [defrecordissue.arecord AReco

在我看来,我遇到了一个稍微令人惊讶的行为 与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命名空间时,在我的示例中,使用
    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)更改为直接构造record类,如下所示:

    (ns defrecordissue.aot2
      (:require [defrecordissue.aprotocol]
                [defrecordissue.arecord]))
    
    (defrecordissue.aprotocol/afn (defrecordissue.arecord.ARecord.))
    
    编译成功

    我怀疑这与 ,但我不明白 事实就是如此

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

    我在GitHub上创建了一个简单的leiningen项目,该项目演示了 问题,有关用法,请参见自述:

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

    更新

    我在GitHub回购协议中添加了一个新分支,更好地说明了核心问题:

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

    我可以用您的回购重现问题。以下是三种适合我的解决方案:

  • 告诉
    lein compile
    编译更多名称空间:

    lein compile defrecordissue.aprotocol defrecordissue.arecord defrecordissue.aot1
    
  • :aot [defrecordissue.aprotocol defrecordissue.arecord defrecordissue.aot1]
    
    :aot :all
    
    project.clj

  • :aot [defrecordissue.aprotocol defrecordissue.arecord defrecordissue.aot1]
    
    :aot :all
    
    project.clj


  • 后两种方法使
    lein编译
    完成
    lein aot1
    (在2的情况下)和
    lein aot1
    lein aot2
    (在3的情况下)的工作。

    。这就是我想到的,而且似乎有效:

    (defmacro with-datatype
      [datatype & body]
      (let [last-dot (.lastIndexOf ^String (str datatype) ".")
            ns (-> datatype
                   str
                   (subs 0 last-dot)
                   symbol)]
         `(do
            (require (quote ~ns))
            (import ~datatype)
            ~@body)))
    
    (defmacro extend-type*
      [datatype & extensions]
      `(with-datatype ~datatype
         (extend-type ~datatype
           ~@extensions)))
    
    (defmacro extend-protocol*
      [protocol & specs]
      (let [splitter (let [l (atom nil)]
                       #(if (symbol? %) (reset! l %) @l))
            specs (partition-by splitter specs)]
        `(do
           ~@(for [[datatype & extensions] specs]
               `(extend-type* ~datatype ~protocol ~@extensions)))))
    
    然后只需将第二个代码块更改为:

    (ns defrecordissue.aprotocol)
    
    (defprotocol AProtocol
      (afn [this]))
    
    (extend-protocol* AProtocol
      defrecordissue.arecord.ARecord
      (afn [this] 42))
    

    也许不是最干净的解决方案,但您不必再对库进行AOT操作。

    谢谢您的回答。我知道我可以通过AOT编译
    defrecordissue.arecord
    名称空间来绕过这个问题。然而,在问题产生的情况下,这并不完全令人满意。在这种情况下,
    defrecordissue.arecord
    defrecordissue.aprotocol
    都是库的一部分,而
    defrecordissue.aot1
    只是该库的使用者,不知道记录类型(甚至不知道协议)。它不直接实例化记录类型,如本人为示例所示。如果可能的话,我希望提供不带任何AOT编译类的库。