Как использовать сигнатуры методов для изменения действий функции извне.
введение
Недавно я написал статью, в которой рассмотрел некоторые из моих любимых функций парадигмы программирования с множественной диспетчеризацией. В целом, это одна из моих любимых недавних статей, и ее стоит прочитать, поэтому, если вы хотите прочитать статью для себя, вот ссылка:
Одной из тем, которые я затронул в статье, была возможность использовать сигнатуры методов для всевозможных фантастических приложений. В статье я рассмотрел два примера этого: в одном я использовал сигнатуры методов для выбора наилучшего из возможных отображаемых MIME, а в другом я использовал сигнатуры методов для создания собственного диспетчера закрытия. Я также кратко упомянул другое приложение, для которого я использовал эту технику, которое создает расширения для действия метода, просто определяя новые отправки этого метода.
Этот прием используется в моем блокноте-редакторе для Юлии Olive.jl, чтобы сделать расширения определяемыми без перезагрузки явно. У меня также есть статья, подробно описывающая все это, но моя цель в этой статье — вернуться к базовым типам, чтобы сделать пример более интерпретируемым. При этом, если вы хотите прочитать статью, в которой подробно описывается, как это используется в Olive, вы можете прочитать больше здесь:
Давайте начнем!
"блокнот"
методы меняют методы
Для того, чтобы продемонстрировать эту технику, нам нужно сделать небольшую настройку. Во-первых, нам нужно сделать символический тип — в данном случае я делаю его символическим исключительно для того, чтобы упростить создание множества различных диспетчеров. Нам не нужны никакие поля, только параметр — так как этот конструктор будет использоваться исключительно для множественной отправки.
struct Object{T <: Any} end
Теперь мы определим метод с отправленным Object
. Мы помещаем любое имя, которое хотим, в параметры в качестве символа, тогда мы можем просто написать метод:
function showinfo(obj::Object{:banana}) println("color: yellow, rating: 5") end
Теперь нам просто нужен другой метод, который будет обращаться к каждому Method
из showinfo
. Это можно сделать с помощью метода methods
. Подпись предоставит нам тип нашего метода, а также аргументы для метода. Это полезно, потому что это может сказать нам и о том, как вызывать метод, и о том, что метод существует.
function showall() sigs = [m.sig.parameters[2] for m in methods(showinfo)] end
Наконец, мы просто вызываем каждый тип с помощью метода showinfo
и передаем его через метод:
[showinfo(sig()) for sig in sigs]
Для окончательного метода, который выглядит так:
function showall() sigs = [m.sig.parameters[2] for m in methods(showinfo)] [showinfo(sig()) for sig in sigs] nothing end
Теперь попробуем:
showall() color: yellow, rating: 5
На первый взгляд это может показаться не таким уж крутым, но теперь давайте создадим еще один модуль. Я также собираюсь напрямую импортировать showinfo
из Main
.
module MyMod import Main: showinfo end
Я напрямую импортировал этот метод по какой-то причине; мы собираемся расширить этот метод внутри этого модуля. При этом давайте напишем новый метод showinfo
:
module MyMod import Main: showinfo, Object function showinfo(obj::Object{:apple}) println("color: red, rating: 8") end function showinfo(obj::Object{:orange}) println("color: orange, rating: 8") end end
Теперь все, что нам нужно сделать, это использовать using
для загрузки этих новых определений методов:
using Main.MyMod
Теперь, если мы воспользуемся нашим методом showall
, мы получим результат не только диспетчера Object{:banana}
, но и двух других!
showall() color: yellow, rating: 5 color: red, rating: 8 color: orange, rating: 8
заключение
Есть много вещей, которые можно сделать с отправкой Джулии, чего нельзя было бы ожидать от языка программирования. Этот пример расширения — особенно крутой взгляд на то, как самоанализ кода Julia, используемый вместе с диспетчеризацией, может быть невероятно мощным. С помощью этой техники мы можем изменить то, что делает метод, не изменяя предоставленные аргументы, просто импортировав модуль. По понятным причинам эта реализация невероятно проста в использовании, поскольку все, что нужно сделать, это импортировать модуль.
Хотя это не может быть решением для каждого приложения, похоже, оно действительно хорошо работает в моем случае использования Olive.jl. Просто невероятно видеть, на что способна реализация множественной диспетчеризации Джулии! Я надеюсь, что вы нашли это таким же потрясающим, как и я, спасибо всем за чтение; Я ценю тебя!