【问题标题】:unable to use Sigilless variables with WHERE clause in CLASS?无法在 CLASS 中使用带有 WHERE 子句的 Sigilless 变量?
【发布时间】:2021-03-02 10:51:06
【问题描述】:

模块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\C6EB86CB837D3BCAAA3D85B66CE337C820700C08\6D\6DCD4CE23D88E2EE9568BA546C007C63D9131C1B 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.

【问题讨论】:

    标签: where-clause predicate raku


    【解决方案1】:

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

    在您的代码中,MAXVALUE 在编译时使用值 Mu 进行初始化。您必须更改您的代码,以便对... &lt; MAXVALUE 的评估 MAXVALUE 已初始化为具有Mu 以外的值。

    在这个答案的其余部分:

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

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

    • 幕后发生了什么?

    • 错误信息是 LTA 吗?

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

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

    也就是说,如果您希望符号保持纯粹的词法范围,您应该以 my 开头:

    my constant MAXVALUE = 1000_000;
    

    问题解决了。

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

    如果where 子句中的变量/符号/表达式位于WhateverCode expression 中,将在编译时评估

    但如果您使用 lambda(使用 { ... } 语法)代替,可能就不是这样了。如果代码中的行:

    has $.c where 0 < * < MAXVALUE;
    

    改为:

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

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

    但是...

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

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

    ...然后错误会返回,因为现在where 子句由于链式反应在编译期间被调用:

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

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

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

    幕后发生了什么?

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

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

      has statement default value
      has $foo; Any
      has $foo where some-condition; &lt;anon&gt;
      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);
    

    显示:

    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 =&gt; Any.

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

    • baz =&gt; &lt;anon&gt;.

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

    • buz =&gt; 99.

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

      1. first 行立即显示在compile-time, at end of class 之后,即当编译器到达类声明的结束花括号时。这是 Raku 进行类组合的时候,buz 获取默认值 42

      2. 然后,在评估foo.new(buz =&gt; 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
    
    • Type check failed ...

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

    • ... in binding to parameter '&lt;anon&gt;';

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

      LTA?也许像 &lt;where clause&gt; 而不是 &lt;anon&gt; 这样的东西可行且合适?

    • expected Any but got Mu (Mu)

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

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

    • in whatevercode

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

      LTA?如果有人知道WhateverCode 是什么,这部分很神秘。我认为in WhateverCode 而不是in whatevercode 将是一个值得改进的地方。也许in WhateverCode (eg '* &lt; 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\
            6D\6DCD4CE23D88E2EE9568BA546C007C63D9131C1B)
      
    • ... line 1

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

      LTA? 让第 1 行所指的内容更清楚是可行且适当的,如果它指的是A\lib,则使其准确指向whatevercode

    • in block &lt;unit&gt; at line 3

      这很有用。

    【讨论】:

    • 感谢您的回答。并使用constant MAXVALUE = 1000_000; 即可。
    • 我认为问题在于,不知何故,整个$.c ... 通过 *.这就是为什么使用显式 lambda 来修复它的原因,因为你划定了它的开始和结束位置......
    • 感谢 jjmerelo,subset C of Int where 0 &lt; * &lt; MAXVALUE; has C $.c; 也证明了。
    • 您不一定需要使用块 lambda 或任何 lambda。 has $.c where 0 &lt; $_ &lt; MAXVALUE; 也可以。 (请注意,由于where 是一个智能匹配,当右侧被评估时,您正在测试的值在$_ 中。)
    • @BradGilbert 我同意。但那是因为变量没有在has 语句中初始化。我正在对我的问题进行更新,以解释我所看到的。
    猜你喜欢
    • 2019-11-21
    • 2019-10-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-18
    • 1970-01-01
    • 2023-03-17
    • 1970-01-01
    相关资源
    最近更新 更多