&?ROUTINE 的_intended_ 值是多少?

What is the _intended_ value of &?ROUTINE?

Raku 的 Rakudo 实现跟踪了多个关于(非常有用!)&?ROUTINE 变量未提供正确值(例如,#1768 and 2362)的问题,因此我意识到它的行为不太正确。但我试图了解它的预期行为是什么——这似乎是解决该行为的重要第一步。

运行 此代码与 Rakudo v2021.06 生成注释中注明的输出。此输出的哪些部分是正确的,哪些是错误?

sub foo  {
    note '## ifs:';
    do if True { say &?ROUTINE.name }                # OUTPUT: «foo»
    if True    { say &?ROUTINE.name }                # OUTPUT: «<unit>»

    note '## ifs w/ topic:';
    do if True -> $a { say $a; say &?ROUTINE.name }  # OUTPUT: «True», # OUTPUT«""»
    if True -> $a    { say $a; say &?ROUTINE.name }  # OUTPUT: «True», # OUTPUT«foo»
    
    note '## fors:';
    for 1 { say &?ROUTINE.name }                     # OUTPUT: «foo»
    say &?ROUTINE.name for 1;                        # OUTPUT: «""»

    note '## methods:';
    42.&{              say &?ROUTINE.name }          # OUTPUT: «foo»
    my method m($a:) { say &?ROUTINE.name }
    42.&m;                                           # OUTPUT: «m»
}
foo 

relevant docs 说 &?ROUTINE returns“Sub 的一个实例”,这听起来好像以上所有内容都应该是 'foo'。另一方面,方法是例程,所以我有点倾向于认为最后两个(匿名方法和命名方法)应该 而不是 是 'foo' .我也不确定是否所有的 '' 和 "" 值都代表错误,或者是否有一个原则在起作用,使某些(或全部?)这些预期行为成为可能。

(我还用 use soft pragma 测试了上面的代码,以确保内联没有产生我可以用那个 pragma 修复的效果;它对输出没有影响)

&?ROUTINE 符号的计算结果应该是表示最近的词法封闭例程的对象 - 即最近的 Routine 类型的封闭声明。这包括 SubMethod。此外,鉴于这些原则上都是闭包,它应该评估例程的正确闭包克隆。

因此正确的实现会产生:

sub foo  {
    note '## ifs:';
    do if True { say &?ROUTINE.name }                # OUTPUT: «foo»
    if True    { say &?ROUTINE.name }                # OUTPUT: «foo»

    note '## ifs w/ topic:';
    do if True -> $a { say $a; say &?ROUTINE.name }  # OUTPUT: «True», # OUTPUT«foo»
    if True -> $a    { say $a; say &?ROUTINE.name }  # OUTPUT: «True», # OUTPUT«foo»
    
    note '## fors:';
    for 1 { say &?ROUTINE.name }                     # OUTPUT: «foo»
    say &?ROUTINE.name for 1;                        # OUTPUT: «foo»

    note '## methods:';
    42.&{              say &?ROUTINE.name }          # OUTPUT: «foo»
    my method m($a:) { say &?ROUTINE.name }
    42.&m;                                           # OUTPUT: «m»
}
foo 

当前的 Rakudo 在许多情况下都犯了这个错误。尽管在编译器方面进行了大量工作,但我并不能很好地猜测它在这里做什么;我知道当我开始在我正在开发的新编译器前端中添加 &?ROUTINE 支持时,我不会复制当前的实现!

根据@Larry 的规范[1]:

&?ROUTINE is always an alias for the lexically innermost Routine (which may be a Sub, Method, or Submethod) ...

You can get the current routine name by calling &?ROUTINE.name. Outside of any [routine] declaration, this call returns failure.

Note that &?ROUTINE refers to the current single [routine], even if it is declared multi. To redispatch to the entire suite under a given short name, just use the named form to call the proto, since there are no anonymous multis.

脚注

[1] "@Larry" is/was 历史 shorthand 不断发展的事实上的 Raku 设计团队。 “spec”is/was 历史 shorthand 用于不断发展的设计。以上引自S06段The &?ROUTINE object