Beefy Boxes and Bandwidth Generously Provided by pair Networks
Problems? Is your data what you think it is?
 
PerlMonks  

Re: conditional catch-blocks 'try {} catch(COND) { }'

by kcott (Archbishop)
on Sep 20, 2021 at 07:23 UTC ( [id://11136866]=note: print w/replies, xml ) Need Help??


in reply to conditional catch-blocks 'try {} catch(COND) { }'

G'day Rolf,

Just throwing a few ideas around.

"... catch error-objects ..."

I'll assume, if $e is the error-object, then 'ref $e' returns "TypeError", "RangeError", and so on.

"... didn't show much implementation ... more interested in comments regarding the interface ..."

I'll use the Try::Tiny syntax. You can implement something else; e.g. there's some alternatives in the "SEE ALSO" section of that module, there's the experimental "Try Catch Exception Handling" introduced in Perl v5.34, and no doubt many more.

Probably in some error handling module, you might have something like:

{ my %dispatch_error; BEGIN { %dispatch_error = ( TypeError => sub { ... handle TypeError exceptions ... }, RangeError => sub { ... handle RangeError exceptions ... } +, ..., '' => sub { ... handle any unspecified exceptions ... }, ); } sub handle_error { my ($e) = @_; my $error_type = ref $e; $error_type = '' unless exists $dispatch_error{$error_type}; $dispatch_error{$error_type}->($e); return; } }

Then your try/catch code could be something like:

try { myroutine(); } catch { handle_error($_); };

So now, the interface in the main code becomes very simple. Beyond whatever try/catch syntax you choose, you can do pretty much whatever you want with the implementation (as long as it has a handle_error() routine).

I would assume your error objects are instantiated from classes with appropriate information; for example, a RangeError class has min and max attributes.

— Ken

Replies are listed 'Best First'.
Re^2: conditional catch-blocks 'try {} catch(COND) { }'
by afoken (Chancellor) on Sep 20, 2021 at 11:01 UTC
    { my %dispatch_error; BEGIN { %dispatch_error = ( TypeError => sub { ... handle TypeError exceptions ... }, RangeError => sub { ... handle RangeError exceptions ... } +, ..., '' => sub { ... handle any unspecified exceptions ... }, ); } sub handle_error { my ($e) = @_; my $error_type = ref $e; $error_type = '' unless exists $dispatch_error{$error_type}; $dispatch_error{$error_type}->($e); return; } } try { myroutine(); } catch { handle_error($_); };

    One obvious disadvantage is that you need a different handle_error() routine for each different set of exceptions you want to handle. Also, the exception handling code moves away from the catch. And the exception handlers might be outside the scope of the try-catch-block (they aren't, in your example), so they don't have access to local variables.

    How would you solve this?

    # not quite Perl sub work { # Handle a few exceptions my $result1; try { $result1=doSomeMath(); } catch (DivByZeroException) { $result1="div by 0"; } catch (NotANumberException) { $result="not a number"; } # no explicit handler for other exceptions, so re-throw # Handle a different set of exceptions my $result2; try { $result2=doSomeMoreMath(); } catch (DivByZeroException) { $result2="div by 0"; } # no check for NaN, will be re-thrown like any other Exception h +ere # Handle all possible exceptions, with a special handling for one +exception try { writeToFile($result1,$result2); } catch (IOException) { say "oopsie - I/O problem"; } catch { say "something else went wrong"; } }

    Alexander

    --
    Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
      Something like
      #!/usr/bin/perl use warnings; use strict; use Try::Tiny; sub handle_error { my ($e, $dispatch_error) = @_; my $error_type = ref $e; $error_type = '' unless exists $dispatch_error->{$error_type}; $dispatch_error->{$error_type}->($e); return } for my $case (0, 1, 2) { try { [ sub { die bless {source => 'Int', target => 'Float'}, 'TypeE +rror' }, sub { die bless {value => 2**65, min => -2**32, max => 2**32}, 'RangeError' }, sub { die bless {}, 'Segmentation Fault' } ]->[$case]->() } catch { handle_error($_, { TypeError => sub { warn "Coercing type $_[0]{source} to $_[0]{target}" }, RangeError => sub { warn "$_[0]{value} not between $_[0]{min} and $_[0]{ma +x}" }, '' => sub { die $_[0] }, }); }; print "next\n"; }
      maybe?

      map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]

        A little bit more code and indenting than expected, but yes, that should work.

        Try::Tiny implements catch as a prototyped sub (sub catch (&;@)), so it should be possible to implement something like sub catchif ($&;@) that accepts an additional parameter, and to extend the logic in sub try to basically implement your handle_error() function.

        That would allow the following syntax that at least looks similar to try-catch in other languages:

        try { # ... } catchif TypeError => { # ... } catchif RangeError => { # ... } catch { # ... } finally { # ... }

        I think it would not need many changes:

        • sub catchif is very similar to sub catch, it "just" needs to also embed its first argument in the returned object
        • try needs to build a hash of catchif cases, extracting the first argument from catchif objects

        • try has to examin the hash before executing the generic catch case.

        Alexander

        --
        Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
        >  handle_error($_, {  TypeError => sub {

        I wanted to suggest

        • dropping the $_ because it's redundant
        • dropping the default case ' => sub { die $_[0] },' because it's redundant too
        • elevating handle_error or rather handle to the same level like catch
        • like this a simple catch could be used if the default isn't wanted.

        But I think Lukas already did this in Try::Tiny::ByClass

        use Try::Tiny::ByClass; try { die $exception_object; } catch_case [ 'Some::Class' => sub { # handle Some::Class exceptions }, 'Exception::DivByZero' => sub { # handle Exception::DivByZero exceptions }, ], finally { # always do this };

        Tho I sense a bit of confusion between finally and a "naked" catch

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        Wikisyntax for the Monastery

      G'day Alexander,

      As per the original question, I focussed on the interface rather than the implementation. I did, however, leave some clues but I didn't expand upon them.

      When I said "I would assume your error objects are instantiated from classes with appropriate information; for example, a RangeError class has min and max attributes.", I was thinking that myroutine() might look something like:

      sub myroutine { ... if ($n < 5 or $n > 10) { die RangeError::->new(min => 5, max => 10, got => $n, ...); + } ... }

      Then a generic $dispatch_error{RangeError} routine would have sufficient information to generate an error message. That might look something like:

      ERROR: Out of bounds. The allowed range is $e->min() to $e->max() (inc +lusive). The number received was $e->got().

      The RangeError class might even have some boilerplate to create such a message using the attribute values of the error object.

      My main intent was to suggest an interface with "an easy syntax" (as requested) and to decouple the implementation. I hadn't really given the implementation details much thought; however, what I've described above, is roughly what had in the back of my mind.

      — Ken

Re^2: conditional catch-blocks 'try {} catch(COND) { }'
by LanX (Saint) on Sep 20, 2021 at 13:37 UTC
    Thanks Ken,

    I agree with afoken that an uniform global handle_error doesn't solve the need to define an ad-hoc catch condition.

    > there's the experimental "Try Catch Exception Handling" introduced in Perl v5.34

    a great I forgot about this one, have to see if it covers all the cases.

    And it's certainly not backwards compatible. Which is a general problem of most experimental features.

    Having a stable strategy which works with older Perls albeit slowly and nice and fast with newer once would facilitate the development and evalution of new features tremendously.

    > and no doubt many more.

    It would be nice to formulize the requirements covered and to test all those "many more" modules against them.

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    Wikisyntax for the Monastery

      >> there's the experimental "Try Catch Exception Handling" introduced in Perl v5.34

      > And it's certainly not backwards compatible. Which is a general problem of most experimental features. Having a stable strategy which works with older Perls albeit slowly and nice and fast with newer once would facilitate the development and evalution of new features tremendously.

      Which is why there is Feature::Compat::Try (FCT), which will choose between the v5.34 native implementation or the Syntax::Keyword::Try (SKT) implementation, which will mean you can write code in such a way that it is backwards compatible down to the v5.14 that SKT supports, using the same syntax on with newer and older versions of Perl. (If I've understood the docs correctly, SKT actually has additional syntax that isn't in the v5.34 native implementation, but the FCT wrapper only exposes the v5.34-compatible syntax. I think.)

      Caveat: I haven't used that pair, because I am often on systems that only has v5.8, so I don't want to get in the habit of using a try/catch pair that's not compatible back that far. But I remembered having seen the FCT/SKT pair mentioned in some other recent thread, so it stuck in my head. So if v5.14 is backward-compatible enough for you, looking at FCT would be a good idea.

        > Syntax::Keyword::Try

        this may be a dumb question, but why is the $e in catch ($e) explicitly needed?

        use Syntax::Keyword::Try; sub foo { try { attempt_a_thing(); return "success"; } catch ($e) { warn "It failed - $e"; return "failure"; } }

        To avoid reusing/redefining a special variable like Try::Tiny does with $_ ?

        We already have $@

        Or is there another reason I'm not aware of?

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        Wikisyntax for the Monastery

        > Which is why there is Feature::Compat::Try (FCT)

        So Syntax::Keyword::Try is an XS module for exactly back-porting this feature "try" till v5.14?

        > So if v5.14 is backward-compatible enough for you, looking at FCT would be a good idea.

        After Devel::Declare and family, I'm no big fan of XS dependencies hacking into the parser.

        The less => the more stable => the better.

        > I haven't used that pair, because I am often on systems that only has v5.8,

        Using pure Perl for backwards compatibility wouldn't have such limitations.

        OTOH using XS would be faster on "older" Perls, at least when parsing.

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        Wikisyntax for the Monastery

      "I agree with afoken that an uniform global handle_error doesn't solve the need to define an ad-hoc catch condition."

      My response may (or may not) alter your view on that. If it's not suitable for your needs, that's fine: as I said, "Just throwing a few ideas around.".

      On the try/catch in 5.34, I generally don't use or suggest experimental features; in fact, I normally only mention them when recommending not to use them. In this instance, you had said, "... new syntax ... experimentally implemented ..."; accordingly, I pointed out a core experimental feature (in the certain knowledge that you're smart enough to understand the pitfalls) — I actually don't know if what you're doing is purely academic or for a production system.

      — Ken

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others imbibing at the Monastery: (4)
As of 2024-04-25 17:43 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found