Объект, роли и многократная отправка - PullRequest
2 голосов
/ 05 ноября 2019

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

role A {
    has $!b;

    submethod BUILD( :$!b ) {}

    multi method bar () {
    return $!b;
    }
}

class B does A {

    submethod BUILD( :$!b ) {}

    multi method bar() {
    return " * " ~ callsame ~ " * ";
    }
}

my $a = A.new( b => 33);
say $a.bar();
my $b = B.new( b => 33 );
say $b.bar();

Сбой, однако, с:

Calling callsame(Str) will never work with declared signature ()

(я действительно понятия не имею, почему callame использует Str в качестве подписи). Изменение method bar на использование callwith:

multi method bar() {
    return " * " ~ callwith() ~ " * ";
}

Просто не работает:

Use of Nil in string context
  in method bar at multi.p6 line 18
 *  *

Есть ли какой-то особый способ работы с call* внутри ролей / классов?

1 Ответ

5 голосов
/ 05 ноября 2019

Первая проблема связана с синтаксисом. Вызов функции listop анализирует список аргументов после него, начиная с термина, поэтому:

return " * " ~ callsame ~ " * ";

Группы, подобные этой:

return " * " ~ callsame(~ " * ");

И поэтому вы вызываете ~ префиксный оператор для "*", откуда исходит аргумент Str, на который он жалуется.

Однако, в конечном счете, проблема заключается в неправильном понимании семантики композиции ролей и / или отсрочки. Рассмотрим случай не multi:

role R { method m() { say 1; callsame() } }
class B { method m() { say 2; callsame() } }
class C is B does R { method m() { say 3; callsame(); } }
C.m

Это приводит к выводу:

3
2

Обратите внимание, что 1 никогда не достигается. Это связано с тем, что композиция ролей выравнивает : как если бы код из роли был введен в класс. Когда у класса уже есть метод с таким именем, он заменяется на тот, который в роли.

Если мы добавим multi к каждому из них:

role R { multi method m() { say 1; callsame() } }
class B { multi method m() { say 2; callsame() } }
class C is B does R { multi method m() { say 3; callsame(); } }
C.m

Поведение сохраняется:

3
2

Поскольку компоновщик ролей учитывает длинное имя multi method, то есть учет подписи. Так как они одинаковы, побеждает тот, кто в классе. Если бы он сохранил оба, мы получили бы исходный вызов, который привел бы к неоднозначной ошибке отправки!

Отсрочка с nextsame, callsame, nextwith и callwith - итерация всех повозможные вещи, которые мы могли бы отправить.

В случае не multi method это достигается путем ходьбы по MRO;поскольку метод из роли не был составлен, он не появляется ни в одном классе в MRO (ничего, что только классы появляются в MRO, поскольку роли сглаживаются во время композиции).

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

...