Перезагрузка мультиметодов через Slime
У меня возникают проблемы при перезагрузке мультиметодов при разработке в Emacs с использованием реплик Slime.
Переопределение defmethod
формы работают нормально, но если я изменю функцию диспетчеризации, я не смогу перезагрузить defmulti
форма. Я думаю, что я специально добавил или удалил параметры функции отправки.
В качестве обходного пути я смог ns-unmap
мультиметод вар, перезагрузите defmulti
формы, а затем перезагрузите все defmethod
формы.
Предположительно, это "ограничение" способа, которым Clojure реализует мультиметоды, то есть мы жертвуем динамизмом для скорости выполнения, но есть ли какие-то идиомы или методы разработки, которые помогут обойти это?
1 ответ
Короткий ответ: ваш способ справиться с этим абсолютно правильный. Если вы обнаружите, что обновляете мультиметод, чтобы особенно часто изменять функцию диспетчеризации, (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
взятие последовательности из нескольких имен и тела и обещание перерегистрировать методы после выполнения тела; вот набросок (опять же, не проверенный):
(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))
, Не уверен, что это стоит потери ясности.