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
| [reply] [d/l] [select] |
|
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.)
| [reply] [d/l] |
|
$ perl -wle " 0+$foo; LABEL: for(1,2){ eval { next LABEL; }; }"
Useless use of addition (+) in void context at -e line 1.
Name "main::foo" used only once: possible typo at -e line 1.
Use of uninitialized value $foo in addition (+) at -e line 1.
Exiting eval via next at -e line 1.
Exiting eval via next at -e line 1.
$ perl -wle " no warnings q[exiting]; 0+$foo; LABEL: for(1,2){ eval {
+ next LABEL; }; }"
Useless use of addition (+) in void context at -e line 1.
Name "main::foo" used only once: possible typo at -e line 1.
Use of uninitialized value $foo in addition (+) at -e line 1.
Related discussions Exiting subroutine via next, #29238: exiting eval via last | [reply] [d/l] |
|
|
|
|
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.
| [reply] [d/l] |
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." | [reply] [d/l] [select] |
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- | [reply] [d/l] |
|
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 { ... };?
| [reply] [d/l] [select] |
|
#!/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- | [reply] [d/l] |
|
|
| [reply] |
|
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.
| [reply] |