为什么“尝试"不会导致未定义的子程序错误? [英] Why does "try" not cause an undefined subroutine error?
问题描述
有几次我遇到了忘记在脚本中加载 Try::Tiny
模块的情况,但我仍然使用它的 try-catch
块,像这样:
#!/usr/bin/env perl使用严格;使用警告;尝试 {call_a('x');} 抓住 {死实际上死$ _";};子调用_a {死是的,我会";}
出于某种原因,该脚本运行良好,没有提示没有 try
.没有未定义的子程序
错误.这让我想知道为什么我提出的异常没有被捕获.
为什么这会默默工作,没有错误?
编辑
我也查看了符号表:
对键 %main:: 说$_: %main::{ $_ }";
发现没有try
.我还尝试在上面的脚本中将其称为 main::try
,它也没有引起任何错误.
这是由于间接对象语法,是 这个例子.
间接对象表示法" 允许代码
PackageName->method(@args);
写成
method PackageName @args;
所以try"和catch"这两个词无关紧要.这里有趣的一点是更多涉及和扩展的语法,由两部分组成,每部分都采用这种间接对象表示法.
有问题的代码实际上具有method BLOCK LIST
形式,但也通过间接对象语法转化为(do BLOCK)->method(LIST)
,其中 do BLOCK
需要为有意义的方法调用生成包的名称或受祝福的(对象)引用.这在下面的 Deparse
输出中可以看到.
使用 B::Deparse 编译器后端(通过 O 模块)在此代码上
使用严格;使用警告;使用功能说";试试 { call_a( 'x' ) }抓住 {死真的死了";#say不要死";};子调用_a {死是的,它死了";#说不死";}
as perl -MO=Deparse script.pl
应该显示非常接近的运行内容:
嵌套的间接对象语法对于 Deparse
来说显然太多了,它仍然在输出中留下 method BLOCK LIST
形式.等效的代码可以拼写为
(do { call_a('x') })->try((do { die("ACTUALLY die") })->catch());
在这种情况下更简单
call_a('x')->try( die("ACTUALLY die")->catch());
因此原始代码被解释为有效语法 (!) 并且它是 try
(call_a('x')
) 之后块的内容首先运行 --- 所以程序死了,永远不会去寻找方法"try
.
如果我们把例子改成
会更有趣使用严格;使用警告;使用功能说";试试 { call_a( 'x' ) }抓住 {#die "真的死了";说不要死";};子调用_a {#die "是的,它死了";说不死";}
在任何地方都没有die
-ing.用 -MO=Deparse
运行看看
使用警告;使用严格;使用功能说";尝试 {call_a('x')} (抓住 {说不要死"});子调用_a {使用警告;使用严格;使用功能说";说不死";}undef_sub.pl 语法正常
现在采用直接的 method {} args
语法(args
本身由 Deparse
以间接对象表示法显示为出色地).等效代码为
call_a('x')->try( say("NO DONT die")->catch());
call_a()
首先运行的地方,在它返回之后,接下来运行 try
方法调用中的参数列表的代码.我们没有遇到 die
而实际的运行就像
所以现在方法catch"确实出现了问题.
感谢 ikegami 的评论
<小时>如果上面的块要返回一个包的名称(或对象引用),它有一个方法 catch
那么 try
最终也会尝试
使用严格;使用警告;使用功能说";开始 {包捕获;sub catch { say "In ", (caller(0))[3] };$INC{"Catch.pm"} = 1;};使用捕捉;试试 { call_a( 'x' ) }抓住 {说不要死";抓住";};sub call_a { 说不死"}
现在我们有了等价物
call_a('x')->try(do { say("NO DONT die"); 'Catch' }->catch());
输出
<前>没有死不要死在 Catch::catch 中在 undef_sub.pl 第 14 行,如果没有包或对象引用,则无法调用方法try".A couple of times I've ran into the situation where I've forgotten to load the Try::Tiny
module in my script and I've still used its try-catch
block, like this:
#!/usr/bin/env perl
use strict;
use warnings;
try {
call_a( 'x' );
} catch {
die "ACTUALLY die $_";
};
sub call_a {
die "Yes, I will";
}
For some reason, the script works fine without giving any hints that there is no try
. No Undefined subroutine
errors. This makes me wonder why my raised exceptions are not caught.
Why does this work silently, without an error?
EDIT
I looked into symbol table as well:
say "$_: %main::{ $_ }" for keys %main::;
and found there no try
. Also I tried to call it as main::try
in the script above, and it caused also no errors.
This is due to the indirect-object syntax, and is a more elaborate variation on this example.
The "indirect object notation" allows code
PackageName->method(@args);
to be written as
method PackageName @args;
So the "try" and "catch" words don't matter. The interesting bit here is the more involved and extended syntax, with two parts, each in this indirect object notation.
The code in question in fact has method BLOCK LIST
form, but that also goes by indirect object syntax into (do BLOCK)->method(LIST)
, where do BLOCK
needs to produce a name of a package or a blessed (object) reference for a meaningful method call. This is seen below in Deparse
output.
Using B::Deparse compiler backend (via O module) on this code
use strict;
use warnings;
use feature 'say';
try { call_a( 'x' ) }
catch {
die "ACTUALLY die";
#say "NO DONT die";
};
sub call_a {
die "Yes it dies";
#say "no die";
}
as perl -MO=Deparse script.pl
should show a very close approximation of what runs:
use warnings; use strict; use feature 'say'; try { call_a('x') } do { die 'ACTUALLY die' }->catch; sub call_a { use warnings; use strict; use feature 'say'; die 'Yes it dies'; } undef_sub.pl syntax OK
The nested indirect object syntax is apparently too much for Deparse
which still leaves method BLOCK LIST
form in the output. The equivalent code can be spelled out as
(do { call_a('x') })->try( (do { die("ACTUALLY die") })->catch() );
what in this case is more simply
call_a('x')->try( die("ACTUALLY die")->catch() );
Thus the original code is interpreted as valid syntax (!) and it is the contents of the block after try
(call_a('x')
) that runs first --- so the program dies and never gets to go for the "method" try
.
It gets more interesting if we change the example to
use strict;
use warnings;
use feature 'say';
try { call_a( 'x' ) }
catch {
#die "ACTUALLY die";
say "NO DONT die";
};
sub call_a {
#die "Yes it dies";
say "no die";
}
with no die
-ing anywhere. Run it with -MO=Deparse
to see
use warnings;
use strict;
use feature 'say';
try {
call_a('x')
} (catch {
say 'NO DONT die'
} );
sub call_a {
use warnings;
use strict;
use feature 'say';
say 'no die';
}
undef_sub.pl syntax OK
which is now in a straight-up method {} args
syntax (with args
itself shown by Deparse
in an indirect object notation as well).
The equivalent code is
call_a('x')->try( say("NO DONT die")->catch() );
where first the call_a()
goes and, after it returns, then the code for the argument list in the try
method call runs next. We aren't running into a die
and an actual run goes as
no die NO DONT die Can't call method "catch" without a package or object reference at ...
So now a problem with the method "catch" does come up.
Thanks to ikegami for comments
If the block above were to return a name of a package (or object reference) which does have a method catch
then the try
would finally be attempted as well
use strict;
use warnings;
use feature 'say';
BEGIN {
package Catch;
sub catch { say "In ", (caller(0))[3] };
$INC{"Catch.pm"} = 1;
};
use Catch;
try { call_a( 'x' ) }
catch {
say "NO DONT die";
"Catch";
};
sub call_a { say "no die" }
Now we have the equivalent
call_a('x')->try( do { say("NO DONT die"); 'Catch' }->catch() );
with the output
no die NO DONT die In Catch::catch Can't call method "try" without a package or object reference at undef_sub.pl line 14.
这篇关于为什么“尝试"不会导致未定义的子程序错误?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!