11

clojure レコードに関連しているように見える、少し驚くべき動作に遭遇しました。

セットアップは次のとおりです。

  1. 1 つの名前空間がレコード タイプを定義します。

    (ns defrecordissue.arecord)
    
    (defrecord ARecord [])
    
  2. 別の名前空間はプロトコルを定義し、それを 1 で定義されたレコード タイプに拡張します。

    (ns defrecordissue.aprotocol
      (:require [defrecordissue.arecord])
      (:import [defrecordissue.arecord ARecord]))
    
    (defprotocol AProtocol
      (afn [this]))
    
    (extend-protocol AProtocol
      ARecord
      (afn [this] 42))
    
  3. 3 番目の名前空間は、レコードのインスタンスを構築し、レコードのプロトコル関数を呼び出します。

    (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) を変更して、レコード クラスを直接構築すると、次のようになります。

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

(defrecordissue.aprotocol/afn (defrecordissue.arecord.ARecord.))

コンパイルは成功します。

これは何らかの形で http://dev.clojure.org/jira/browse/CLJ-371に関連しているのではないかと疑っていますが、何が起こっているのか正確にはわかりません。

lein cleanまた、レコードのクラスがクラスパスで使用できるようになったため、コンパイルが 2 回目に成功することも追加する必要があります。したがって、レコード タイプを定義する名前空間を AOT コンパイルすることで、この問題を回避できます。

この問題を示す簡単な leiningen プロジェクトを GitHub で作成しました。使用方法については README を参照してください: https://github.com/ragnard/defrecordissue

この動作が見られるのはなぜですか? また、それを回避する正しい方法は何ですか?

アップデート

コアの問題をよりよく説明するために、GitHub リポジトリに新しいブランチを追加しました: https://github.com/ragnard/defrecordissue/tree/more-realistic/

この問題は、レコード インスタンスが構築される場所 (つまり、どのネームスペース) に関係なく発生します。

4

2 に答える 2

2

I can reproduce the problem with your repo. Here's three solutions which work for me:

  1. Tell lein compile to compile more namespaces:

    lein compile defrecordissue.aprotocol defrecordissue.arecord defrecordissue.aot1
    
  2. Put

    :aot [defrecordissue.aprotocol defrecordissue.arecord defrecordissue.aot1]
    

    in project.clj.

  3. Put

    :aot :all
    

    in project.clj.

The latter two make lein compile do the work of lein aot1 (in the case of 2.) and both lein aot1 and lein aot2 (in the case of 3.).

于 2013-04-17T22:25:36.353 に答える