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

Hello everybody

Yesterday, debugging a program I was writing, I thought I spotted a problem in an eval.

It is a common practice to trap exceptions with code blocks like the following:

eval { # some perl code here . . . } if ($@) { print "eval failed: $@\n\n" ; }

and the one I was in was quite similar to the above. I went back to the documentation and double checked if I remembered exactly eval's syntax. A small section made a bell sound in my head:

If there is a syntax error or runtime error, or a "die" + state- ment is executed, an undefined value is returned by "ev +al", and $@ is set to the error message. If there was no error, + $@ is guaranteed to be a null string.

So, actually, a failed eval returns undef and sets $@. Ok, but...

The question is: it is easy to have an eval succeed (hence setting $@ and return undef, but is it possible to have a failing eval to return a null $@?

Ciao!
--bronto


The very nature of Perl to be like natural language--inconsistant and full of dwim and special cases--makes it impossible to know it all without simply memorizing the documentation (which is not complete or totally correct anyway).
--John M. Dlugosz

Replies are listed 'Best First'.
Re: seeking eval failures
by broquaint (Abbot) on Aug 18, 2003 at 09:20 UTC
    It is possible, but not easily, and not definitively. If you die with an object (v.5.6+) that is overloaded to return undef in the appropriate contexts e.g
    ## warning - segfaults in 5.6.1 { package UndefError; use overload ( q[""] => sub { undef }, bool => sub { undef }, '0+' => sub { undef }, fallback => 1 ); sub new { bless [] } } eval { die UndefError->new; }; warn "ack - $@" if $@; warn "object error - $@" if ref $@; __output__ object error - at - line 19.
    So it can be emulated in some respects, but only through very convoluted means :)
    HTH

    _________
    broquaint

Re: seeking eval failures
by Abigail-II (Bishop) on Aug 18, 2003 at 09:10 UTC
    is it possible to have a failing eval to return a null $@?

    I don't think so. If an eval fails, a die message will be set, either because of a fatal error, or an explicite die. In the former case, I don't think it ever happens there's an empty message (except from a bug). In the latter case, if the message ends with a newline, it's not empty, and if it doesn't end with a newline, Perl will add the filename and line number the die happened to the message.

    Abigail

Re: seeking eval failures
by adrianh (Chancellor) on Aug 18, 2003 at 10:23 UTC
    The question is: it is easy to have an eval succeed (hence setting $@ and return undef, but is it possible to have a failing eval to return a null $@?

    Yes and no.

    You cannot get $@ set to an actual null string. If you die with '' it will get the filename and line number info appended - just like any other die with a string sans trailing "\n".

    As broquaint points out you can die with an error object that evaluates to false. However, this is (technically) not a null string.

    If you really want to be sure an exception was thrown do:

    if( ref($@) || $@ ne '' ) { ... handle exception ... }

    But this is almost always overkill, unless you expect to be dealing with broken exception mechanisms. I've never come across a false exception that hasn't been a bug.

Re: seeking eval failures (fixed?)
by tye (Sage) on Aug 18, 2003 at 16:11 UTC

    It used to be quite easy to get eval to fail but not have $@ set at the end (simply use eval inside a destructor). Doing some testing, it appears that several of the ways this used to happen have been fixed.

    I'll continue to make the last expression in my evals to be a true value so I can test for failure directly:

    eval "...; 1" or die "Eval failed: $@\n";

                    - tye
Re: seeking eval failures
by CombatSquirrel (Hermit) on Aug 18, 2003 at 09:17 UTC
    How about a nested eval like this:
    #!perl use strict; use warnings; my $s = eval "s/temp/;"; print qq[<$@>] unless ($s); print $/; $s = eval 'my $intern = eval "s/temp/;"; $@ = ""; return $intern;'; print qq[<$@>] unless ($s); __OUTPUT__ <Substitution replacement not terminated at (eval 1) line 1. > <>

    It does seem to work.

      I think you're missing what he's saying. Your code works. However, consider *why* it's working. Suppose I have the following code:

      #!/usr/bin/perl use Data::Dumper; my $a = eval { eval { die; } }; print Dumper($a,$@); __DATA__ output: $VAR1 = undef; $VAR2 = '';

      Look at that. It returns undef and $@ is an empty string just as bronto was looking for. This, however, is because the eval inside did its job. It stopped runtime errors from propogating upwards and returned undef. The outer eval didn't notice a thing and as such happily set $@ to '' and returned the last thing in the block, which was the undef from the eval. As far as what the OP is looking for, I'm with Abigail-II on this one. I don't see any way of getting $@ empty on a bad eval.

      antirice    
      The first rule of Perl club is - use Perl
      The
      ith rule of Perl club is - follow rule i - 1 for i > 1

        Yes, sorry. Seems as if you guys were right. But why exactly does the eval have to fail? Where could you detect the difference? I mean, after all the return value of the outer eval is the same as the one of the inner eval and $@ is empty, as specified.
      But the second eval doesn't fail. It just happens to return a false value. An eval can return anything, including false values (otherwise, eval would not be useful). $@ is the way to check whether an eval succeeded or not.

      Abigail