Raku中运行时创建的符号在PseudoStash与EVAL中不可用问题问询
问题背景
我最初尝试排查运行时创建的符号无法被EVAL访问的问题。
outer-EVAL.raku
#!/usr/bin/env raku use MONKEY-SEE-NO-EVAL; package Foobar { our $foo = 'foo'; our sub eval { say OUTER::; EVAL "say $bar"; } } Foobar::<$bar> = 'bar'; say $Foobar::bar; Foobar::eval; .say for Foobar::;
运行报错:
$ ./outer-EVAL.raku ===SORRY!=== Error while compiling /development/raku/VTS-Template.raku/scratchpad/./outer-EVAL.raku Variable '$bar' is not declared at /development/raku/VTS-Template.raku/scratchpad/./outer-EVAL.raku:10 ------> EVAL "say ⏏$bar";
我推测该问题和这类方式创建的符号在PseudoStash中不可用有关,但我也不确定是否正确。
outer.raku
#!/usr/bin/env raku package Foobar { our $foo = 'foo'; our sub outer { say OUTER::; } } Foobar::<$bar> = 'bar'; say $Foobar::bar; Foobar::outer; .say for Foobar::;
运行结果:
$ ./outer.raku bar PseudoStash.new(($?PACKAGE => (Foobar), $_ => (Any), $foo => foo, &outer => &outer, ::?PACKAGE => (Foobar))) &outer => &outer $bar => bar $foo => foo
如运行结果所示,$Foobar::bar存在于Foobar::的Stash中,但不在OUTER::对应的PseudoStash里。因此我有两个疑问:
- 为什么运行时创建的符号无法被
EVAL访问? - 为什么运行时创建的符号在
PseudoStash中不可用?
解答
首先你遇到的第一个编译报错和EVAL本身、动态符号都无关,是字符串插值的低级错误:你给EVAL传的是双引号包裹的字符串,Raku编译外层代码时会先对双引号内的$bar做变量插值,此时$bar在Foobar::eval的词法作用域中没有定义,所以还没到EVAL运行阶段,外层代码的编译就直接失败了。
把双引号换成单引号避免提前插值,再给EVAL指定包上下文即可正常访问动态创建的符号:
#!/usr/bin/env raku use MONKEY-SEE-NO-EVAL; package Foobar { our $foo = 'foo'; our sub eval { # 单引号避免提前插值,指定PACKAGE参数让EVAL在Foobar包上下文编译代码 EVAL 'say $bar', :PACKAGE(Foobar); } } Foobar::<$bar> = 'bar'; Foobar::eval; # 正常输出 bar
关于PseudoStash的问题:PseudoStash是编译期生成的作用域快照,在你定义Foobar::outer、Foobar::eval这些子例程的时候,Raku编译器已经捕获了当时Foobar包的符号表状态,生成对应的PseudoStash实例绑定到这些子例程的OUTER::上下文。后续你在运行时动态往Foobar的Stash里添加$bar,只会更新包的实际符号表,不会回溯修改之前已经生成的所有PseudoStash实例,所以你在子例程中打印OUTER::时看不到后续添加的动态符号。
内容的提问来源于stack exchange,提问作者JustThisGuy
相关产品推荐
相关产品推荐

