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'的建设将失败。简而言之,如果语言不能按照您想要的方式工作,包括诸如限制之类的事情,您至少可以在原则上改变它。