Reputation: 6956
In a project using clojure.java.jmx
, I was extending it's Destract
protocols objects->data
function to transform more of the JMX data structures returned from calls or metadata queries into plain clojure data structures.
When I was done with the individual data structures, it should have been possible to do a (walk/prewalk jmx/objects->data (jmx/operations "java.lang:type=Threading"))
.
However, in the Destract
protocol there's an implementation of the objects->data
function for the type clojure.lang.Associative
that would mean maps would be processed incorrectly.
I could add an implementation for clojure.lang.IPersistentMap
in my namespace, but since clojure.lang.Associative
is also an interface for maps this would not work.
I ended up having to fork clojure.java.jmx
because of it. If there would be a way to change preference, or retract a protocol for a type within another namespace I wouldn't have had to.
Is there a way to prevent clojure.lang.Associative
taking precedence over clojure.lang.IPersistentMap
in the protocol ?
If there isn't, is it possible to retract a protocol for a type in another namespace ? Would it even be possible to implement it in regard to the way protocols are compiled into Java interfaces ?
Upvotes: 3
Views: 427
Reputation: 13185
Are you sure that providing your own implementation for clojure.lang.IPersistentMap
won't work? It works in my REPL session. It even works when I just override the default implementation for clojure.lang.Associative
:
user=> (ns ns1)
;;=> nil
ns1=> (defprotocol IPrintable (prnt [this]))
;;=> IPrintable
ns1=> (extend-protocol IPrintable clojure.lang.Associative (prnt [this] (str "clojure.lang.Associative " this)))
;;=> nil
ns1=> (ns ns2)
;;=> nil
ns2=> (ns1/prnt {:a 1})
;;=> "clojure.lang.Associative {:a 1}"
ns2=> (extend-protocol ns1/IPrintable clojure.lang.Associative (prnt [this] (str "My custom impl for clojure.lang.Associative " this)))
;;=> nil
ns2=> (ns1/prnt {:a 1})
;;=> "My custom impl for clojure.lang.Associative {:a 1}"
It also works in a sample project.
You must remember to require
the namespace where you extend-protocol
if it's not loaded transitively by another namespace. It's important that your extend-protocol
will be loaded after the one you would like to override.
Internally extend-protocol
or extend-type
modify a special map attached to protocol's metadata where it assoc
s a given type with a function implementation. If there was an existing entry for a given type it will be overridden. Thus the order in which extend-*
are executed is important.
When an object implements an interface or a protocol directly (e.g. inline in defrecord
) you cannot override the implementation (continued REPL session):
ns2=> (defrecord SomeData []
ns1/IPrintable (prnt [this] "I'm SomeData"))
;;=> ns2.SomeData
ns2=> (ns1/prnt (SomeData.))
;;=> "I'm SomeData"
ns2=> (extend-protocol ns1/IPrintable SomeData (prnt [this] "Custom impl " this))
;;=> IllegalArgumentException class ns2.SomeData already directly implements interface ns1.IPrintable for protocol:#'ns1/IPrintable clojure.core/extend (core_deftype.clj:775)
Upvotes: 1