问题描述
Raku 的 Rakudo 实现跟踪了关于(非常有用!)&?ROUTINE 变量没有提供正确值(例如,#1768 和 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 返回“一个 Sub 的实例”,这使得上面的所有内容听起来都应该是 'foo'。另一方面,方法是例程,所以我有点倾向于认为最后两个(匿名方法和命名方法)应该不是'foo'。我也不确定所有的 '' 和 "" 值是否都代表错误,或者是否有一个原则在起作用,导致某些(或全部?)这些预期行为。
(我还使用 use soft
编译指示测试了上述代码,以确保内联不会产生我可以使用该编译指示修复的影响;它对输出没有影响)
解决方法
&?ROUTINE
符号的计算结果应该是表示最近的词法封闭例程的对象 - 即最近的 Routine
类型的封闭声明。这包括 Sub
和 Method
。此外,鉴于这些原则上都是闭包,它应该评估为例程的正确闭包克隆。
因此正确的实现会产生:
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
当前的乐堂因此在许多情况下都犯了这个错误。尽管在编译器上做了大量工作,我还是不太清楚它在这里做什么;我知道在我正在开发的新编译器前端添加 &?ROUTINE
支持时,我不会复制当前的实现!
根据@Larry 的规范[1]:
&?ROUTINE
始终是词法最内层 Routine
(可能是 Sub
、Method
或 Submethod
)的别名...>
您可以通过调用 &?ROUTINE.name
获取当前例程名称。在任何 [例程] 声明之外,此调用返回失败。
请注意,&?ROUTINE
指的是当前的单个 [例程],即使它声明为 multi
。要以给定的短名称重新调度到整个套件,只需使用命名形式调用 proto,因为没有匿名 multi
。
脚注
[1]“@Larry”是/曾经是不断发展的事实上的 Raku 设计团队的历史速记。 “规格”是/曾经是不断发展的设计的历史简写。以上引述来自 S06 部分 The &?ROUTINE object