无法在 CLASS 中将无符号变量与 WHERE 子句一起使用?

unable to use Sigilless variables with WHERE clause in CLASS?

模块A有一个成员变量名c,有一个WHERE子句:

unit class A;

my \MAXVALUE = 1000_000;

has $.c where 0 < * < MAXVALUE;

method gist() {
    "$!c";
}

设置RAKULIB环境变量:

set RAKULIB="d:\scripts\Raku\A\lib"      # Windows
export RAKULIB="/opt/scripts/Raku/A/lib" # Linux

像这样使用模块 A:

use A;

my $a = A.new(c => 1);
say $a;

但出现类型检查错误:

Type check failed in binding to parameter '<anon>'; expected Any but got Mu (Mu)
  in whatevercode  at D:\scripts\Raku\Raku\A\lib\.precomp\C6EB86CB837D3BCAAA3D85B66CE337C820700C08DDCD4CE23D88E2EE9568BA546C007C63D9131C1B line 1
  in block <unit> at basic.rakutest line 3

这是一个错误吗?

乐曲版本:

raku -v
This is Rakudo version 2020.05.1 built on MoarVM version 2020.05
implementing Raku 6.d.

Golfed to: BEGIN say 1 < Mu 显示基本相同的错误消息。

在您的代码中,MAXVALUE 在编译时用值 Mu 初始化。您必须更改您的代码,以便 ... < MAXVALUE 的计算在 之后出现 MAXVALUE 已初始化为具有 Mu.

以外的值

在这个答案的其余部分:

  • 最简单的编译时解决方案是什么?

  • 如果要使用运行时间值怎么办?

  • 幕后发生了什么?

  • 错误信息是LTA吗?

最简单的编译时解决方案是什么?

你自己在这个答案下面的评论中提供了一个很好的编译时解决方案,以回应我的第一个版本。

就是说,如果您希望符号保持纯粹的词法作用域,您应该以 my:

开头
my constant MAXVALUE = 1000_000;

问题已解决。

如果要使用 运行 时间值怎么办?

where 子句中的 variables/symbols/expressions 如果它们在 WhateverCode expression 中,将在编译时 计算

但是如果您使用 lambda(使用 { ... } 语法),那么 可能 不是这种情况。如果您的代码中的行:

has $.c where 0 < * < MAXVALUE;

更改为:

has $.c where { 0 < $_ < MAXVALUE }

那么你的代码就可以工作了。

但是...

如果您向 has 行添加显式初始值...

has $.c where { 0 < $_ < MAXVALUE } = 10;
                                    ^^^^ Explicit initialization

...那么错误将是 return 因为现在 where 子句在编译时由于连锁反应被调用:

  • 编译器决定检查初始化值是否通过 where 检查。

  • 为此,编译器会在编译时评估 where 子句

  • 这反过来会导致对 MAXVALUE 求值——它在编译时包含 Mu,导致错误 return.

幕后发生了什么?

初始化has变量时有编译时和运行时两个阶段:

  • 编译时,当class组合时,每个 has 变量的实例将具有的 默认 值已确定。三种常见情况是:

    has statement default value
    has $foo; Any
    has $foo where some-condition; <anon>
    has $foo = 42; 42
  • 运行-time期间,当class的实例是在 built 中,特定实例 has 变量 的值被设置,可能将它们初始化为 class的默认值。

以下代码用于演示该过程:

BEGIN say 'code compile-time, start, outside class';
say 'code run-time, start, outside class';

sub init (*%_) { say "initializing {|%_}" }

class foo {
  has $.bar;
  has $.baz where init(baz => $_);
  has $.buz where init(buz => $_) = 42;
  say 'run-time, at end of class';
  BEGIN say 'compile-time, at end of class';
}
BEGIN say 'compile-time, outside class again';

say 'run-time, outside class again';
say foo.new(buz => 99);

显示:

code compile-time, start, outside class
compile-time, at end of class
initializing buz    42
compile-time, outside class again
code run-time, start, outside class
run-time, at end of class
run-time, outside class again
initializing buz    99
foo.new(bar => Any, baz => <anon>, buz => 99)

注意完整构建实例中三个 has 变量的完整初始化:

  • bar => Any.

    这是变量的通常默认初始化。

  • baz => <anon>.

    cf say my Int $var;显示(Int),因为有类型约束但没有显式初始化值的变量默认值为类型约束对应的类型对​​象,而say my $var where 1; 显示 (<anon>),反映了 where 约束的匿名性质(与 subset 形成对比)。所以 has $.baz where init(baz => $_); 导致默认值 (<anon>)

  • buz => 99.

    这是显示 initializing ... 行的唯一 has 变量 -- 而且,重要的是,有 两行 行,而不是一行:

    1. 第一个行在compile-time, at end of class之后立即显示,即当编译器到达class声明的结束卷曲时。这是Raku做class合成时,buz得到默认值42.

    2. 然后,在评估 foo.new(buz => 99); 期间,它构建了 class 实例 运行-时间,来initializing 99.

错误信息是LTA吗?

我在这个回答的第一个版本中写道:

I'm not myself able to provide a coherent explanation ... whether it's considered a bug. I do currently consider the error message LTA.

现在是我仔细讨论错误信息的时候了:

Type check failed in binding to parameter '<anon>'; expected Any but got Mu (Mu)
  in whatevercode  at ... A\lib ... line 1
  in block <unit> at ... line 3
  • Type check failed ...

    where 检查失败。它被称为“类型检查”。考虑到 Raku 对“类型”一词的正常使用,我认为这很好。

  • ... in binding to parameter '<anon>';

    我不确定“参数”指的是什么,也不知道 '<anon>'。 Imhh(在我的假设中)“参数”是指 infix:«<» 函数的参数,'<anon>' 是指在匿名 where 之前的编译时存储在 $.c 中的值在 运行 时尝试约束。

    LTA? 也许像 <where clause> 而不是 <anon> 这样的东西是可行和合适的?

  • expected Any but got Mu (Mu)

    默认情况下,has 变量需要 Mu,而不是 Any。所以这条消息似乎 而不是 指的是 $.c。因此,与我关于“参数”的假设一样,我认为这是关于 infix:«<» 函数的一个参数。

    这是非常有用的信息。每当您看到 but got Mu (Mu) 时,您都可以非常确定初始化某些值失败是问题的一部分,就像这里的情况一样。

  • in whatevercode

    炸毁发生在 WhateverCode,所以这部分对知道 WhateverCode 是什么的人很有用。

    LTA? 如果有人不知道 知道什么是 WhateverCode,这部分是神秘的。我认为 in WhateverCode 而不是 in whatevercode 将是一个有价值的改进。也许 in WhateverCode (eg '* < 42'),其中 '* < 42' 按字面意思固定,而不是实际源代码,因为我认为那不可行,会更好吗?

  • at ... A\lib ...

    我省略的路径部分 (...) 既有用(完整)又糟糕(长的非人类友好字符串)。

    LTA? 也许将路径移动到最后会有帮助:

    Type check failed ... got Mu (Mu)
      in whatevercode at A\lib line 1
    
      (Full path to A\lib: D:\scripts\Raku\Raku\A\lib\.precomp\
          C6EB86CB837D3BCAAA3D85B66CE337C820700C08\
          6DDCD4CE23D88E2EE9568BA546C007C63D9131C1B)
    
  • ... line 1

    “第 1 行”大概是指 whatevercode 中的第 1 行或 A\lib 中的第 1 行。无论哪种方式,它都不是特别有用。

    LTA? 也许把第 1 行指的更清楚一些是可行的和适当的,如果它指的是 A\lib,那么就准确指向到 whatevercode.

  • in block <unit> at line 3

    很有用。