Короткий ответ: ваш способ борьбы с этим абсолютно верен.Если вы обнаружите, что обновляете мультиметод с целью частого изменения функции диспетчеризации, (1) я думаю, что это необычно :-), (2) вы можете написать набор функций / макросов, чтобы помочь с перезагрузкой.Ниже я делаю набросок двух непроверенных (!) Макросов, чтобы помочь с (2).
Почему?
Сначала, однако, краткое обсуждение вопроса «почему».Функция диспетчеризации для поиска мультиметода, реализованная в настоящее время, не требует синхронизации - диспетчеризация fn хранится в поле final
объекта MultiFn
.Это, конечно, означает, что вы не можете просто изменить функцию диспетчеризации для данного мультиметода - вы должны воссоздать сам мультиметод.Это, как вы указываете, требует перерегистрации всех ранее определенных методов, что создает трудности.
Текущее поведение позволяет перезагружать пространства имен с формами defmethod
в них, не теряя при этом все свои методы за счет затрат.сделать немного более громоздким замену фактического мультиметода, когда это действительно то, что вы хотите сделать.
Если вы действительно хотите, диспетчер fn можно изменить с помощью отражения, но это имеет проблематичную семантику, особенно вмногопоточные сценарии (см. Спецификация языка Java 17.5.3 для получения информации об отражающих обновлениях полей final
после построения).
Хаки (неотражающие)
Один из подходов к (2) состоит в том, чтобы автоматизировать повторное добавление методов после переопределения с помощью макроса в соответствии с (непроверенным)
(defmacro redefmulti [multifn & defmulti-tail]
`(let [mt# (methods ~multifn)]
(ns-unmap (.ns (var ~multifn)) '~multifn)
(defmulti ~multifn ~@defmulti-tail)
(doseq [[dispval# meth#] mt#]
(.addMethod ~multifn dispval# meth#))))
Альтернативный дизайн будет использовать макрос, скажем, скажем, with-method-reregistration
взяв последовательность из нескольких имен и тела и пообещав перерегистрировать методы после выполнения bоды;вот эскиз (опять же, не проверенный):
(defmacro with-method-reregistration [multifns & body]
`(let [mts# (doall (zipmap ~(map (partial list 'var) multifns)
(map methods ~multifns))))]
~@body
(doseq [[v# mt#] mts#
[dispval# meth#] mt#]
(.addMethod @v# dispval# meth#))))
Вы бы использовали его, чтобы сказать (with-method-reregistration [my-multi-1 my-multi-2] (require :reload 'ns1 ns2))
.Не уверен, что это стоит потери ясности.