在运行时在 Stash 中创建的符号在 Raku 的 PseudoStash 中不可用

这个问题始于我试图弄清楚为什么在运行时创建的符号对EVAL.

外-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";

认为这与以这种方式创建的符号在PseudoStashs 中不可用有关。但我可能是错的。

外乐

#!/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符号对PseudoStashs不可用,为什么在运行时创建的符号对s不可用?

回答

什么时候回答是 nanswer?

虽然我很高兴我写了这个,但我对它不满意,因为我得出的结论是你问题的核心,即为什么没有办法EVAL违反词汇符号在期间冻结的原则汇编。

Aiui 的基本原理归结为 A) 避免危险的安全漏洞,以及 B) 在某些MONKEYpragma下允许违规被认为是不值得的。

但是,这是否准确,并讨论它,以及 Rakudo 插件可能导致违反词汇原则的任何可能性,都远远超出了我的薪水。

我很想将此答案复制到要点中,从对您的问题的评论中链接到它,然后删除此答案。

或者,如果您同意我的答案中有这个大漏洞,也许您会善意地不接受它,然后我可以悬赏以尝试从 jnthn 那里得到答案,和/或鼓励其他人回答?

快速修复

  1. package Foo {
    our sub outer {
    EVAL 'say $Foo::bar'  # Insert `Foo::`
    }
    }
    $Foo::bar = 'bar';
    Foo::outer; # bar
    

或者:

  1. package Foo {
    our $bar;          # Create *two* symbols *bound together*
    our sub outer {
    EVAL 'say $bar'  # Use *lexical* symbol from *lexical* stash
    }
    }
    $Foo::bar = 'bar';   # Use *package* symbol from *package* stash
    Foo::outer;          # bar
    

    (阅读jnthn对一个相当不相关的 SO 问题的回答的开头部分(直到“所以:our $foo = 42;正在这样做:(my $foo := $?PACKAGE.WHO<$foo>) = 42;”)可能会有所帮助。)

对您问题的初步回答

他们可用的。

但:

因此,例如,给定在运行时在包中$foo::bar = 42;声明符号的语句:$barfoo

然后,要引用$bar您必须编写的符号$foo::bar(或使用其他形式的包限定引用之一,例如foo::<$bar>)。不允许将其称为 just $bar


PseudoStash语言/编译器使用内置类型在编译时存储词法符号。


有关词法范围/符号/存储之间这种区别的基本原理的讨论(以及our声明符对两者的使用等并发症;以及可以创建词法包的事实),请参阅和之间有什么区别?my classour class.

关于藏匿处

有两种内置的存储类型:

词汇隐藏

这些由语言/编译器管理并在编译结束时冻结。


您可以添加全新的词法存储,并添加到现有的词法存储中,但只能通过在语言的精确控制下使用语言结构,例如:


可以参考词法储物箱,以及其中的符号,通过使用各种“伪包” [3]MYOUTERCORE,和UNIT


您可以使用与词法存储关联的伪包分配绑定到这些词法存储中的现有符号:

my $foo = 42;
$MY::foo = 99;    # Assign works:
say $foo;         # 99
$MY::foo := 100;  # Binding too:
say $foo;         # 100

但这是您唯一可以做的修改。您不能以其他方式修改这些隐藏或它们包含的符号:

$MY::$foo = 99;          # Cannot modify ...
BEGIN $MY::foo = 99;     # Cannot modify ...
my $bar;
MY::<$bar>:delete;       # Can not remove values from a PseudoStash
BEGIN MY::<$bar>:delete; # (Silently fails)

EVAL坚持认为不合格的符号(没有::在引用中,所以像 plain 这样的引用$bar)是词法符号。(有关基本原理,请参阅我在开头附近链接的 SO。)

包裹藏匿处

包存储由语言/编译器根据用户代码根据需要创建。


与词法存储一样,您可以通过一些“伪包”名称来引用包存储。

这个... 指的是与...相关联的藏匿处
OUR:: 在其中的范围OUR出现
GLOBAL:: 口译员
PROCESS:: 解释器运行的进程

由于语言结构(例如声明)的隐含含义,可以将符号添加到包存储中our

our $foo = 42;

这会向与最内部封闭词法范围对应$foo词法存储和与该范围对应的存储添加一个符号:

say $foo;      # 42  (Accesses `$foo` symbol in enclosing *lexical* stash)
say $MY::foo;  # 42  (Same)
say $OUR::foo; # 42  (Accesses `$foo` symbol in enclosing *package* stash)

与词法存储不同,包存储是可修改的。继续上面的代码:

OUR::<$foo>:delete;
say $OUR::foo; # (Any)
$OUR::foo = 99;
say $OUR::foo; # 99

所有这些都使词汇存储保持不变:

say $foo;      # 42
say $MY::foo;  # 42

由于用户代码的隐含含义,也可以添加包隐藏:

package Foo { my $bar; our $baz }

假定声明符之前没有范围声明符(例如myour)。因此上面的代码将:packageour

因此,尽管最初有任何惊喜,但现在这有望变得有意义:

package Foo { my $bar; our $baz }
say  MY::Foo;        # (Foo)
say OUR::Foo;        # (Foo)
say  MY::Foo::.keys; # ($baz)
say OUR::Foo::.keys; # ($baz)

词法存储中的Foo符号值与存储中的值完全相同。该值绑定到另一个存储,通过aka访问。MY OUR Foo.WHOFoo::

因此,MY::Foo::.keysOUR::Foo::.keys列出相同的符号,即 just $baz,它位于Foo包的存储中。

没有看到,$bar因为它在词法存储中,它对应于与Foo包相同的周围范围,但仍然是一个不同的存储。更一般地,你不能看到$bar外面是支撑代码,因为一个关键乐的设计元素是用户和编译器可以依靠纯词法范围符号为100%密封,由于其性质的词汇。


虽然您甚至无法从其词法范围之外看到任何词法符号,但您不仅可以从任何可以访问与其封闭包对应的符号的地方看到而且可以修改任何符号:

package Foo { our sub qux { say $Foo::baz } }
$Foo::baz = 99;
Foo::qux; # 99

$Foo::Bar::Baz::qux = 99;如有必要,类似的行将自动激活任何不存在的包存储,然后可以使用包存储引用(例如伪包OUR[4])查看这些信息:

$Foo::Bar::Baz::qux = 99;
say OUR::Foo::.WHAT;           # (Stash)
say OUR::Foo::Bar::.WHAT;      # (Stash)
say OUR::Foo::Bar::Baz::.WHAT; # (Stash)
say $Foo::Bar::Baz::qux;       # 99

EVAL 如果对它们的引用适当限定,将很乐意使用在运行时添加的符号:

package Foo { our sub bar { say EVAL '$OUR::baz' } }
$Foo::baz = 99;
Foo::bar; # 99

脚注

[1]他们被称为“储物箱”,因为他们是小号ymbol牛逼能^ hES。

[2]抽象概念“符号”被实现为Pair存储在 stash 中的 s。

[3]术语“伪包”可能有点不幸,因为其中一些是Stashs 而不是PseudoStashs 的别名。

[4]对于Foo::Bar不以 sigil 开头但包含 的引用之类的引用::,您需要确保遵守 Raku 解决此类引用的规则。我仍在弄清楚这些到底是什么,并打算在确定后更新此答案,但我决定在此期间按原样发布此答案。


以上是在运行时在 Stash 中创建的符号在 Raku 的 PseudoStash 中不可用的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>