Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl-Sensitive Sunglasses
 
PerlMonks  

Re: the try/catch example from "Programming Perl" analyzed

by hardburn (Abbot)
on Aug 18, 2004 at 17:59 UTC ( #384056=note: print w/ replies, xml ) Need Help??


in reply to the try/catch example from "Programming Perl" analyzed

On a tangently related point, I'm rather unsatisfied with the idioms for OO exception handling in Perl.

With Java exceptions, you are guarenteed that any caught exception is an object (one place where Java's nearly-pure OO environment comes in handy). In Perl, you also have to handle the condition of a runtime error which is not an object:

eval { do_something_that_could_die() }; if($@) { # We know it died, but is it an object or a # printable message? if( ref $@ ) { # Assume it's an exception object. Not a # great way to do it, but it works. # # Now, what kind of exception is it? # if( $@->isa( 'IOException' ) ) { . . . } elsif( $@->isa( 'OtherException' ) ) { . . . } else { . . . } } else { # Assume it's an error string . . . } }

If you're good aboug indenting your code, then this produces an extra level of indentation (and an extra case) that Java doesn't have. Java does have an extra level by nature of enclosing everything in a class declaration, but this is compensated by using 4-space indent, whereas most Perl code can reasonably sit in an 8-space indent. (Of course, I'm now sitting dangerously close to a coding-style flame war.)

Thank God for folding editors, or this would be a huge blight to see in the middle of a subroutine.

"There is no shame in being self-taught, only in not trying to learn in the first place." -- Atrus, Myst: The Book of D'ni.


Comment on Re: the try/catch example from "Programming Perl" analyzed
Download Code
Re^2: the try/catch example from "Programming Perl" analyzed
by dragonchild (Archbishop) on Aug 18, 2004 at 18:05 UTC
    Don't use ref - use blessed. Now, you can be guaranteed it's an exception object, insofar as you can guarantee that all code you would want to call in an eval/$@ block would propagate exception objects if they propagate objects at all.

    ------
    We are the carpenters and bricklayers of the Information Age.

    Then there are Damian modules.... *sigh* ... that's not about being less-lazy -- that's about being on some really good drugs -- you know, there is no spoon. - flyingmoose

    I shouldn't have to say this, but any code, unless otherwise stated, is untested

      Well, that helps a bit with the implementation, but doesn't solve the fundamental problem of having to handle the non-blessed case. Also, you now have to pull in another module to check for blessedness.

      "There is no shame in being self-taught, only in not trying to learn in the first place." -- Atrus, Myst: The Book of D'ni.

        You could always use some sort of object wrapper to convert the strings into error objects, and hide that in the 'try' code,

        local $_ = (blessed $@) ? $@ : MyStringErrorClass->new($@);
        or some such. That class (or a function that returns different error objects depending on the error string, or something), would know all about the Errno module, and other standard kinds of errors.

        Ron Steinke
        <rsteinke@w-link.net>
Re^2: the try/catch example from "Programming Perl" analyzed
by stvn (Monsignor) on Aug 18, 2004 at 21:37 UTC
    In Perl, you also have to handle the condition of a runtime error which is not an object

    I believe that the try block is where the memory leak issues usually show up, since that is where the bulk of your working code is. But why throw catch away too?

    sub catch (&) { if ($@) { my ($catch) = @_; my $e = $@; $e = My::Base::Exception->new($@) unless (ref($@) && UNIVERSAL::isa($@, "My::Base::Exception +")); $catch->($e); } } eval { die "test"; }; catch { my $e = shift; if ($e->isa('IOException')) { # ... } else { print $e; } };
    I know chromatic would be upset that I am using UNIVERSAL::isa, but it will avoid your problem with using Scalar::Util::blessed, and as long as you don't use any other classes that override isa you should be okay.

    -stvn
Re^2: the try/catch example from "Programming Perl" analyzed
by adrianh (Chancellor) on Aug 19, 2004 at 13:05 UTC
    In Perl, you also have to handle the condition of a runtime error which is not an object:

    You could always encapsulate the extra test in some code:

    sub exception (;$) { my $wanted_exception = shift; return unless $@; return 1 unless defined $wanted_exception; return ref($@) && $@->isa( $wanted_exception ) };

    allowing you to do:

    eval { $coderef->() }; if ( exception 'IOException' ) { ... handle IOException ... } elsif ( exception 'OtherException' ) { ... handle OtherException ... } elsif ( exception ) { ... handle all other exceptions ... } else { ... we lived ... };

    Sure, you have an extra ref test and subroutine call, but since exceptions should be... well... exceptional it shouldn't impact your runtime speed much.

Re^2: the try/catch example from "Programming Perl" analyzed
by Arunbear (Parson) on Aug 19, 2004 at 14:36 UTC
    Not sure that the nested if is actually needed. This code works with string and object exceptions (on 5.8.3 at least):
    use strict; use warnings; package Exception; package IOException; @IOException::ISA = qw(Exception); package OtherException; package YetAnotherException; package main; *isa = \&UNIVERSAL::isa; my @grisly = ( sub { die "AARRGH!\n" }, sub { die bless [], 'Exception' }, sub { die bless [], 'IOException' }, sub { die bless [], 'OtherException' }, sub { die bless [], 'YetAnotherException' }, sub { print "I live\n"; } ); sub do_something_that_could_die { $grisly[0]->(); # or int(rand(@grisly)) } eval { do_something_that_could_die(); }; if(isa($@, 'Exception')) { print ref($@), ' caught'; } elsif(isa($@, 'OtherException')) { print ref($@), ' caught'; } elsif(ref $@) { # handle any other exception object print ref($@), ' caught'; } elsif($@) { # handle non-object exception print $@; }
    Update:
    replaced $@->isa('package') calls with calls to UNIVERSAL::isa()

      As long as you are sure you are going to get an object. You may want to use the functional UNIVERSAL::isa() if you want to avoid your 'catch' crapping out when something does just a plain die $!

      /J\

        Thanks, it would also have crapped out if $@ was undefined.
Re^2: the try/catch example from "Programming Perl" analyzed
by Jenda (Abbot) on Aug 19, 2004 at 18:23 UTC

    I have yet to find a case in which I would want to throw an object. Besides all exception objects are supposed to be stringifiable AFAIK.

    Besides Java code doing the same thing is usualy several times longer than its Perl conterpart so I don't think these three or four additional lines matter. That is unless you program Java in perl.

    Jenda
    Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live.
       -- Rick Osborne

      I have yet to find a case in which I would want to throw an object.

      I have. Specifically, I want to do different things for different errors, and extract information from it. For instance, Exception::Class provides full information about the uid/gid, package, file, line number, stacktrace, pid, and so on. Sure, it'd be possible to encode all that into a single string, but the extraction process isn't as easy. Further, you might want to provide some information to the user, and send more details to the developer. It's much easier (and safer) to do that with an object than trying to munge a string.

      Besides Java code doing the same thing is usualy several times longer than its Perl conterpart so I don't think these three or four additional lines matter. That is unless you program Java in perl.

      It's not just the extra lines, but the extra level of indentation. Like I said, Java programmers typically operate with 4-space indents, because they know they're going to take a lot more space for indentation than other langs (and also because the identifiers tend to be much longer). However, most Perl programmers can easily get away with 8-space indents. As such, every extra level of indentation is a big deal.

      Other people have given solutions which get rid of that extra level. My prefered is to place the string into an exception object and then handle it like any other exception..

      "There is no shame in being self-taught, only in not trying to learn in the first place." -- Atrus, Myst: The Book of D'ni.

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others pondering the Monastery: (8)
As of 2014-08-01 11:01 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    Who would be the most fun to work for?















    Results (6 votes), past polls