Beefy Boxes and Bandwidth Generously Provided by pair Networks
Keep It Simple, Stupid
 
PerlMonks  

Question on "Effective Perl Programming"

by jfroebe (Parson)
on Sep 06, 2007 at 03:02 UTC ( [id://637312]=perlquestion: print w/replies, xml ) Need Help??

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

Hi all :)

Tonight I started and finished reading the Effective Perl Programming: Writing Better Programs with Perl ISBN 0-201-41975-0. In Item 54, page 224, I'm curious as to why the authors are comparing $@ with the empty string ''.

eval { open F1, $fname1 or die "$!"; open F2, $fname2 or die "$!"; # some other stuff ... }; if ($@ ne '') { warn "error in eval: $@\n"; }

I'm thinking the authors did the comparison for readability but my sleepy brain (been up since before time began this morning) is second guessing it. Sort of like when you know the word potato is spelled "p-o-t-a-t-o" but it still looks funny.

Wouldn't it be better to just look for a true $@ (non-zero and non-empty string)?

if ($@) { warn "error in eval: $@\n"; }

Am I suffering from a brain fart?

Jason L. Froebe

Blog, Tech Blog

Replies are listed 'Best First'.
Re: Question on "Effective Perl Programming"
by kyle (Abbot) on Sep 06, 2007 at 03:21 UTC

    According to the eval documentation, "If there was no error, $@ is guaranteed to be a null string." According to True or False? A Quick Reference Guide, the only expressions that would be false but not a null string are undef and 0 (or "0").

    I think the only way there could be an error that is not caught by "if ($@)" would be if you were to throw an exception that uses overload to generate a false value in that case. That is to say, I think "if ($@)" would work in every case except where the code in the eval does something deliberately mean or it has a really unlikely bug.

      Amusingly enough, if you use Exception::Class and throw an error without a message, it has an overload that will stringify to undef, so "if ($@)" will in fact fail. The test against empty string will work though, even in this case. (And by the way, don't throw an Exception::Class error without a message.)

        Maybe I'm not reading your description correctly, or maybe our versions of Exception::Class differ, but I don't see the behavior you're describing. Quite the opposite, in fact. The boolean test sees the exception, but the test against '' can't see it (because it stringifies to '').

        use Exception::Class ( 'MyException' => { description => '' } ); eval { MyException->throw( message => '' ); }; if ($@) { print "I see you, exception class (boolean)!\n"; } else { print "Nothing to see here (boolean).\n"; } if ( $@ ne '' ) { print "I see you, exception class (ne '')!\n"; } else { print "Nothing to see here (ne '').\n"; } __END__ I see you, exception class (boolean)! Nothing to see here (ne '').

        My version is 1.2, and I see this in it:

        use overload # an exception is always true bool => sub { 1 }, '""' => 'as_string', fallback => 1;
Re: Question on "Effective Perl Programming" (;1 > $@)
by tye (Sage) on Sep 06, 2007 at 13:52 UTC

    I personally never test $@ at all. I've seen way too many practical examples of eval failing and yet $@ being left unset so I don't consider using $@ to test whether an eval failed to be wise.

    A more robust approach is:

    my $okay= eval { ....; 1 }; if( ! $okay ) { warn "Error in eval: $@\n"; }

    Here is a quick example of how to make eval fail and leave $@ unset:

    sub DESTROY { # local $@; # Uncomment this line to "fix" this eval { # Something that might fail # And failure should just be ignored here }; } my $okay= eval { my $x= bless []; die "The world is coming to an end!!!\n"; 1 }; if( $@ ) { warn "eval failed obviously: $@\n"; } elsif( ! $okay ) { warn "eval failed silently ($@).\n"; } else { warn "eval succeeded.\n"; }

    which produces "eval failed silently ()." for me.

    I'm a bit disappointed that the authors of "Effective Perl Programming" neglected this technique. You might ask them to have that noted in the errata.

    - tye        

      Ouch! That will eat any useful exception you want to throw too. Consider:

      package EvalEater; sub new { bless {} } sub DESTROY { eval { sleep 1 if 0 } } package main; use Test::More 'tests' => 2; use Exception::Class ( 'Bomb' ); my $okay = eval { my $ee = EvalEater->new(); Bomb->throw( error => 'Ouch!' ); 1; }; ok( ! $@, "Eval appears to succeed: $@" ); ok( ! defined $okay, "Eval failed silently" );

      I see the local $@ will fix the EvalEater, but is there a way to rescue the exception if I can't change EvalEater?

        package DESTROY::EVAL_ERROR::NOT; my %done; sub import { shift @_; for my $pkg ( @_ ) { next if $done{$pkg}++; $pkg .= "::DESTROY"; my $orig= \&{$pkg}; no warnings; *{$pkg}= sub { local $@; $orig->(@_) }; } }

        Then:

        use Broken::Package; use DESTROY::EVAL_ERROR::NOT qw( Broken::Package ); ...

        In other words, "if I can't change EvalEater" need never be true. :)

        - tye        

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others chilling in the Monastery: (3)
As of 2024-04-25 23:45 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found