raku 中角色的签名限制
Signature restriction in roles in raku
也许我遗漏了一些东西,但我想知道这段代码是否应该有充分的理由
编译
role L {
method do-l (Int, Int --> Int ) { ... }
}
class A does L {
method do-l (Int $a, Real $b --> Str) {
.Str ~ ": Did you expect Int?" with $a + $b
}
}
my $a = A.new;
say $a.do-l: 2, 3.323
这将输出
5.323: Did you expect Int?
我很好奇是否有人知道编译器至少可以抛出一些
使用角色 L
.
的已实现签名发出警告
我假设您在这里问的是为什么没有关于存根的警告。实际上,通常必须实现存根方法 — 但仅此而已。
你可以在 Rakudo 的 source 中看到这些角色是如何组成 class 的($yada
基本上意味着 $is-stubbed
):
if $yada {
unless has_method($!target, $name, 0)
|| $!target.HOW.has_public_attribute($!target, $name) {
my @needed;
for @!roles {
for nqp::hllize($_.HOW.method_table($_)) -> $m {
if $m.key eq $name {
nqp::push(@needed, $_.HOW.name($_));
}
}
}
nqp::push(@stubs, nqp::hash('name', $name, 'needed', @needed, 'target', $!target));
}
}
所以可以看出逻辑就是看是否存在同名方法。绝对有可能编写一个模块来通过扩充 apply() 方法或完全替换 RoleToClassApplier
class 来更新此逻辑。但是,这可能很困难。例如,考虑:
class Letter { }
class A is Letter { }
class B is Letter { }
role Foo {
method foo (Letter) { ... }
}
class Bar does Foo {
method foo (A) { 'a' }
method foo (B) { 'b' }
}
我们是否应该考虑正确实施存根方法?但是其他人稍后可能会说 class C is Letter
并且突然没有实现。 (当然,我们可以说最好的逻辑至少需要相同或超类型,但这比动态语言所需的限制性更强,IMO)。
没有,AFAICT,在组合过程中对角色调用的方法也引用了 class(实际上,add_method
中根本没有调用任何方法),所以没有办法在角色中编写自己的支票。 (但我可能是错的,也许 raiph、lizmat 或 jnthn 可以纠正我)。
在这种情况下,我的建议是,不要对其进行存根,而只需添加一个 die 语句:
role L {
method do-l(Int $a, Int $b --> Int) {
die "SORRY! Classes implementing role L must support the signature (Int, Int)";
}
}
这不会在编译时捕获它,但如果在任何时候 L
中的另一个方法需要调用 do-l(Int, Int)
— 即使组合 class 实现了其他签名 — , 它会被调用并且很快就会发现错误。
throw some warning with the implemented signature of the role L
.
如果在方法声明前加上 multi
:
role L {
multi method do-l (Int, Int --> Int ) { ... }
}
有了这个你的程序显示:
===SORRY!=== Error while compiling ...
Multi method 'do-l' with signature :(A: Int $, Int $, *%_ --> Int)
must be implemented by A because it is required by a role ...
I'd like to know if there is a good reason why this code should compile [without the multi
]
我认为设计意图是支持多态组合的两个概念:
如果没有 multi
,执行仅涉及具有权利名字;忽略参数。
With multi
,执行也涵盖名称和所有参数(or some).
我个人认为是否有充分理由:
支持两种方法多态性? 有时强制严格遵守签名是有帮助的。有时它会妨碍。
通过 multi
区分它们? 完整的签名执行要求实现 classes/roles 有一个方法 相同的签名。但是,如果一个实现 class/role 想要处理一个 int
而不是 Int
的参数怎么办?乐妥协。如果实施 class/role 有一个完全兼容的方法,它可以 也 有变化。传达这一点的完美方法是在存根方法前加上 multi
.
默认是仅名称多态性? 我们可以选择 multi
语义作为默认值,让用户写一个 only
前缀,如果他们想要仅名称多态性。但这会扭转通常的情况(即忽略存根方法)。更一般地说,Raku 的目的是为其功能提供广泛的限制,从宽松到紧张,并根据多年来用户的反馈为任何给定的功能选择一个默认值。
如果默认值似乎不正确怎么办?如果现有的限制范围不够怎么办?如果一组认为我们应该向左走而另一组认为我们应该向右走怎么办?
Raku 拥有 (imo) 卓越的治理机制来支持用户驱动的语言发展。在顶层有像 the braid architecture. At the bottom level there are elements like . In the middle are elements like RoleToClassApplier
这样的元素,它调解了将角色应用到 class 的过程,这是需要找到所需方法的点或 class'的建设将失败。简而言之,如果语言不能按照您想要的方式工作,包括诸如限制之类的事情,您至少可以在原则上改变它。
也许我遗漏了一些东西,但我想知道这段代码是否应该有充分的理由 编译
role L {
method do-l (Int, Int --> Int ) { ... }
}
class A does L {
method do-l (Int $a, Real $b --> Str) {
.Str ~ ": Did you expect Int?" with $a + $b
}
}
my $a = A.new;
say $a.do-l: 2, 3.323
这将输出
5.323: Did you expect Int?
我很好奇是否有人知道编译器至少可以抛出一些
使用角色 L
.
我假设您在这里问的是为什么没有关于存根的警告。实际上,通常必须实现存根方法 — 但仅此而已。
你可以在 Rakudo 的 source 中看到这些角色是如何组成 class 的($yada
基本上意味着 $is-stubbed
):
if $yada {
unless has_method($!target, $name, 0)
|| $!target.HOW.has_public_attribute($!target, $name) {
my @needed;
for @!roles {
for nqp::hllize($_.HOW.method_table($_)) -> $m {
if $m.key eq $name {
nqp::push(@needed, $_.HOW.name($_));
}
}
}
nqp::push(@stubs, nqp::hash('name', $name, 'needed', @needed, 'target', $!target));
}
}
所以可以看出逻辑就是看是否存在同名方法。绝对有可能编写一个模块来通过扩充 apply() 方法或完全替换 RoleToClassApplier
class 来更新此逻辑。但是,这可能很困难。例如,考虑:
class Letter { }
class A is Letter { }
class B is Letter { }
role Foo {
method foo (Letter) { ... }
}
class Bar does Foo {
method foo (A) { 'a' }
method foo (B) { 'b' }
}
我们是否应该考虑正确实施存根方法?但是其他人稍后可能会说 class C is Letter
并且突然没有实现。 (当然,我们可以说最好的逻辑至少需要相同或超类型,但这比动态语言所需的限制性更强,IMO)。
没有,AFAICT,在组合过程中对角色调用的方法也引用了 class(实际上,add_method
中根本没有调用任何方法),所以没有办法在角色中编写自己的支票。 (但我可能是错的,也许 raiph、lizmat 或 jnthn 可以纠正我)。
在这种情况下,我的建议是,不要对其进行存根,而只需添加一个 die 语句:
role L {
method do-l(Int $a, Int $b --> Int) {
die "SORRY! Classes implementing role L must support the signature (Int, Int)";
}
}
这不会在编译时捕获它,但如果在任何时候 L
中的另一个方法需要调用 do-l(Int, Int)
— 即使组合 class 实现了其他签名 — , 它会被调用并且很快就会发现错误。
throw some warning with the implemented signature of the role
L
.
如果在方法声明前加上 multi
:
role L {
multi method do-l (Int, Int --> Int ) { ... }
}
有了这个你的程序显示:
===SORRY!=== Error while compiling ...
Multi method 'do-l' with signature :(A: Int $, Int $, *%_ --> Int)
must be implemented by A because it is required by a role ...
I'd like to know if there is a good reason why this code should compile [without the
multi
]
我认为设计意图是支持多态组合的两个概念:
如果没有
multi
,执行仅涉及具有权利名字;忽略参数。With
multi
,执行也涵盖名称和所有参数(or some).
我个人认为是否有充分理由:
支持两种方法多态性? 有时强制严格遵守签名是有帮助的。有时它会妨碍。
通过
multi
区分它们? 完整的签名执行要求实现 classes/roles 有一个方法 相同的签名。但是,如果一个实现 class/role 想要处理一个int
而不是Int
的参数怎么办?乐妥协。如果实施 class/role 有一个完全兼容的方法,它可以 也 有变化。传达这一点的完美方法是在存根方法前加上multi
.默认是仅名称多态性? 我们可以选择
multi
语义作为默认值,让用户写一个only
前缀,如果他们想要仅名称多态性。但这会扭转通常的情况(即忽略存根方法)。更一般地说,Raku 的目的是为其功能提供广泛的限制,从宽松到紧张,并根据多年来用户的反馈为任何给定的功能选择一个默认值。
如果默认值似乎不正确怎么办?如果现有的限制范围不够怎么办?如果一组认为我们应该向左走而另一组认为我们应该向右走怎么办?
Raku 拥有 (imo) 卓越的治理机制来支持用户驱动的语言发展。在顶层有像 the braid architecture. At the bottom level there are elements like RoleToClassApplier
这样的元素,它调解了将角色应用到 class 的过程,这是需要找到所需方法的点或 class'的建设将失败。简而言之,如果语言不能按照您想要的方式工作,包括诸如限制之类的事情,您至少可以在原则上改变它。