Приоритет протоколов 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)