[Перевод] Meta-Object Protocol в Perl6
В некоторых языках программирования существует интерфейс для создания класса не через его определение, а через выполнение некоего кода. Этот API называется Meta-Object Protocol или MOP.
В Perl 6 есть MOP, позволяющий создавать классы, роли и грамматики, добавлять методы и атрибуты и делать интроспекцию классов. К примеру, вот как в Rakudo можно использовать вызовы MOP, чтобы узнать, как реализован тип Rat (рациональные числа). Вызовы методов MOP обычно начинаются не с точки, а с .^
$ perl6
> say join ', ', Rat.^attributes
$!numerator, $!denominator
> # список всех методов слишком длинный,
> # поэтому мы сделаем случайную их выборку
> say join ', ', Rat.^methods(:local).pick(5)
unpolar, ceiling, reals, Str, round
> say Rat.^methods(:local).grep('log').[0].signature.perl
:(Numeric $x: Numeric $base = { ... };; *%_)
Большинство строк должны быть понятны: у объектов класса Rat есть два атрибута, $! numerator и $! denominator, и много методов. Метод log принимает значение Numeric как вызывающий метод (это отмечено двоеточием после имени параметра) и необязательный второй параметр $base, у которого есть значение по умолчанию (число Эйлера e).
Хороший пример использования можно взять из интерфейса баз данных Perl 6. У него есть возможность записывать вызовы объекта в log, ограничив при этом запись методов одной конкретной ролью (к примеру, только роли, которая занимается вопросами соединения с базой, или же вопросами извлечения данных). Вот пример:
sub log-calls($obj, Role $r) {
my $wrapper = RoleHOW.new;
for $r.^methods -> $m {
$wrapper.^add_method($m.name, method (|$c) {
# вывести протокол выполнения
# note() пишет в standard error
note ">> $m";
# вызвать следующий метод с тем же именем и
# теми же аргументами
nextsame;
});
}
$wrapper.^compose();
# оператор 'does' работает так же, как 'but', но
# изменяет только копию объекта
$obj does $wrapper;
}
role Greet {
method greet($x) {
say "hello, $x";
}
}
class SomeGreeter does Greet {
method LOLGREET($x) {
say "OH HAI "~ uc $x;
}
}
my $o = log-calls(SomeGreeter.new, Greet);
# записали в log, поскольку это отработала роль Greet
$o.greet('you');
# не записали в log, поскольку это не та роль
$o.LOLGREET('u');
Выводит:
>> greet
hello, you
OH HAI U
С использованием Meta-Object Protocol классы, роли и грамматики становятся доступны вам не только через специальный синтаксис, но и через обычный API. Это добавляет гибкости в ООП-код и позволяет проводить интроспекцию и изменения объектов.