在其他命名空间中找不到记录类型的Clojure协议实现

时间:2013-09-24 15:11:29

标签: clojure protocols records

我们在不同名称空间中的记录和协议存在一些问题。

我们在命名空间foo.proto中有一个协议。

(ns foo.proto)

(defprotocol Proto
   (do-stuff [this x y]))

我在命名空间foo.record中有记录RecordA:

(ns foo.record
  (:require [foo.proto :as proto]))

(defrecord RecordA [bar])

;; RecordA implements the protocol:

(extend-type RecordA
    proto/Proto
    (do-stuff [this x y] (* x y (:bar this))))

只要我们在repl中,这就可以正常工作。现在,如果我们在另一方面制作一个uberjar并运行我们得到的代码:

没有实现方法::协议的做法:#'foo.proto / Proto for class

另一方面,如果我们在类型声明中实现协议,如下所示:

(defrecord RecordA [bar]
    proto/Proto
    (do-stuff [this x y] (* x y (:bar this))))

我们不再收到错误(花了一些时间才弄明白)。 另外,如果我们将Proto的声明移动到与RecordA相同的ns中,我们也不会得到错误。

我的问题:

  1. 在声明中实现以及在extend-type或extend-protocol中有什么区别?

  2. 如果我们将记录和协议声明移到同一个ns中,为什么它会起作用?

  3. 由于

3 个答案:

答案 0 :(得分:4)

问题可能在于如何将记录和协议包含在您正在使用它们的文件中。以下适用于我:

record.clj

(ns testclojure.record
  (:require [testclojure.proto :as proto]))

(defrecord RecordA [bar])

(extend-type RecordA
  proto/Proto
  (do-stuff [this x y] (* x y (:bar this))))

proto.clj

(ns testclojure.proto)

(defprotocol Proto
  (do-stuff [this x y]))

core.clj

(ns testclojure.core
  (:require [testclojure.record :refer [->RecordA]]
            [testclojure.proto :refer :all])
  (:gen-class))

(defn -main [& args]
  (-> (->RecordA 2)
      (do-stuff 2 6)
      (println)))

lein uberjar之后直接运行jar,我得到24的正确答案。

至于为什么它适用于命名空间和扩展的不同组合,defrecord创建一个Java类,而extend-type在协议的:impls集合中创建一个条目。

答案 1 :(得分:1)

我遇到了同样的问题:

我正在尝试将协议扩展到我已经与协议实现分开定义的记录,即我正在使用(扩展协议)而不是定义与记录内联的实现。 Record,协议和实现都在同一名称空间中。但是,当我试图调用它时,它抱怨没有实现。

(extend-protocol MyProtocol
  myns.message.Message
 (my-protocol-function [this] (println "got here")))


(my-protocol-function new-msg)

=> IllegalArgumentException No implementation of method: :my-protocol-function of protocol: #'myns.connector/MyProtocol found for class: myns.message.Message  clojure.core/-cache-protocol-fn (core_deftype.clj:544)

但是,如果我看看扩展器,我会在那里看到我的记录

(extenders MyProtocol)
=> (myns.message.Message)

但是(extends?)是假的

(extends? MyProtocol myns.message.Message)
=> false

如果我将协议定义内联到记录中,则所有内容都按预期工作。

答案 2 :(得分:-2)

您需要在(:import (foo.proto Proto))命名空间声明中导入协议foo.record