为什么 Perl 6 会为我的子集类型抛出 X::AdHoc 异常?
Why does Perl 6 throw an X::AdHoc exception for my subset type?
这是 Perl 6 中报告的错误:X::AdHoc instead of X::TypeCheck::Binding with subset parameter,首次报告于 2015 年 11 月。
在玩我的 Perl 6 模块时 Chemisty::Elements, I've run into an Exception 出乎我意料的问题。
我定义了一个类型,ZInt
,它将数字限制为周期表上的序数(我在这里伪造了一点)。然后我使用该类型将参数约束到子例程。我希望得到某种 X::TypeCheck, but I get 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 must be between a positive whole number from 1 to 120. Got <156>. in block at zint.p6 line 5
Z must be between a positive whole number from 1 to 120. Got <156>. in block at zint.p6 line 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;
};
但是,我收到警告:
Use of uninitialized value of type Any in string context
Any of .^name, .perl, .gist, or .say can stringify undefined things, if needed.
此时我不知道在抱怨什么。我认为其中一种方法需要一些我没有提供的东西,但我在文档中没有看到任何关于 new
或 throw
的参数。
如何在没有警告的情况下获得我想要的类型以及我的自定义文本?
您可以通过 --ll-exception
并尝试弄清楚您最终是如何得到错误和消息的,但我不确定这会有多大帮助。
关于使用未初始化值的警告:您需要为X::TypeCheck.new
提供一个命名的operation
参数;您可以提供的其他参数是 got
和 expected
,请参见 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 子句、失败和异常的整个交互可能有些脆弱,因此您可能需要进行一些试验,直到获得您喜欢的语义和行为。
另一种方法是通过单独的范围检查从 Cool
强制转换为 Int
:
subset ZInt of Int where 1..120 ;
sub foo(Int(Cool) $Z where ZInt) {
say $Z.perl;
}
在理想的世界中,应该有某种方式来表达这种强制类型约束,例如 ZInt(Cool)
。
不要抛出异常或发出警告。相反,你想失败:
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 中有一个错误。它应该要求 "operation" 或像 "got" 和 "expected".
那样提供合理的默认值
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 => $_,
);
};
这是 Perl 6 中报告的错误:X::AdHoc instead of X::TypeCheck::Binding with subset parameter,首次报告于 2015 年 11 月。
在玩我的 Perl 6 模块时 Chemisty::Elements, I've run into an Exception 出乎我意料的问题。
我定义了一个类型,ZInt
,它将数字限制为周期表上的序数(我在这里伪造了一点)。然后我使用该类型将参数约束到子例程。我希望得到某种 X::TypeCheck, but I get 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 must be between a positive whole number from 1 to 120. Got <156>. in block at zint.p6 line 5 Z must be between a positive whole number from 1 to 120. Got <156>. in block at zint.p6 line 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;
};
但是,我收到警告:
Use of uninitialized value of type Any in string context Any of .^name, .perl, .gist, or .say can stringify undefined things, if needed.
此时我不知道在抱怨什么。我认为其中一种方法需要一些我没有提供的东西,但我在文档中没有看到任何关于 new
或 throw
的参数。
如何在没有警告的情况下获得我想要的类型以及我的自定义文本?
您可以通过 --ll-exception
并尝试弄清楚您最终是如何得到错误和消息的,但我不确定这会有多大帮助。
关于使用未初始化值的警告:您需要为X::TypeCheck.new
提供一个命名的operation
参数;您可以提供的其他参数是 got
和 expected
,请参见 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 子句、失败和异常的整个交互可能有些脆弱,因此您可能需要进行一些试验,直到获得您喜欢的语义和行为。
另一种方法是通过单独的范围检查从 Cool
强制转换为 Int
:
subset ZInt of Int where 1..120 ;
sub foo(Int(Cool) $Z where ZInt) {
say $Z.perl;
}
在理想的世界中,应该有某种方式来表达这种强制类型约束,例如 ZInt(Cool)
。
不要抛出异常或发出警告。相反,你想失败:
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 中有一个错误。它应该要求 "operation" 或像 "got" 和 "expected".
那样提供合理的默认值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 => $_,
);
};