We are having a few issues with records and protocols in different namespaces.
We have a protocol in namespace foo.proto.
(ns foo.proto)
(defprotocol Proto
(do-stuff [this x y]))
I have a record RecordA in namespace foo.record:
(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))))
This works fine as long as we are in the repl. Now if we on the otherhand make an uberjar and run the code we get:
No implementation of method: :do-stuff of protocol: #'foo.proto/Proto found for class
If we on the other hand implement the protocol in the type declaration like so:
(defrecord RecordA [bar]
proto/Proto
(do-stuff [this x y] (* x y (:bar this))))
We no longer get the error (which took some time to figure out). Also if we move the declaration of Proto into the same ns as RecordA we do not get the error either.
My questions:
What is the difference between implementing in the declaration and in extend-type or extend-protocol?
Why would it work if we move the Record and Protocol declarations into the same ns?
Thanks
The issue may be in how you are including the record and protocol within the file you are using them. The following works for me:
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)))
After lein uberjar
and running the jar directly, I get the correct answer of 24.
As for why it works with different combinations of namespaces and extending, defrecord
creates a Java class while extend-type
creates an entry in the :impls
collection of the protocol.
I'm running into what looks like the same issue:
I'm trying to extend a protocol to a Record I have defined separate from the Protocol implementation, i.e. I'm using (extend-protocol) rather than defining the implementation inline with the record. Both the Record, the protocol and the implementation are in the same namespace. However when I attempt to call it, it complains that no implementation exists.
(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)
However if I look at extenders, I see my record there
(extenders MyProtocol)
=> (myns.message.Message)
However (extends?) is false
(extends? MyProtocol myns.message.Message)
=> false
If I inline the protocol definition into the record, all works as expected.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With