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

w.k*_*w.k 13 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";
}
Run Code Online (Sandbox Code Playgroud)

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

为什么这会无声地工作而没有错误?

编辑

我也查看了符号表:

say "$_: %main::{ $_ }" for keys %main::; 
Run Code Online (Sandbox Code Playgroud)

没有发现try。我也尝试像main::try上面的脚本一样调用它,它也没有引起错误。

zdi*_*dim 10

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

“间接对象符号”允许代码

PackageName->method(@args);
Run Code Online (Sandbox Code Playgroud)

被写成

method PackageName @args;
Run Code Online (Sandbox Code Playgroud)

因此,“ 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";
}
Run Code Online (Sandbox Code Playgroud)

作为perl -MO=Deparse script.pl应该呈现什么运行一个很接近的近似:

使用警告;
使用严格
使用功能“说”;
尝试{
    call_a('x')
}做{
    死了“实际上死了”
}->抓住;
子call_a {
    使用警告;
    使用严格
    使用功能“说”;
    死“是死”;
}
undef_sub.pl语法确定

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

(do { call_a('x') })->try( (do { die("ACTUALLY die") })->catch() );
Run Code Online (Sandbox Code Playgroud)

在这种情况下更简单

call_a('x')->try( die("ACTUALLY die")->catch() );
Run Code Online (Sandbox Code Playgroud)

因此,原始代码被解释为有效的语法(!),并且之后的块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";
}
Run Code Online (Sandbox Code Playgroud)

没有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
Run Code Online (Sandbox Code Playgroud)

现在采用简单method {} args语法(args本身也Deparse以间接对象表示法显示)。等效代码是

call_a('x')->try( say("NO DONT die")->catch() );
Run Code Online (Sandbox Code Playgroud)

首先运行call_a(),然后返回,然后在try方法调用中运行参数列表的代码。我们没有遇到die,实际的运行

没有死
不死
在没有包或对象引用的情况下无法调用方法“ catch”

因此,现在出现了“捕获”方法的问题。

感谢池上的评论


如果上面的块返回确实具有方法的包(或对象引用)的名称,catchtry最后也将尝试

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" }
Run Code Online (Sandbox Code Playgroud)

现在我们有等效的

call_a('x')->try( do { say("NO DONT die"); 'Catch' }->catch() );
Run Code Online (Sandbox Code Playgroud)

与输出

没有死
不死
在Catch :: catch中
在undef_sub.pl第14行中没有包或对象引用的情况下,无法调用方法“ try”。