无法在另一个命名空间中插入变量 (Raku)

Jus*_*Guy 9 namespaces string-interpolation raku

我正在尝试使用插值在另一个命名空间中创建一个变量,但在咨询https://docs.raku.org/language/packages#index-entry-::()后无法使其工作。在测试不同的东西时,我发现了一些让我感到困惑的东西。这有效:

#!/usr/bin/env raku

$Foo::bar = 'foobar';
say $Foo::bar;
Run Code Online (Sandbox Code Playgroud)
#!/usr/bin/env raku

$Foo::bar = 'foobar';
say $Foo::bar;
Run Code Online (Sandbox Code Playgroud)

但这不是:

#!/usr/bin/env raku

my $bar = 'bar';
$Foo::($bar) = 'foobar';
say $Foo::bar;
Run Code Online (Sandbox Code Playgroud)
$ ./package-interpolate.raku 
No such symbol '$bar'
  in block <unit> at ./package-interpolate.raku line 4
Run Code Online (Sandbox Code Playgroud)

我错过了什么吗?谢谢!

rai*_*iph 11

TL;DR对于保存在某个特定包(命名空间)中的变量(符号)的引用,有三种不同的语法。您尝试了两个,但看起来您可能需要第三个。这里也有一些皱纹,所以这个答案很长。


这是三种语法的摘要,从可能是(部分)您想要的语法开始,然后是您尝试过的两种:


  • Foo::Bar::Baz::<$qux>

    静态指定包,动态指定变量

    如果嵌套包规范无法解析为相应的一组现有包,则该构造会生成错误。如果包规范解析,则如果变量被分配或绑定到并且不存在,则创建该变量。


  • $Foo::Bar::Baz::qux

    静态指定的包和变量

    如果嵌套包规范中的任何包无法解析为现有包,则会自动创建该包。如果变量被分配或绑定到并且不存在,也会创建该变量。


  • ::('$Foo::Bar::Baz::qux')

    动态指定的包和变量

    如果嵌套包规范无法解析为相应的一组现有包,或者如果变量不存在,则该构造会生成错误。


此答案的其余部分是三个部分,其中包含与上述内容相对应的更多详细信息。

Foo::Bar::Baz::<$qux>

静态指定包,动态指定变量

  • 如果所有包都已经存在,则构造将起作用,如果变量不存在,则创建变量。否则构造将产生一个错误,在编译时是一个合理的错误(如果只指定了一个包)或在运行时是一个 LTA 错误(如果指定了嵌套包)。

  • 可以通过在运行时计算其值的表达式/变量间接指定变量名称。


引用本节标题中链接的文档:

要在不扫描的情况下直接在包的符号表中查找,请将包名称视为散列

这是第一个示例,其中我们引用了非嵌套不存在的包中不存在的变量:

my $bar = '$baz';
say Foo::{$bar};
Run Code Online (Sandbox Code Playgroud)

产生编译时错误:

Undeclared name:
    Foo used at line 2
Run Code Online (Sandbox Code Playgroud)

接下来,一组嵌套的不存在的包中的一个不存在的变量:

my $bar = '$baz';
try say Foo::Bar::Baz::{$bar};
say $!;               # Could not find symbol '&Baz' in 'GLOBAL::Foo::Bar'
say GLOBAL::Foo::Bar; # (Bar)
Run Code Online (Sandbox Code Playgroud)

错误消息(保存在 中的异常$!)不仅仅是 LTA,而是反映了疯狂。并且构造已经创建了包GLOBAL::Foo::Bar。更疯狂!


现在的例子中,我们引用一个包,存在和在其内的变量,它最初存在:

package Foo {}
my $bar = '$qux';
say Foo::{$bar};              # (Any)
say Foo::{$bar}:exists;       # False
Foo::{$bar} = 99;
say Foo::{$bar}:exists;       # True
say Foo::{$bar};              # 99
say Foo.WHO.keys;             # ($qux)
say $Foo::qux;                # 99
Run Code Online (Sandbox Code Playgroud)

因此,Foo::{...}语法(或Foo::<...>,但不是::(...))并没有报错了,如果指定的变量不存在,而是只有当程序包不存在。

此语法中对不存在的变量(在存在的包中)的引用将返回(Any). 如果(Any)分配给它,它会创建变量。

(除非包是MY或未指定,这在此语法中意味着相同的事情。如果MY缺少查找的变量,查找将返回值Nil而不是(Any),并且不能分配或绑定到。)


$Foo::Bar::Baz::qux

静态指定的包和变量

  • 如果你提到一个使用这种语法的包,即使是一个非嵌套的包,如果它不存在,你就会创建它。

  • 如果您分配/绑定到一个变量,如果它不存在,您将创建它。

  • 您必须静态声明要指定的(嵌套)包和变量名称。您不能间接说明(例如通过变量)此语法的任何部分。


引用本节标题中链接的文档:

普通的包限定名称看起来像这样:$Foo::Bar::quux,这将是$quux包中的变量Foo::Bar

这是您问题中的第一个代码片段(对@user0721090601 很惊讶),并附加了一些行。三行的块显示了Foo包的位置(并提供了对本答案和文档中涵盖的三种语法的先睹为快)。最后一行确认$bar变量已添加到Foo包中:

$Foo::bar = 'foobar';
say $Foo::bar;             # foobar

say GLOBAL::Foo;           # (Foo)    Package qualified name yields type object `(Foo)`
say GLOBAL::<Foo>;         # (Foo)    Package qualified lookup (yields same object)
say ::('Foo');             # (Foo)    Package scanning lookup (yields same object)

say GLOBAL::Foo.WHO.keys;  # ($bar)   `.WHO` returns `(Foo)`'s package named `Foo`
Run Code Online (Sandbox Code Playgroud)

GLOBAL是包含“解释器范围的包符号,真的”的“伪”包UNIT::GLOBAL


如果删除上面代码的第一行 ( $Foo::bar = 'foobar';):

  • say $Foo::bar;行的结果是(Any);

  • 添加的三行代码块继续显示(Foo),表明仅在语法形式中提及一个变量$Foo::bar就足以创建包Foo

  • 最后一行显示(),这表明$bar添加到Foo,尽管包装say $Foo::bar;显示线(Any),而不是Nil


::('$Foo::Bar::Baz::qux')

动态指定的包和变量

  • 如果要读取或写入现有变量现有的包,毫无意外地创建他们,如果他们不存在的危险,使用此语法。

  • 相反,如果您希望自动创建包和/或变量,请使用其他两种语法之一或其他一些方法。

  • 您可以在(...)此语法的一部分中使用复杂的表达式,它们将被动态插入,但随后将它们视为静态源代码。因此,你可以做的事情等,包括文字串'$Foo::Bar::Baz::qux'(...),或一个变量,计算结果为这样的字符串,并且编译器将其插入总的参考,其随后用来引导查找,处理之间的每个分量::S作为独特的嵌套包。


引用本节标题中链接的文档:

使用::($expr)您通常放置包或变量名称的位置......查找间接名称......首先优先引导伪包名称,然后是词法范围内的名称(向外搜索范围,以 CORE 结尾) )。最后搜索当前包。

注意,查找将扫描很多的包。


您问题中的第二个代码片段(它不像您预期的那样工作)使用此扫描逻辑进行变量查找,可能不仅查找一个明确指定的包,还查找许多包。

这是相同的代码,但在开头插入了另一行:

package Foo { our $bar }     # <-- This line added
my $bar = 'bar';
$Foo::($bar) = 'foobar';
say $Foo::bar;               # foobar
Run Code Online (Sandbox Code Playgroud)

现在它起作用了。

这表明您的$Foo::($bar) = 'foobar';线路:

  • 看着用于可变$bar在封装Foo。(因此,“插入名称”确实“如文档所宣传的”那样工作。)

  • 但并没有创建一个包。

  • 而且也没有创建一个变量。

  • 但是确实做了分配,因为查找成功找到了与查找匹配的包和变量。