Почему "try" не вызывает неопределенную ошибку подпрограммы?

Пару раз я сталкивался с ситуацией, когда я забыл загрузить модуль 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. Это заставляет меня задуматься, почему мои повышенные исключения не обнаружены.

Почему это работает тихо, без ошибок?

EDIT

Я также посмотрел в таблицу символов:

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

и не нашел там try. Также я попытался назвать его как main::try в приведенном выше скрипте, и это также не вызвало ошибок.

Ответы

Ответ 1

Это связано с синтаксисом косвенного объекта и является более сложным вариантом этого примера.

"косвенная запись объекта" разрешает код

PackageName->method(@args);

записывается как

method PackageName @args;

Поэтому слова "попробуй" и "поймай" не имеют значения. Интересным моментом здесь является более сложный и расширенный синтаксис, состоящий из двух частей, каждая в этой записи косвенного объекта.

На самом деле рассматриваемый код имеет форму 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() );

Таким образом, исходный код интерпретируется как действительный синтаксис (!), и это содержимое блока после try (call_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 -ing нигде. Запустите его с -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 ...

Итак, теперь проблема с методом "catch" действительно возникает.

Спасибо икегами за комментарии


Если блок выше должен был вернуть имя пакета (или ссылку на объект), у которого есть метод 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.