在我们的代码库中,我们经常使用此构造来处理程序的选项:
Readonly my $ARGS => Getopt::Declare->new(
join( "\n",
'[strict]',
"--option1 <some-option-description> [required]",
)
) or exit(1);
问题是,当Getopt::Declare->new(...)
中违反严格时,它将返回undef
,这通常会正常,但此undef
存储在 Readonly 变量,在布尔上下文中计算时为 true 。
一个明显的解决方案是删除Readonly
,代码将按预期运行;但我真的不喜欢这个解决方案,因为改变它会允许修改$ARGS
。
my $ARGS => Getopt::Declare->new(
join( "\n",
'[strict]',
"--option1 <some-option-description> [required]",
)
) or exit(1);
另一个解决方案是将Getopt::Declare->new(...) or exit(1)
包装在parens中,以便在分配给 Readonly 变量之前对它们进行评估。
Readonly my $ARGS => (
Getopt::Declare->new(
join( "\n",
'[strict]',
"--option1 <some-option-description> [required]",
)
) or exit(1);
)
我想另一种方法是使用更高优先级的版本或||
,它比=>
强,但我不确定它是否可读。
Readonly my $ARGS => Getopt::Declare->new(
join( "\n",
'[strict]',
"--option1 <some-option-description> [required]",
)
) || exit(1);
答案 0 :(得分:3)
Readonly my $ARGS => Getopt::Declare->new(
# ...
);
$ARGS or exit(1);
应该也可以。
答案 1 :(得分:1)
@choroba建议的直接问题是优先考虑的问题之一:
Readonly my $v => ... or die; # This construct... Readonly(my $v, ...) or die; # is really this... tie my $v, 'Readonly', ... or die; # which is more-or-less this, which evaluates to ... ReadOnly::Scalar=SCALAR(0x123) or die; # the object beneath the tie() ...
因此,您永远不会点击or
的右半部分,因为返回的对象的计算结果为真。
现在,如果你真的想继续使用这种语法,你可以做类似的事情来强制使用tie()d变量而不是底层对象:
use Readonly ();
sub MakeReadonly(\[$@%]@) {
&Readonly::Readonly; # tie() ${$_[0]} to Readonly implementation
${$_[0]}; # ... but return the tied variable, not the object
}
....
MakeReadonly my $e => ... or die; # This ...
MakeReadonly(my $e, ...) or die; # becomes this ...
$e or die; # which is effectively this.
顺便说一句,我建议小心这个结构。 Readonly
的吸引力在于它允许语法看似类似于分配给属性标量,就好像你这样做:
my $v : Readonly = something() or die; # XXX Doesn't work! Not even Attribute::Constant
# works like this, unfortunately.
......但它完全不同。你在帖子的标题和正文中的短语表明你至少最初是这样想的。 (例如,“Readonly变量评估为true” - 好吧,它们的底层对象确实如此,绑定变量本身可能不是。或者,“在分配之前进行评估” - 好吧,你实际上并没有真正分配本身,但又是tie()ing。)