对象、角色和多重分派
Object, roles and multiple dispatch
我正在尝试使用多重分派来重载和使用组合 类 中的方法。这是实现:
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 ()
(我真的不知道为什么callsame使用Str
作为签名)。将 method bar
更改为使用 callwith
:
multi method bar() {
return " * " ~ callwith() ~ " * ";
}
根本行不通:
Use of Nil in string context
in method bar at multi.p6 line 18
* *
在 roles/classes 中使用 call*
有什么特别的方法吗?
第一个问题是语法问题。一个 listop 函数调用在它之后解析一个参数列表,从一个术语开始,所以这个:
return " * " ~ callsame ~ " * ";
这样的群组:
return " * " ~ callsame(~ " * ");
因此您在“*”上调用 ~
前缀运算符,这是它抱怨的 Str
参数的来源。
然而,归根结底,这里的问题是对角色组合语义的误解 and/or 延迟。考虑一个非 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 是如何从未达到的。这是因为角色组合是 扁平化 :就好像来自角色的代码被放入了 class。当 class 已经有一个同名的方法时,它会优先于角色中的方法。
如果我们把 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
的长名——即占签名。由于它们完全相同,因此 class 中的那个获胜。如果它同时保留两者,我们将以初始调用结束,导致不明确的调度错误!
Deferral nextsame
、callsame
、nextwith
和 callwith
都遍历了我们本可以派遣到的可能对象。
在非multi
method
的情况下,这是通过走MRO实现的;由于角色中的方法未组合,因此它不会出现在 MRO 中的任何 class 中(只有 classes 不会出现在 MRO 中,因为角色在组合时被展平了) .
在 multi
method
的情况下,我们取而代之的是遍历那些会接受初始调度参数的候选集。同样,由于在 class 中选择了相同的长名称方法以支持组合时的角色,因此角色中的方法根本不首先考虑调度:它不是' t在proto
的候选名单中,所以不会推迟到。
我正在尝试使用多重分派来重载和使用组合 类 中的方法。这是实现:
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 ()
(我真的不知道为什么callsame使用Str
作为签名)。将 method bar
更改为使用 callwith
:
multi method bar() {
return " * " ~ callwith() ~ " * ";
}
根本行不通:
Use of Nil in string context
in method bar at multi.p6 line 18
* *
在 roles/classes 中使用 call*
有什么特别的方法吗?
第一个问题是语法问题。一个 listop 函数调用在它之后解析一个参数列表,从一个术语开始,所以这个:
return " * " ~ callsame ~ " * ";
这样的群组:
return " * " ~ callsame(~ " * ");
因此您在“*”上调用 ~
前缀运算符,这是它抱怨的 Str
参数的来源。
然而,归根结底,这里的问题是对角色组合语义的误解 and/or 延迟。考虑一个非 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 是如何从未达到的。这是因为角色组合是 扁平化 :就好像来自角色的代码被放入了 class。当 class 已经有一个同名的方法时,它会优先于角色中的方法。
如果我们把 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
的长名——即占签名。由于它们完全相同,因此 class 中的那个获胜。如果它同时保留两者,我们将以初始调用结束,导致不明确的调度错误!
Deferral nextsame
、callsame
、nextwith
和 callwith
都遍历了我们本可以派遣到的可能对象。
在非multi
method
的情况下,这是通过走MRO实现的;由于角色中的方法未组合,因此它不会出现在 MRO 中的任何 class 中(只有 classes 不会出现在 MRO 中,因为角色在组合时被展平了) .
在 multi
method
的情况下,我们取而代之的是遍历那些会接受初始调度参数的候选集。同样,由于在 class 中选择了相同的长名称方法以支持组合时的角色,因此角色中的方法根本不首先考虑调度:它不是' t在proto
的候选名单中,所以不会推迟到。