首页 文章

为什么Perl 6会为我的子集类型抛出X :: AdHoc异常?

提问于
浏览
8

这是Perl 6中报告的错误:X::AdHoc instead of X::TypeCheck::Binding with subset parameter,于2015年11月首次报道 .


在玩我的Perl 6模块Chemisty::Elements时,我遇到了一个我没想到的Exception问题 .

我定义了一个类型 ZInt ,它将数字限制在周期图表上找到的序数(我在这里伪造了一点) . 然后我使用该类型将参数约束到子例程 . 我希望得到某种X::TypeCheck,但我得到X::AdHoc代替:

use v6;

subset ZInt of Cool is export where {
    state ( $min, $max ) = <1 120>;
    ( $_.truncate == $_ and $min <= $_ <= $max )
        or warn "Z must be between a positive whole number from $min to $max. Got <$_>."
    };

sub foo ( ZInt $Z ) { say $Z }

try {
    CATCH {
        default { .^name.say }
        }

    foo( 156 );
    }

首先,我得到两次警告,这很奇怪:

Z必须介于1到120之间的正整数之间 . <156> . 在zint.p6第5行的块中,Z必须在从1到120的正整数之间 . 得到<156> . 在块zint.p6第5行X :: AdHoc

但是,当我宁愿人们知道这是一个类型错误时,我得到 X::AdHoc 类型 .

我检查了没有 warn 会发生什么,并再次得到 X::AdHoc

subset ZInt of Cool is export where {
    state ( $min, $max ) = <1 120>;
    ( $_.truncate == $_ and $min <= $_ <= $max )
    };

所以,我想我可以抛出自己的异常:

subset ZInt of Cool is export where {
    state ( $min, $max ) = <1 120>;
    ( $_.truncate == $_ and $min <= $_ <= $max )
        or X::TypeCheck.new.throw;
    };

但是,我得到一个警告:

在字符串上下文中使用类型为Any的未初始化值 . ^ name,.perl,.gist或.say可以根据需要对未定义的内容进行字符串化 .

在这一点上,我不抱怨 . 我认为其中一种方法需要我在文档中看到 newthrow 的参数 .

如何在没有警告的情况下获得我想要的类型以及我的自定义文本?

2 回答

  • 3

    不要抛出异常或与之发出警告 . 相反,你想失败:

    subset ZInt of Cool is export where {
        state ( $min, $max ) = <1 120>;
        ( $_.truncate == $_ and $min <= $_ <= $max )
            or fail "Z must be between a positive whole number from $min to $max. Got <$_>."
    };
    

    我相信这是你的意图 . 没有你自己的异常也没关系,但是X :: TypeCheck中有一个bug . 它应该要么“操作”要么提供合理的默认,就像它“得到”和“预期”一样 .

    subset ZInt of Cool is export where {
        state ( $min, $max ) = <1 120>;
        ( $_.truncate == $_ and $min <= $_ <= $max )
        or fail X::TypeCheck.new(
                operation => "type check",
                expected  => ::('ZInt'),
                got       => $_,
            );
    };
    
  • 3

    您可以通过 --ll-exception 并尝试弄清楚您最终得到的错误和消息,但我不确定它会有多大帮助 .

    关于使用未初始化值的警告:您需要为 X::TypeCheck.new 提供一个名为 operation 的参数;您可能提供的其他参数是 gotexpected ,cf core/Exception.pm .

    然而,从子集声明中抛出是一个坏主意,因为针对该特定类型的任何智能匹配现在都会爆炸 . 稍微好一点的想法是 .fail 异常,但这对我来说仍然不合适:不是子集类型的成员不是一个例外情况 .

    或者,您可以提供一个完成死亡的多候选人:

    subset ZInt of Cool where $_ %% 1 && $_ ~~ 1..120;
    
    proto foo($) {*}
    multi foo(ZInt $Z) { say $Z }
    multi foo($Z) {
        die X::TypeCheck.new(
            operation => 'foo',
            got => $Z,
            expected => ZInt
        );
    }
    

    如果您提供像 "hello" 这样的参数在数字转换时失败仍然会出现问题,因为 %% 将抛出而不是传播失败,这可能被认为是Rakudo核心设置的缺陷 .

    你可以通过类似的东西解决这个问题

    subset ZInt of Cool where { try $_ %% 1 && $_ ~~ 1..120 }
    

    要么

    subset ZInt of Cool where { .Numeric andthen $_ %% 1 && $_ ~~ 1..120 }
    

    参数类型检查,子集或where子句,失败和异常的整个交互可能有点脆弱,因此您可能需要进行一些实验,直到达到您喜欢的语义和行为 .

    另一种方法是从 CoolInt 进行强制执行,并进行单独的范围检查:

    subset ZInt of Int where 1..120 ;
    
    sub foo(Int(Cool) $Z where ZInt) {
        say $Z.perl;
    }
    

    在一个理想的世界中,应该有一些方法来表达这种强制类型约束,如 ZInt(Cool) .

相关问题