关于 Raku 中 EVAL 中哈希绑定的问题

Question About Hash Binding in EVAL in Raku

我 运行 遇到了一些我无法理解的事情,即在 EVAL 中绑定散列。在 EVAL 之外绑定散列按预期工作。 EVAL 中的未绑定散列按预期工作。但是在 EVAL 中绑定散列并不能像我预期的那样工作。 (我的期望可能是错误的。)这是代码:

这个有效:

#!/usr/bin/env raku

class Hash::Test does Associative {
  has %.hash;

  multi method STORE(@pairs) {
    for @pairs -> $pair {
      self.STORE: $pair
    }
  }

  multi method STORE(Pair $pair) {
    %!hash{$pair.key} = $pair.value;
  }
}

no strict;
%hash-test := Hash::Test.new;
%hash-test = foo => 'bar', baz => 'quux';
say %hash-test;

输出:

$ ./hash-binding-works.raku 
Hash::Test.new(hash => {:baz("quux"), :foo("bar")})

这有效:

#!/usr/bin/env raku

class Foo {
  use MONKEY-SEE-NO-EVAL;

  method eval(Str $code) {
    EVAL $code;
  }
}

my $code = q:to/END/;
  no strict;
  %hash = foo => 'bar', baz => 'quux';
  END

Foo.eval: $code;
say %Foo::hash;

输出:

$ ./hash-EVAL-works.raku 
{baz => quux, foo => bar}

但这不起作用:

#!/usr/bin/env raku

class Hash::Test does Associative {
  has %.hash;

  multi method STORE(@pairs) {
    for @pairs -> $pair {
      self.STORE: $pair
    }
  }

  multi method STORE(Pair $pair) {
    %!hash{$pair.key} = $pair.value;
  }
}

class Foo {
  use MONKEY-SEE-NO-EVAL;

  method eval(Str $code) {
    EVAL $code;
  }
}

my $code = q:to/END/;
  no strict;
  %hash-test := Hash::Test.new;
  %hash-test = foo => 'bar', baz => 'quux';
  say %hash-test;
  END

no strict;
Foo.eval: $code;
say %Foo::hash-test;

输出:

$ ./hash-EVAL-does-not-work.raku 
Hash::Test.new(hash => {:baz("quux"), :foo("bar")})
{}

Hash::Test 不是我正在使用的真正的 class,而是我使用它的结果。谁能解释这里发生了什么?谢谢!

TL;DR no strict; autodeclares package variables via an implicit our declaratorour 通过隐式 my 词法 变量声明符声明包变量,该声明符绑定到具有相同名称的隐式包符号。您的代码破坏了该绑定,而这也破坏了您的代码。要修复它,请换一种说法。

解决方案

no strict; 没有帮助,所以我们摆脱了它。 our 也是如此。相反,我们声明一个 my lexical 变量,做我们 need/can 所做的一切,然后,在代码的末尾将是 EVALd,创建一个包变量并将其绑定到词法中存储的值。

my $code = q:to/END/;
  my %hash is Hash::Test; 
  %hash = foo => 'bar', baz => 'quux';
  OUR::<%hash-test> := %hash;
  END

Foo.eval: $code;
say %Foo::hash-test; # Hash::Test.new(hash => {:baz("quux"), :foo("bar")})

惊喜的解释

no strict; 下没有显式声明符声明的变量隐式声明 our 个变量:

no strict;
%hash-test = :a;
say MY::<%hash-test>;  # {a => True}
say OUR::<%hash-test>; # {a => True}

换句话说,上面前两行的净效果相当于:

our %hash-test = :a;

反过来,our 个变量隐式声明 my 个变量并遵循 中所示的逻辑。所以这段代码:

no script;
%hash-test := ...;

正在这样做:

(my %hash-test := $?PACKAGE.WHO<%hash-test>) := ...;

创建一个 lexical %hash-test 符号和一个 package %hash-test 符号,并绑定它们 - - 哪个绑定 必不可少 our 变量的正常运行 - 然后立即 打破 那个必不可少的绑定。

此后,无论您的其余代码做什么,它都只针对变量的 词法 %hash-test 版本,留下 %hash-test 的符号版本又高又干,所以它稍后会自动生成一个空哈希。


正如 jnthn 在我开头链接的 SO 下方的评论中所说:

We certainly could warn that binding to an our variable is pointless

但是目前没有警告。


正如您在下面的评论中解释的那样,当您尝试使用 %hash-test is Hash::Test 时,编译器神秘地决定您已经写了“连续两个术语”。正如我在评论中解释的那样,这是由于当您使用通常的语法(或隐式地使用 no strict;)声明一个 our 变量时造成的上述欺骗。


要解决以上所有问题,忘记 no strict;,忘记使用 our,而是:

  • 使用词法来设置值;

  • 通过使用 OUR::<%hash-test> 创建包符号并将其绑定到词法的值来结束。