3

我正在尝试使用多个调度来重载和使用组合类中的方法。这是实现:

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 ()

(我真的不知道为什么 callameStr用作签名)。更改method bar使用callwith

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

根本不起作用:

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

call*在角色/类中是否有任何特殊的工作方式?

4

1 回答 1

7

第一个问题是语法问题。一个 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和all 进行延迟,callwith遍历我们可以调度的可能事物。

在非 的情况下multi method,这是通过步行 MRO 来实现的;由于来自角色的方法没有被组合,因此它不会出现在 MRO 的任何类中(没有什么只有类出现在 MRO 中,因为角色在组合时被展平了)。

在 a 的情况下multi method,我们改为遍历将接受初始调度参数的候选集。同样,由于在组合时选择了类中具有相同长名称的方法来支持角色一,因此角色中的方法一开始根本不考虑调度:它不在候选中的列表proto,因此不会被推迟到。

于 2019-11-05T11:55:10.933 回答