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

What does eval catch? A function that issues a die? A function that just returns an error?
for instance when using LWP.do you wrap the response object with eval? Does it die on a timeout which you can catch?
Should you always wrap in eval functions imported from modules?
Or builtins.For instance ,open , returns undef when failing but it doesn't kill the rest of the program due to the 'exception'.
Should you wrap it in eval and will eval catch the undef return or it would catch it in case open would also fire a 'die'?

Replies are listed 'Best First'.
Re: When to use eval
by hippo (Archbishop) on Feb 01, 2024 at 17:22 UTC
    What does eval catch? A function that issues a die?

    It allows the execution of code which may error (ie. die) without terminating the enclosing running instance.

    $ perl -E 'eval { die "Ay, caramba!"; }; say "foo"' foo
    For instance ,open , returns undef when failing but it doesn't kill the rest of the program due to the 'exception'.

    It does if you use autodie; so you might use a block eval in that situation.

    Note that there are cleaner ways to trap and check for exceptions. In a recent enough perl you can use try. For older perls there are modules such as TryCatch, Try::Tiny, etc.


    🦛

      >which may error (ie. die)
      what other conditions except of 'die' can eval trap?
      And autodie does consider 'undef' as an exception?
      There's also a Fatal module I think.What is it's difference with 'autodie'?
        what other conditions except of 'die' can eval trap?

        Depends what you count as "except of 'die'". It will trap other run-time errors which would be fatal such as de-referencing non-references or failures of require, etc.

        The documentation for eval is pretty extensive. All the answers you seek should be there.

        And autodie does consider 'undef' as an exception?

        Not explicitly. See autodie.

        here's also a Fatal module I think.What is it's difference with 'autodie'?

        You haven't read the docs for Fatal either then?

        Fatal has been obsoleted by the new autodie pragma. Please use autodie in preference to Fatal. autodie supports lexical scoping, throws real exception objects, and provides much nicer error messages.

        🦛

        I have never used autodie, but my understanding that it just makes functions that normally return false on failure die instead. So if you have this code:

        open my $fh, '<', 'filename.txt';

        ...and you have autodie in use, the code will behave as if it were written like this:

        open my $fh, '<', 'filename.txt' or die $!;

        Myself, I prefer to be able to see within the code if something is designed to die explicitly or not so I would never use such a contraption as autodie.

Re: When to use eval
by eyepopslikeamosquito (Archbishop) on Feb 01, 2024 at 20:28 UTC

    hippo has supplied excellent answers to your specific questions. I suggest you now take some time to read and understand the concepts and history behind Perl error handling:

    Anything that remains unclear, please ask.

    👁️🍾👍🦟
Re: When to use eval
by stevieb (Canon) on Feb 01, 2024 at 18:36 UTC
    Should you always wrap in eval functions imported from modules?

    Only if you expect exceptions from it, AND you want to catch that exception and do something (such as allow the program to continue on) instead of having the program crash.

    What does eval catch? A function that issues a die? A function that just returns an error?

    It catches exceptions. A function that returns an error is not an exception. Since it's returning something, it was programmed to do that. It didn't crash. If code returns an error status, it means that although there was an error, it wasn't fatal, and now your code must decide what to do based on the error.

    When someone puts a die or croak or whatever into their code, it typically means that "my code was not designed to handle this situation, and my code can't continue to function properly so I'm bailing out immediately". It's up to you to decide whether the failure of someone elses code will prevent yours from continuing to do what it needs to do. If the underlying code dies, but your code can follow another code path without the results of that code, then use eval to catch that situation, and direct your code appropriately.

    Here are a couple of examples. This first one runs a while loop that tries to create a shared memory segment using random keys until it can create one. If it tries to create a segment but it collides with an existing one, the underlying code will die as that's definitely a fatal error. What this code does is it uses eval in block fashion. If the call to create the segment fails, the eval returns undef (ie. false) which re-triggers the loop. If the segment creation is successful, the eval returns 1 (ie true) and the loop terminates.

    my $shared_memory_segment_created = 0; while (! $shared_memory_segment_created) { $shared_memory_segment_created = eval { tie %events, 'IPC::Shareable', { key => _rand_shm_key(), create => 1, exclusive => 1, protected => _shm_lock(), mode => 0600, destroy => 1 }; 1; }; }

    In this next example, I don't use the return value from eval. I don't need to because I later check for the existence of an imported function. What I'm doing here is attempting to require a library and import a specific function. I wrap it in eval in case the module doesn't exist. After the eval block, I check to see if the trace() function loaded or not. If it didn't, I just generate one that does nothing. This allows me to have trace() calls within the code even if the module that contains it isn't available on the system.

    eval { require Devel::Trace::Subs; import Devel::Trace::Subs qw(trace); }; if (! defined &trace){ *trace = sub {}; }

    The most common way I use eval is like this:

    my $thing_ok = eval { do_something(); 1; }; if (! $thing_ok) { # code in eval failed, so return was undef if ($@ =~ /bad parameter/) { # if the die statement died because of a bad param, warn the u +ser # otherwise carry on silently warn $@; } }

    Blindly wrapping imported subs with eval is a very bad idea. You'll never know if any failures are generated if you don't check, and that could lead to all manner of undefined behaviour and very difficult to track down bugs. IMHO, eval should only be used when you absolutely expect specific failures, and you have code ready to handle them.

    Update: Another very important use case for using eval is to allow cleanup routines to do their job. A die crashes the program, and if any cleanup code is required, it won't run. An example would be the DESTROY method for an object. Sometimes cleanup is very important... resetting GPIO on hardware based systems, cleaning up shared memory segments or temporary files etc.