Как использовать сигнатуры методов для изменения действий функции извне.

введение

Недавно я написал статью, в которой рассмотрел некоторые из моих любимых функций парадигмы программирования с множественной диспетчеризацией. В целом, это одна из моих любимых недавних статей, и ее стоит прочитать, поэтому, если вы хотите прочитать статью для себя, вот ссылка:



Одной из тем, которые я затронул в статье, была возможность использовать сигнатуры методов для всевозможных фантастических приложений. В статье я рассмотрел два примера этого: в одном я использовал сигнатуры методов для выбора наилучшего из возможных отображаемых 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. Просто невероятно видеть, на что способна реализация множественной диспетчеризации Джулии! Я надеюсь, что вы нашли это таким же потрясающим, как и я, спасибо всем за чтение; Я ценю тебя!