Приоритет протоколов clojure в другом пространстве имен

В проекте с использованием clojure.java.jmxЯ расширяю это Destract протоколы objects->data функция для преобразования большего количества структур данных JMX, возвращаемых из вызовов или запросов метаданных, в простые структуры данных clojure.

Когда я закончил с отдельными структурами данных, должно было быть возможно сделать (walk/prewalk jmx/objects->data (jmx/operations "java.lang:type=Threading")),

Однако в Destract Протокол есть реализация objects->data функция для типа clojure.lang.Associative это будет означать, что карты будут обработаны неправильно. Я мог бы добавить реализацию для clojure.lang.IPersistentMap в моем пространстве имен, но так как clojure.lang.Associative также интерфейс для карт, это не будет работать.

В итоге мне пришлось раскошелиться clojure.java.jmx из-за этого. Если бы был способ изменить предпочтение или убрать протокол для типа в другом пространстве имен, мне бы этого не пришлось.

Есть ли способ предотвратить clojure.lang.Associative иметь приоритет над clojure.lang.IPersistentMap в протоколе?

Если нет, возможно ли отозвать протокол для типа в другом пространстве имен? Можно ли было бы реализовать его в отношении того, как протоколы компилируются в интерфейсы Java?

1 ответ

Решение

Он должен работать

Вы уверены, что предоставляете свою собственную реализацию для clojure.lang.IPersistentMap не сработает? Это работает в моей сессии REPL. Это даже работает, когда я просто переопределяю реализацию по умолчанию для 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}"

Это также работает в примере проекта.

Вы должны помнить, чтобы require пространство имен, где вы extend-protocol если он не загружен транзитивно другим пространством имен. Важно, чтобы ваш extend-protocol будет загружен после того, который вы хотите переопределить.

внутренне extend-protocol или же extend-type изменить специальную карту, прикрепленную к метаданным протокола, где assocДанный тип с реализацией функции. Если для данного типа существовала запись, она будет переопределена. Таким образом, порядок, в котором extend-* выполнены важно.

Когда это не сработает

Когда объект реализует интерфейс или протокол напрямую (например, встроенный в defrecord) вы не можете переопределить реализацию (продолжение сессии REPL):

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)
Другие вопросы по тегам