Clojure协议在另一个名称空间中的优先级

Precedence of clojure protocols in another namespace

在使用clojure.java.jmx的项目中,我扩展了它的Destract协议objects->data函数,以将调用或元数据查询返回的更多JMX数据结构转换为纯Clojure数据结构。

当我处理完各个数据结构后,应该可以做一个(walk/prewalk jmx/objects->data (jmx/operations"java.lang:type=Threading"))了。

但是,在Destract协议中,对于clojure.lang.Associative类型存在objects->data函数的实现,这意味着映射将被错误地处理。
我可以在名称空间中添加clojure.lang.IPersistentMap的实现,但是由于clojure.lang.Associative也是地图的接口,因此无法使用。

因此,我最终不得不分叉clojure.java.jmx。如果有一种方法可以更改首选项,或者在另一个名称空间中撤回某个类型的协议,则不必。

有没有办法防止协议中的clojure.lang.Associative优先于clojure.lang.IPersistentMap

如果没有,是否可以在另一个名称空间中撤回某个类型的协议?关于将协议编译到Java接口中的方式,甚至可以实现它?


它应该工作

您确定不能为clojure.lang.IPersistentMap提供自己的实现吗? 它可以在我的REPL会话中使用。 当我只是覆盖clojure.lang.Associative的默认实现时,它甚至可以工作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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}"

它也适用于示例项目。

如果extend-protocol未被其他命名空间传递加载,则必须记住require命名空间。 重要的是,您的extend-protocol将在您要覆盖的那个之后加载。

extend-protocolextend-type在内部修改附加到协议元数据的特殊映射,在该映射中assoc是具有函数实现的给定类型。 如果有给定类型的现有条目,它将被覆盖。 因此,执行extend-*的顺序很重要。

什么时候不行

当对象直接实现接口或协议时(例如defrecord中的内联),您不能覆盖实现(继续的REPL会话):

1
2
3
4
5
6
7
8
9
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)