in reply to Re: Idomatic Handling of Subroutine Error
in thread Idomatic Handling of Subroutine Error

I have no doubt that this will work. But I'm looking for a way to hide the ugly details from the calling code.

Perhaps (looking at your suggestion and making this up as I go...)

sub my_sub { # do stuff eval { potential icky bit }; if ($@) { return 0; } else { return $something_useful; } } # MAIN unless ( $result = my_sub() ) { # use $@ to figure out what to do }
Well and good. But what if the problem arises from the logic of my task and not from an eval-able error. Bad data for example.

I'm willing to hid any ugliness in the sub. But how do I best set a retreavable value of my own chosing to flag the problem and still allow succinct, clean code where the sub is called:

unless ( $result = my_sub() ) { # handle error condition based on error var }

Replies are listed 'Best First'.
Re: Re: Re: Idomatic Handling of Subroutine Error
by dragonchild (Archbishop) on Sep 18, 2001 at 23:35 UTC
    You're almost there.
    # MAIN unless ($result = my_sub() ) { # Now you use $result if it's not 0 } # WRAPPER function sub my_sub { eval { _my_sub(@_) }; if ($@) { return $@; } else { return ''; } } # Actual worker... sub _my_sub { # Do your stuff here. If something fails, do a die with a useful error + message. Otherwise, return 0. # For example ... die "Bad data passed in.\n"; }
    You actually do a die which doesn't end your script - it gets trapped by the eval and the string you passed die will be put in $@.

    Now, I personally dislike this type of error-handling because 0 is FALSE and non-zero is TRUE. Thus, you have to flip your thinking the way the C libs force you to and say you're calling the function and hope it "fails" for success. (Sorta like a drug screening ...)

    Instead, I would something similar. Instead of passing back '' for success, I'd pass back undef instead.

    # MAIN if (defined ($error = my_sub()) ) { # Now you use $error if it's defined } # WRAPPER function sub my_sub { eval { _my_sub(@_) }; if ($@) { return $@; } else { return undef; } } # Actual worker... sub _my_sub { # Do your stuff here. If something fails, do a die with a useful error + message. Otherwise, return 0. # For example ... die "Bad data passed in.\n"; }
    It seems like a semantic difference, but now your code use TRUE and FALSE the way they intuitively used, to indicate success and failure.

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

    Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement.

      Promising. But can I drop the wrapping sub by eval'ing the 'die' directly? I also need to have a useful returned val on success. With that, I'll need to reverse the return logic. Is there any hope for an approach like this?
      # MAIN unless ($result = my_sub($val1, $val2)) { my $err_str = $@; # handle error based on $err_str } # do wonderful things with $result sub my_sub { my ($param1, $param2) = @_; # do stuff if ($bad_data) { eval {die "bad data"}; # ??? return undef; } # do more stuff if ($useless_param2) { eval {die "useless param 2"}; # ??? return undef; } # finish doing stuff return $useful_result; }
        If you need a useful return value upon success, you're having that one function do too many things at once.

        A function should do one and only one thing, and do it well. It should be simple enough that you can know what's going on simply due to success or failure. Maybe, you need failure to give you more than one option. Hence, the option I suggested above.

        Try and refactor your function so that if it succeeds, that's all the info you need. If you cannot, make it into an object similar to what btrott said in direct response to your original node.

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

        Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement.