Возможно, вам знаком способ, которым ключевое слово super в языке Java позволяет передавать управление методу (или конструктору) базового класса. В Perl 6 есть нечто похожее. Но в мире с множественной наследуемостью и миксинами нет смысла называть эту функцию super. Поэтому она называется nextsame.
Пример:
И после вызова C.new.sing, наша иерархия классов выдаст следующее:
в лесу родилась ёлочка,
в лесу она росла.
зимой и летом стройная,
а после умерла.
Обратите внимание, как вызов проходит от C.sing через B.sing к A.sing. Эти переходы обеспечиваются вызовами nextsame. Похоже на работу super в Java.
Но nextsame пригодится не только для вызовов по цепочке наследования. Вот пример без ООП:
Поэтому nextsame не назвали super: он не всегда обращается к базовому классу. Вместо этого происходит нечто более общего порядка. Что же именно?
Каждый раз при вызове некоторая часть языка заботится о том, что вызов приходит в нужную процедуру. Эта часть зовётся диспетчером. Диспетчер убеждается, что в следующем множественном вызове принимает участие нужная процедура:
И использование nextsame во втором foo приведёт к вызову первого.
В Perl 6 диспетчеры повсюду.
Они работают при вызовах методов, чтобы те могли передать вызов по цепочке наследования. Они во wrapped-процедурах, чтобы код, делающий «обёртывание», мог вызвать код, который он обёртывает. И они участвуют во множественных вызовах – чтобы разные варианты одной функции могли ссылаться друг на друга. Принцип один.
nextsame – способ пообщаться с вашим соседом-диспетчером. Диспетчер обращается к следующему кандидату с такой же сигнатурой.
Его можно использовать и в миксинах:
Особой новизны в таком использовании nextsame нет. Это ещё один пример передачи управления по цепочке наследования. Миксин в роли LogFoo вместе с but приводит к тому, что создаётся ещё один анонимный подкласс, который тоже выполняет роль LogFoo. Поэтому в данном случае миксин nextsame сводится к nextsame для наследования.
В итоге, nextsame работает везде, где вы ожидаете, и так, как ожидаете. Он передаёт управление следующей такой же штуке.
И у него есть близкие родственники:
Все они обычно могут быть использованы в тех же ситуациях, в которых работает nextsame.
Пример:
class A {
method sing {
say "а после умерла.";
}
}
class B is A {
method sing {
say ("зимой и летом стройная," xx 4).join(" ");
nextsame;
}
}
class C is B {
method sing {
say "в лесу родилась ёлочка,";
say "в лесу она росла.";
nextsame;
}
}
И после вызова C.new.sing, наша иерархия классов выдаст следующее:
в лесу родилась ёлочка,
в лесу она росла.
зимой и летом стройная,
а после умерла.
Обратите внимание, как вызов проходит от C.sing через B.sing к A.sing. Эти переходы обеспечиваются вызовами nextsame. Похоже на работу super в Java.
Но nextsame пригодится не только для вызовов по цепочке наследования. Вот пример без ООП:
sub bray {
say "а у барана - лексус.";
}
# Oh right, forgot to add the first line of the song...
&bray.wrap( {
say "У нашей Мэри был баран,";
nextsame;
} );
bray(); # У нашей Мэри был баран,
# а у барана - лексус.
Поэтому nextsame не назвали super: он не всегда обращается к базовому классу. Вместо этого происходит нечто более общего порядка. Что же именно?
Каждый раз при вызове некоторая часть языка заботится о том, что вызов приходит в нужную процедуру. Эта часть зовётся диспетчером. Диспетчер убеждается, что в следующем множественном вызове принимает участие нужная процедура:
multi foo( $x) { say "Любой аргумент " }
multi foo(Int $x) { say "Аргумент Int " }
foo(42) # Аргумент Int
И использование nextsame во втором foo приведёт к вызову первого.
В Perl 6 диспетчеры повсюду.
Они работают при вызовах методов, чтобы те могли передать вызов по цепочке наследования. Они во wrapped-процедурах, чтобы код, делающий «обёртывание», мог вызвать код, который он обёртывает. И они участвуют во множественных вызовах – чтобы разные варианты одной функции могли ссылаться друг на друга. Принцип один.
nextsame – способ пообщаться с вашим соседом-диспетчером. Диспетчер обращается к следующему кандидату с такой же сигнатурой.
Его можно использовать и в миксинах:
class A {
method foo { "Оба-на" }
}
role LogFoo {
method foo {
note ".foo вызвали";
nextsame;
}
}
my $logged_A = A.new but LogFoo;
say $logged_A.foo; # .foo вызвали
# Оба-на
Особой новизны в таком использовании nextsame нет. Это ещё один пример передачи управления по цепочке наследования. Миксин в роли LogFoo вместе с but приводит к тому, что создаётся ещё один анонимный подкласс, который тоже выполняет роль LogFoo. Поэтому в данном случае миксин nextsame сводится к nextsame для наследования.
В итоге, nextsame работает везде, где вы ожидаете, и так, как ожидаете. Он передаёт управление следующей такой же штуке.
И у него есть близкие родственники:
nextsame передать управление с теми же аргументами, не возвращаться
callsame передать управление с теми же аргументами, и вернуться
nextwith($p1, $p2, ...) передать управление с заданными аргументами, не возвращаться
callwith($p1, $p2, ...) передать управление с заданными аргументами, вернуться
Все они обычно могут быть использованы в тех же ситуациях, в которых работает nextsame.
lockywolf
А как она работает с множественной наследуемостью?