[Перевод] 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. Это добавляет гибкости в ООП-код и позволяет проводить интроспекцию и изменения объектов.

© Habrahabr.ru