Лекция
В компьютерном программировании диспетчеризация предикатов — это обобщение множественной диспетчеризации («мультиметодов»), позволяющее выбирать метод для вызова во время выполнения на основе произвольных разрешимых логических предикатов и/или сопоставления с шаблоном, прикрепленных к объявлению метода.
Проще говоря:
Raku поддерживает диспетчеризацию предикатов с использованием условий «where», которые могут выполнять произвольный код для любого параметра функции или метода.
В Julia для этого есть пакет PatternDispatch.jl , но в остальном она изначально поддерживает множественную диспетчеризацию.
Были созданы экспериментальные реализации для Common LISP и для Java (JPred ).
Это позволяет свободно расширять ранее объявленные методы на детальном уровне, но множественные расширения с идентичными или перекрывающимися предикатами, созданные разными разработчиками, могут непредвиденным образом мешать друг другу. В этом отношении это похоже на аспектно-ориентированное программирование .

На псевдокоде:
draw(shape::Circle) draw(shape::Rectangle)
Выбор зависит только от типа аргумента.
draw(Circle(...)) -> первый метод draw(Rectangle(...)) -> второй метод
Предположим, есть функция расчета скидки.
discount(customer) where customer.vip discount(customer) where customer.age >= 65 discount(customer) where true
Вызовы:
discount(Customer(vip=true, age=30))
→ выберется VIP-версия.
discount(Customer(vip=false, age=70))
→ версия для пенсионеров.
discount(Customer(vip=false, age=40))
→ версия по умолчанию.
Здесь все объекты одного типа Customer, но выбор метода идет по значениям полей.
В Raku есть встроенные предикаты where.
multi sub sign(Int $x where * > 0) {
"positive"
}
multi sub sign(Int $x where * < 0) {
"negative"
}
multi sub sign(Int $x where * == 0) {
"zero"
}
Использование:
say sign(10); # positive say sign(-5); # negative say sign(0); # zero
Здесь все методы принимают один и тот же тип Int, но диспетчер выбирает нужный вариант по условию.
Допустим, обрабатываются HTTP-запросы.
handle(request) where request.method == "GET" handle(request) where request.method == "POST" handle(request) where request.method == "DELETE"
Вызов:
handle(Request("POST"))
→ будет вызван обработчик POST.
Проверка диапазонов:
tax(amount) where amount < 1000 tax(amount) where amount >= 1000 && amount < 5000 tax(amount) where amount >= 5000
Вызовы:
tax(300) tax(2000) tax(10000)
Тип аргумента всегда один и тот же (Number), но логика выбора разная.
Представим, что два разработчика независимо добавили методы.
Разработчик А:
process(x) where x > 0
Разработчик Б:
process(x) where x % 2 == 0
Что делать с вызовом:
process(10)
Оба предиката истинны:
10 > 0 10 % 2 == 0
Система должна как-то решить конфликт:
Именно поэтому диспетчеризация предикатов значительно сложнее обычной множественной диспетчеризации.
| Механизм | Основание выбора метода |
|---|---|
| Одиночная диспетчеризация | Тип одного объекта |
| Множественная диспетчеризация | Типы всех аргументов |
| Диспетчеризация предикатов | Произвольные логические условия над аргументами |
Например, вызов
pay(user, amount)
может выбирать реализацию по условию:
where user.vip where amount > 10000 where user.country == "UA" where amount < 0
что делает ее намного более гибкой, но и намного сложнее для анализа и разрешения конфликтов.
Комментарии