Clojure Protocol implementation not found for record type in different namespace

We have several problems with records and protocols in different namespaces.

We have a protocol in the foo.proto namespace.

(ns foo.proto) (defprotocol Proto (do-stuff [this xy])) 

I have a RecordA entry in the foo.record namespace:

 (ns foo.record (:require [foo.proto :as proto])) (defrecord RecordA [bar]) ;; RecordA implements the protocol: (extend-type RecordA proto/Proto (do-stuff [this xy] (* xy (:bar this)))) 

This works great while we are in a cue. Now, if we do uberjar on the other hand and run the code:

There is no implementation of the :: do-stuff protocol method: # 'foo.proto / Proto found for class

If we, on the other hand, implement the protocol in a type declaration as follows:

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

We no longer get the error (it took some time to understand). Also, if we move the Proto declaration to the same ns as RecordA, we also won’t get an error.

My questions:

  • What is the difference between the implementation in the declaration and the extension-extension or extension-protocol?

  • Why will this work if we move the Record and Protocol declarations to the same ns?

thanks

+6
source share
3 answers

The problem may be how you include the record and protocol in the file you are using. 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 xy] (* xy (:bar this)))) 

proto.clj

 (ns testclojure.proto) (defprotocol Proto (do-stuff [this xy])) 

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 launching the jar directly, I get the correct answer 24.

As to why it works with various combinations of namespaces and extends, defrecord creates a Java class, and extend-type creates an entry in the collection :impls protocol :impls .

+4
source

I am facing what seems to be the same problem:

I am trying to extend the protocol to a record that I defined separately from the protocol implementation, i.e. I am using (extension protocol), and not defining an implementation built into the record. Both the record and the protocol and implementation are in the same namespace. However, when I try to call it, he complains that the implementation does not exist.

 (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 the expanders, I see my entry there

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

However (extends?) Is false

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

If I embed a protocol definition in a record, everything works as expected.

+1
source

You need to import the protocol (:import (foo.proto Proto)) in the foo.record namespace foo.record .

-2
source

Source: https://habr.com/ru/post/954519/


All Articles