http://www.perlmonks.org?node_id=279188


in reply to Re^8: Learning how to use the Error module by example
in thread Learning how to use the Error module by example

sub find { my ($self, $search_item) = @_; eval { # skip expensive search if item not stored anywhere throw Signal::Search::Not_Stored unless $search_item->stored; # otherwise do expensive search $self->first; while (my $item = $self->next) { throw Signal::Search::Found if $item == $search_item; }; throw Signal::Search::Not_Found; }; if ($@) { my $e = $@; # Do NOT handle Signals here. Those propagate back # up to the caller who will handle them appropriately. throw $e if $e->isa('Signal::Search'); ... handle exceptions here ... }; };
I would argue that you have three possible return codes - NOT_STORED, FOUND, NOT_FOUND, plus all the possible exceptions that can occur. Instead of trying to shoehorn your return codes into a C-like morass of constants and bit-vectors, use signalling exceptions! Heck, you can even include a reference to the found item in your Signal::Search::Found object, if you were so inclined. And, your code is self-commenting because you make a distinction between NOT_STORED and NOT_FOUND, in case the caller or reader cares. If they don't, they can explicitly treat the two cases the same, increasing the commenting.

------
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.

Please remember that I'm crufty and crochety. All opinions are purely mine and all code is untested, unless otherwise specified.

Replies are listed 'Best First'.
Re^10: Learning how to use the Error module by example
by adrianh (Chancellor) on Jul 30, 2003 at 15:20 UTC

    But why is this better than the version using return? (and we don't care between no-stored and not-found :-). Why are returns bad?

    We just seem to be adding three more exception classes, moving the logic of what we care and don't care about handling from the mainline to the catch block, and making the caller handle exceptions when they're just interested in a boolean value. How is this simpler? All the callers of find are now going to have to do:

    eval { $self->find($foo) }; print "found" if ( UNIVERSAL::isa($@, 'Signal::Search::Found') ); die $@ unless UNIVERSAL::isa($@, 'Signal::Search'); # rather than print "found" if $self->find($foo);

    which seems insane - but ignoring that point what is wrong with the return? That's the bit I'm not getting.

      The only thing that's "wrong" with return is that you have more than 2 return codes. You have found, not found, and possible error conditions. To handle the error conditions, you will still have to wrap find() in an eval block. I would rewrite your call as such:
      eval { $self->find($foo); }; if ($@) { if ($@->isa('Signal::Search') { if ($@->isa('Signal::Search::Found')) { print "found"; } else { print "Not found"; } } else # These are (presumably) error conditions ... { # Handle error here. } }
      It looks like a lot more code, but so does the part() example in Exegesis 6. I still liked the flexibility it gave me.

      Now, the system I seem to be proposing here involves using try-catch (or eval-$@) syntax throughout the entire application, including all callers. To me, this isn't something you can just implement in one part, but not the other. I would completely eschew return for status codes and use it only when there is something to actually return, other than true or false. (Speaking of truth/falsehood, you could rename the signals to Signal::True and Signal::False and collapse the number of signals you're working with. But, I prefer to have all the signals explicitly named.)

      As for the worry that you're propagating a bunch of classes, you could have each class define the signals it happens to the only user of, so that the useing of the class auto-uses the signals for that class. TMTOWDI.

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

      The idea is a little like C++ templates, except not quite so brain-meltingly complicated. -- TheDamian, Exegesis 6

      Please remember that I'm crufty and crochety. All opinions are purely mine and all code is untested, unless otherwise specified.

        The only thing that's "wrong" with return is that you have more than 2 return codes.

        But why are multiple return statements "worse" than multiple throw statements? Why replace the boolean functionality found everywhere else in Perl with your own exception based mechanism? Why move mainline code into catch blocks?

        I think we're just going to have to agree to disagree over this one! If I was a maintenance developer faced with the code you gave, or:

        print $self->find($foo) ? "found" : "Not found"; if ( $@ ) { # Handle error here };

        I know which I'd prefer :-)