无法在 CLASS 中使用带有 WHERE 子句的无符号变量?

che*_*nyf 8 predicate where-clause raku

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

unit class A;

my \MAXVALUE = 1000_000;

has $.c where 0 < * < MAXVALUE;

method gist() {
    "$!c";
}
Run Code Online (Sandbox Code Playgroud)

设置RAKULIB环境变量:

set RAKULIB="d:\scripts\Raku\A\lib"      # Windows
export RAKULIB="/opt/scripts/Raku/A/lib" # Linux
Run Code Online (Sandbox Code Playgroud)

像这样使用模块A:

use A;

my $a = A.new(c => 1);
say $a;
Run Code Online (Sandbox Code Playgroud)

但得到类型检查错误:

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

这是一个错误吗?

乐库版本:

raku -v
This is Rakudo version 2020.05.1 built on MoarVM version 2020.05
implementing Raku 6.d.
Run Code Online (Sandbox Code Playgroud)

rai*_*iph 7

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

在您的代码中,在编译时MAXVALUE使用该值Mu进行初始化。你必须改变你的代码,这样的评价... < MAXVALUE自带之后 MAXVALUE已经初始化到带以外的值Mu

在这个答案的其余部分:

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

  • 如果您想使用运行时值怎么办?

  • 幕后发生了什么?

  • 错误消息是 LTA 吗?

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

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

也就是说,如果您希望保持符号纯粹是词法作用域,您应该从 a 开始my

my constant MAXVALUE = 1000_000;
Run Code Online (Sandbox Code Playgroud)

问题解决了。

如果您想使用运行时值怎么办?

一中的变量/符号/表情where条款将被计算在编译时,如果他们是在一个WhateverCode表达

但如果您使用 lambda(带语法),情况可能并非如此{ ... }。如果您的代码中的行:

has $.c where 0 < * < MAXVALUE;
Run Code Online (Sandbox Code Playgroud)

改为:

has $.c where { 0 < $_ < MAXVALUE }
Run Code Online (Sandbox Code Playgroud)

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

但...

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

has $.c where { 0 < $_ < MAXVALUE } = 10;
                                    ^^^^ Explicit initialization
Run Code Online (Sandbox Code Playgroud)

...然后错误将返回,因为现在where由于连锁反应,该子句在编译时被调用:

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

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

  • 这反过来会导致MAXVALUE被评估——它Mu在编译时包含,导致错误返回。

幕后发生了什么?

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

  • 编译期间,当一个组合时,实例将具有的每个变量的默认has被确定。三种常见的场景是:

    has 陈述 默认值
    has $foo; Any
    has $foo where some-condition; <anon>
    has $foo = 42; 42
  • 运行时,当一个实例类的正在建设中,值has变量的特定实例的设置,可能他们初始化比类的默认值不同。

以下代码旨在演示该过程:

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);
Run Code Online (Sandbox Code Playgroud)

显示:

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)
Run Code Online (Sandbox Code Playgroud)

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

  • bar => Any.

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

  • baz => <anon>.

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

  • buz => 99.

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

    1. 所述第一被后立即显示的行compile-time, at end of class,即,当编译器卷曲类声明的到达关闭。这是 Raku 进行类组合并buz获得默认值的时候42

    2. 然后,在 的评估期间foo.new(buz => 99);在运行时构建类的一个实例,出现。initializing 99

错误消息是 LTA 吗?

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

我自己无法提供连贯的解释......它是否被认为是一个错误。我目前确实考虑了错误消息 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
Run Code Online (Sandbox Code Playgroud)
  • Type check failed ...

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

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

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

    陆路交通局?也许像<where clause>而不是<anon>这样的东西是可行和合适的?

  • expected Any but got Mu (Mu)

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

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

  • in whatevercode

    爆炸发生在 a 中WhateverCode,所以这部分对于知道 aWhateverCode是什么的人很有用。

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

  • at ... A\lib ...

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

    陆路交通局?也许将路径移动到最后会有所帮助:

    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\
          6D\6DCD4CE23D88E2EE9568BA546C007C63D9131C1B)
    
    Run Code Online (Sandbox Code Playgroud)
  • ... line 1

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

    陆路交通局?也许更清楚地说明第 1 行所指的是什么是可行和适当的,如果它指的是A\lib,则使其准确地指向whatevercode.

  • in block <unit> at line 3

    这很有用。