Beefy Boxes and Bandwidth Generously Provided by pair Networks
Your skill will accomplish
what the force of many cannot
 
PerlMonks  

Exiting eval via next: is that so bad?

by jplindstrom (Monsignor)
on Aug 14, 2001 at 19:10 UTC ( [id://104789]=perlquestion: print w/replies, xml ) Need Help??

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

I get the warning "Exiting eval via next at xxx".

But is that such a bad thing, or is it just very unusual? Are there any unexpected side effects that warrant the warning that I should know about? After all, I think I mean what I do :)

/J

Replies are listed 'Best First'.
Re: Exiting eval via next: is that so bad?
by Hofmator (Curate) on Aug 14, 2001 at 19:26 UTC

    Well, you should be aware of the fact that the next from within the eval jumps to the end of an enclosing loop, which might be a little bit surprising. Example:

    my $var = 5; { eval {$var = 3; next}; $var = 4; } print $var; # 3 !!
    The block after eval does not count as a loop block (same as do), so the loop commands next, redo and last work on the outer block. To make the eval block a loop block (so that you can redo it e.g.) double the braces like this:
    eval {{ $i++; redo unless do_sth($i); }}

    -- Hofmator

      Please forgive necromancy.

      In the case I just hit, I'm doing this:

      CLIENT: while (!exitflag) { eval { # Various things that might throw exceptions if (blah blah) { next CLIENT; } }; if ($@) { # Exception handling } }

      So, I'm not just exiting the eval, I'm exiting considerably more. AND I'm doing it by an explicit label, not implicitly, so I really shouldn't be confused about what I'm doing. I'd really prefer not to get this warning in the case when I'm using a loop label to exit a particular loop.

      (The big while loop is handling client connects as they come in, the eval inside it is to prevent unexpected errors from killing the whole server; they just abort the one connection. This is test code, the full version will fork of course, once it can handle the simple case.)

Re: Exiting eval via next: is that so bad?
by bluto (Curate) on Aug 14, 2001 at 19:39 UTC
    The canonical way of getting out of an eval block is to raise an exception with 'die'. If you really want to use 'next', you could try to add another level of braces so that it can jump out of a "real" block. For example...
    eval {{ next; }}
    ... doesn't issue a warning (and doesn't do anything useful either). Even in this case I'd probably use 'last' instead since it implies you are finished doing whatever you were doing in the block, but YMMV.

    bluto Update: Ok, I'm a slow typer so this is redundant. Think about using 'die' instead though.

Re: Exiting eval via next: is that so bad?
by Agermain (Scribe) on Aug 14, 2001 at 19:19 UTC
    Well, if you use next to leave an eval, you're effectively leaving the eval container to get back to your main program or whatever. Logically, a next command should take you to a block in the same container, shouldn't it? I mean, if you write a subroutine with a next command, Perl would expect the next command to refer to a local label. Same with eval commands, I'm guessing. It's like pointing to someone in the Marketing department to get an Engineering task done, which defeats the purpose of encapsulating things into evals and subs. The 'damage' would be limited to confusion when/if things don't work as expected.

    Update: hofmator below gives a better answer, I think. It basically comes down to the fact that eval isn't built specifically for looping, so you should probably use another structure (like do) if possible.

    andre germain
    "Wherever you go, there you are."

Re: Exiting eval via next: is that so bad?
by SimonSaysCake (Beadle) on Jun 26, 2019 at 21:12 UTC

    I happen to have a use case (and you can argue that it might be best refactored and I may just agree with you but for now, it has to be this way) where we have a couple eval closures within a larger loop and there are times we want to call "next" but within the "eval"s.

    I was getting the same error as the OP when I found this question/thread. I then tried the double curly version of "eval" but it doesn't seem to handle $@ the way I want/expect so I tried something else:

    #!/usr/bin/perl use strict; use warnings; my @times = (2, 4, 3, 1, 2, 5); select((select(STDOUT), $|=1)[0]); TIME: for my $time_to_wait (@times) { print "Time to wait is: $time_to_wait\n"; local $@; eval { # Don't handle even numbers. die '__NOT_FATAL__' if !($time_to_wait % 2); this_will_die_if_true($time_to_wait); 1; } or do { if($@ && $@ =~ /__NOT_FATAL__/) { next TIME; } print "Something went boom! ($@)\n"; }; print "Sleeping for $time_to_wait seconds ... "; sleep $time_to_wait; print "Done.\n"; } exit 0; sub this_will_die_if_true { my ($arg) = @_; $arg //= 0; $arg && die "We died!"; return; }

    This does not produce the dreaded "Exiting eval via next at test.pl line 17." type errors and works the way I want but I'm reluctant to use this in production as it just seems, well...icky somehow. Thoughts?

    -s1m0n-
      but I'm reluctant to use this in production as it just seems, well...icky somehow.

      I would be very reluctant too. I'm confused by the example though - why not move the next TIME if !($time_to_wait % 2); outside of the eval? I guess this code might just be representative of some larger piece of code - in that case, isn't it possible to restrict the scope of the eval to just where it is needed, around the code that you don't want to be fatal, e.g. eval { this_will_die_if_true($time_to_wait); 1 } or do { ... };?

        Sorry, I can see why my example is confusing. Here's a slightly better one, more true to the production code:

        #!/usr/bin/perl use strict; use warnings; use Scalar::Util qw(looks_like_number); my @times = (2, 4, 3, 1, 3.5, 2, 5); select((select(STDOUT), $|=1)[0]); TIME: for my $time_to_wait (@times) { print "Time to wait is: $time_to_wait\n"; local $@; eval { # Skip even numbers. die '__NOT_FATAL__' if !($time_to_wait % 2); verify_number($time_to_wait); # Skip numbers less than 3. die '__NOT_FATAL__' if ($time_to_wait < 3); verify_time($time_to_wait); 1; } or do { if($@ && $@ =~ /__NOT_FATAL__/) { next TIME; } print "Something went boom! ($@)\n"; }; print "Sleeping for $time_to_wait seconds ... "; sleep $time_to_wait; print "Done.\n"; } exit 0; sub verify_number { my ($arg) = @_; die "We died!" if !defined $arg; looks_like_number($arg) or die "We died!"; return; } sub verify_time { my ($arg) = @_; die "We died!" if !defined $arg; # Floats not allowed. int($arg) == ($arg / 1) or die "We died!"; return; }

        Now imagine several more "verify" type calls within that same eval. Granted, it could be re-written to use a bunch more separate eval calls thus potentially allowing next calls outside/between them but in production those function calls are all very related and would die in similar ways. So I feel it comes down to what's more simple, cleaner? I think I might give the undermentioned Syntax::Keyword::Try package a try (swidt) since it does seem to support exiting via "next" calls.

        -s1m0n-

      You gotta do what you gotta do. I use this pattern in C++ programming (and of course Perl programming) a fair bit... but not excessively. :-)

      There is now https://metacpan.org/pod/Syntax::Keyword::Try which may allow you to set up your logic in a much cleaner way. In particular, it explicitly supports next/etc as well as return applied to the enclosing stack, unlike eval.

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others chilling in the Monastery: (9)
As of 2024-03-28 09:24 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found