为什么“尝试”不会导致未定义的子例程错误?

时间:2019-09-18 09:44:27

标签: perl

有几次我遇到了忘记在脚本中加载Try::Tiny模块而仍然使用try-catch块的情况,如下所示:

#!/usr/bin/env perl

use strict; 
use warnings;

try {
  call_a( 'x' );
} catch {
  die "ACTUALLY die $_";
};


sub call_a {
  die "Yes, I will";
}

由于某种原因,该脚本可以正常运行,而没有任何提示try的提示。没有Undefined subroutine错误。这使我想知道为什么未捕获到我提出的异常。

为什么它会无声无息地正常工作?

编辑

我也查看了符号表:

say "$_: %main::{ $_ }" for keys %main::; 

,发现没有try。我也尝试在上面的脚本中将其称为main::try,并且也没有引起错误。

1 个答案:

答案 0 :(得分:10)

这是由于间接对象语法引起的,并且是this example上更为详尽的变体。

"indirect object notation"允许代码

PackageName->method(@args);

写为

method PackageName @args;

因此,“ try”和“ catch”这两个词无关紧要。有趣的是语法更加复杂和扩展,分为两个部分,每个部分都使用间接对象表示法。

所讨论的代码实际上具有method BLOCK LIST形式,但这也通过间接对象语法进入(do BLOCK)->method(LIST)中,其中do BLOCK需要产生一个包名或一个有福的(对象)引用,以进行有意义的方法调用。在下面的Deparse输出中可以看到。

在此代码上使用B::Deparse编译器后端(通过O模块)

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";
}

perl -MO=Deparse script.pl应该显示出运行情况的非常近似值:

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

对于Deparse来说,嵌套的间接对象语法显然太多了,仍然在输出中保留method BLOCK LIST形式。等效代码可以拼写为

(do { call_a('x') })->try( (do { die("ACTUALLY die") })->catch() );

在这种情况下更简单

call_a('x')->try( die("ACTUALLY die")->catch() );

因此,原始代码被解释为有效语法(!),并且{em> trycall_a('x'))之后的块的内容首先运行 ---程序死了,再也没有去寻求“方法” try

将示例更改为

会变得更加有趣。
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";
}

在任何地方都没有die-。使用-MO=Deparse运行它以查看

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

现在采用了一种简单的method {} args语法(args本身也由Deparse用间接对象表示法表示)。 等效代码为

call_a('x')->try( say("NO DONT die")->catch() );

首先call_a()到达的位置,然后返回,然后try方法调用中的参数列表的代码接下来运行。我们没有遇到die,实际的运行情况是

no die
NO DONT die
Can't call method "catch" without a package or object reference at ...

所以现在出现了方法“捕获” 的问题。

感谢ikegami的评论


如果要在上面的块返回的包(或对象引用)的名称确实具有方法catch,则最终也将尝试使用try

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" }

现在我们有等效的

call_a('x')->try( do { say("NO DONT die"); 'Catch' }->catch() );

与输出

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.