Beefy Boxes and Bandwidth Generously Provided by pair Networks
go ahead... be a heretic
 
PerlMonks  

Why <c>eval {...};if ($@) { die $@ } else { ...</c> ???

by Jenda (Abbot)
on Apr 03, 2009 at 21:00 UTC ( [id://755338]=perlquestion: print w/replies, xml ) Need Help??

Jenda has asked for the wisdom of the Perl Monks concerning the following question:

I'm probably missing something important here, but ... why would anyone want to write

my $res = eval { something(...); }; if ($@) { die $@; } else { do_something_more(...) } return $res;

I mean ... if the something(...) does throw an exception, the code catches the exception only to rethrow it unchanged (I checked that with die "foo", die "foo\n"; and die $object.), if it doesn't something more is done and the result of that something(...) is returned.

So as far as I can tell it's equivalent to

my $res = do { something(...); }; do_something_more(...) return $res;

Or, if there really is just one expression within the eval{}:

my $res = something(...); do_something_more(...) return $res;

Or not? Am I missing something or is XML::SAX::Base (1.04) cargoculting?

Replies are listed 'Best First'.
Re: Why eval {...};if ($@) { die $@ } else { ...???
by shmem (Chancellor) on Apr 03, 2009 at 21:46 UTC

    Well, they could have written that as

    my $res = eval { something(...); }; die $@ if $@; do_something_more(...);

    But! compare

    use Carp; $SIG{__DIE__} = \&Carp::confess; my $res = foo(); print "ok\n"; sub foo { local $SIG{__DIE__}; die "BANG!"; } __END__ BANG! at - line 8.

    and

    use Carp; $SIG{__DIE__} = \&Carp::confess; my $res = eval { foo() }; die $@ if $@; print "ok\n"; sub foo { local $SIG{__DIE__}; die "BANG!"; } __END__ BANG! at - line 9. at - line 4

    I guess it's not cargo-culting... the eval BLOCK and subsequent die provides an additional frame for Carp to consider.

      Well.. Actually I don't think you have to play tricks with $SIG{__DIE__} to obtain a different result. calling confess() within the something(...) would show that additional frame as well.

      I don't see any reason to want that though. What's that additional frame good for? Apart from maybe causing you to look at that part of the code and thus wasting time. I think the additional frame is at best confusing.

        What's that additional frame good for?

        something() might be in some other module which you don't control. That technique allows you to delay the exit until the very line where you choose to die. An that's precisely what eval BLOCK is all about - delay the die, to mask it or die elsewhere.

Re: Why <c>eval {...};if ($@) { die $@ } else { ...</c> ???
by Your Mother (Archbishop) on Apr 03, 2009 at 23:30 UTC

    That construction has crept into my code a few times and it's been because I was planning on doing something with the exception but ended up just letting it lie because I got sidetracked or went off a'testing from the outside and stopped looking at the code directly. It's easy to forget to do anything about it when it's behaving the same way, mostly, as if it weren't there.

Re: Why <c>eval {...};if ($@) { die $@ } else { ...</c> ???
by moritz (Cardinal) on Apr 04, 2009 at 08:21 UTC

    Actually the eval { ... }; if ($@) { ... } pattern is dangerous. Consider this example:

    use 5.010; use strict; use warnings; sub DESTROY { eval { "no error here" } } eval { my $x = bless {}; die "an error here"; }; say $@ ? "ERROR" : "no error caught";

    This reports no error caught, because $x goes out of scope, the DESTROY method is called, which in turn executes an eval that rests $@. Hooray for global variables. (This is one of the reasons that Perl 6 has context variables instead).

    A safer meme is

    my $success = eval { ...; 1 }

    So back to the question, what's the difference? It's that in the case with eval and if the code doesn't work as expected if some DESTROY method forgot to localize $@.

      Actually, eval without diagnostics after the call and without localizing $@ is what's dangerous here, not the pattern itself. And that doesn't apply to DESTROY only, although not with such catastrophic consequences. Like trampling on $_. Yes, "hooray for global variables"... ;-)

Re: Why <c>eval {...};if ($@) { die $@ } else { ...</c> ???
by repellent (Priest) on Apr 04, 2009 at 02:03 UTC
    Based on an idiom, I have the following as a template to handle timeouts:
    { local $SIG{__DIE__}; local $@ = ""; eval { local $SIG{ALRM} = sub { die("alarm\n") }; alarm(3); chomp($input = <STDIN>); # potentially long operation alarm(0); 1; } or do { die($@) unless $@ eq "alarm\n"; # timed out warn("No answer for three seconds.\n"); $input = ""; } }

    Here, I only want to do_something_more(...) when the alarm is up, but pass on every other $@. So, my if ($@) actually does a little more than your example. Then again, it's subtly different with or do { ... } vs. eval { ... }; if ....

    Hope this helps as an example as to why $@ needs to be mucked around :)

    Update: Localized $SIG{__DIE__}. Thanks, jplindstrom!</c>
      Note that anything that happens to alter $SIG{__DIE__} would break that since the exception string wouldn't be "alarm\n" anymore.

      I've done that myself numerous times when adding a use Carp::Always; for debugging purposes, thereby accidentally the whole test run.

      If you want to be extra super safe, you should do local $SIG{__DIE__}; in the block as well.

      /J

Re: Why <c>eval {...};if ($@) { die $@ } else { ...</c> ???
by Bloodnok (Vicar) on Apr 03, 2009 at 22:19 UTC
    I'm with you on this one, Jenda.

    A user level that continues to overstate my experience :-))

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://755338]
Approved by perrin
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others examining the Monastery: (7)
As of 2024-04-18 15:54 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found