Why does Try::Tiny's "try {}" seem different from "eval {}" for objects created inside the try block?

215 Views Asked by At

I have the following initialisation code in a system tray applet:

use Gtk3 -init;
use Glib::Object::Introspection;

eval {
    Glib::Object::Introspection->setup(
        basename => 'Notify',
        version => '0.7',
        package => "MyProgram::Notify",
        );
};

if ($@) {
    say "no notify because setup failed: $@";
    $use_notify = 0;
} else {
    MyProgram::Notify->init();
}

That code is based upon fdpowermon, but seems to come more or less from the Exception handling examples in the POD of Glib::Object::Introspection.

But perlcritic (at level 3) argues about it:

Return value of eval not tested at line …

So I tried to rewrite that with Try::Tiny:

use Gtk3 -init;
use Glib::Object::Introspection;
use Try::Tiny;

try {
    Glib::Object::Introspection->setup(
        basename => 'Notify',
        version => '0.7',
        package => "MyProgram::Notify",
        );
} catch {
    say "no notify because setup failed: $@";
    $use_notify = 0;
} finally {
    if (!$@) {
        MyProgram::Notify->init();
    }
}

But then perl argues:

Can't locate object method "new" via package MyProgram::Notify::Notification

While I do see that especially the finally block is not a real improvement, I do not understand why using Try::Tiny makes such a difference with regards to the package created by Glib::Object::Introspection.

Or is there a better way than Try::Tiny to make this code more elegant and more readable while keeping perlcritic happy?

2

There are 2 best solutions below

4
On

The whole point of the critique is to avoid checking $@ because it could have been clobbered. Yet after all your changes, you're still checking $@!

Worse, Try::Tiny puts the error in $_, not in $@, and only in catch blocks.

I think what's happening is that MyProgram::Notify->init() is being called when it shouldn't because of the above bugs.

Fix:

my $use_notify = 1;
try {
    Glib::Object::Introspection->setup(
        basename => 'Notify',
        version => '0.7',
        package => "MyProgram::Notify",
    );

    MyProgram::Notify->init();
} catch {
    say "no notify because setup failed: $_";
    $use_notify = 0;
}

or

my $use_notify = 1;
try {
    Glib::Object::Introspection->setup(
        basename => 'Notify',
        version => '0.7',
        package => "MyProgram::Notify",
    );
} catch {
    say "no notify because setup failed: $_";
    $use_notify = 0;
}

MyProgram::Notify->init() if $use_notify;

Without Try::Tiny:

my $use_notify = 1;
if (!eval {
    Glib::Object::Introspection->setup(
        basename => 'Notify',
        version => '0.7',
        package => "MyProgram::Notify",
    );

    MyProgram::Notify->init();

    1;  # No exception
}) {
    say "no notify because setup failed: " . ( $@ // "Unknown error" );
    $use_notify = 0;
}

or

my $use_notify = 1;
if (!eval {
    Glib::Object::Introspection->setup(
        basename => 'Notify',
        version => '0.7',
        package => "MyProgram::Notify",
    );
    1;  # No exception
}) {
    say "no notify because setup failed: " . ( $@ // "Unknown error" );
    $use_notify = 0;
}

MyProgram::Notify->init() if $use_notify;
1
On

Actually the answer to my question is basically the same as this one: A missing semicolon behind the catch (or rather finally) block.

Sorry for the noise.