in reply to Re: Exiting eval via next: is that so bad?
in thread Exiting eval via next: is that so bad?

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 { ... };?

Replies are listed 'Best First'.
Re^3: Exiting eval via next: is that so bad?
by SimonSaysCake (Beadle) on Jul 01, 2019 at 14:50 UTC

    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-
      Now imagine several more "verify" type calls within that same eval.

      Yeah, I figured it was something like that. Personally I still wouldn't use that "eval/die with a fixed string" pattern, and instead maybe wrap each call in an eval, or to make it look a little nicer, perhaps Try::Tiny. Of course, this being Perl, other ways come to mind :-) Say you've got a bunch of verify_* functions that you can't change and whose errors are fatal, but you want nonfatal versions of them:

      use warnings; use strict; # UUT use Scalar::Util qw/looks_like_number/; sub verify_number { my ($arg) = @_; die "not a number" if !defined $arg; looks_like_number($arg) or die "not a number"; return; } # wrap the funcs my @funcs = qw/ verify_number /; for my $f (@funcs) { my $orig = \&{$f}; my $wrapped = sub { eval { $orig->(@_); 1 } }; { no strict 'refs'; *{$f.'_nf'} = $wrapped } } use Test::More tests => 4; # helper sub exception (&) { eval { shift->(); 1 } ? undef : ($@ || die "\$@ was false") } ok !exception { verify_number("3.14") }; like exception { verify_number("foo") }, qr/\bnot a number\b/i; ok verify_number_nf("3.14"); ok !verify_number_nf("foo");

      Of course you don't need to install the wrapped versions into the symbol table, a hash would work fine too.