Beefy Boxes and Bandwidth Generously Provided by pair Networks
The stupid question is the question not asked
 
PerlMonks  

Re: Best Practices for Exception Handling

by Anonymous Monk
on Jan 28, 2003 at 23:35 UTC ( [id://230806]=note: print w/replies, xml ) Need Help??


in reply to Best Practices for Exception Handling

"Exception handling" and "BestPractices" seems like a contradiction to me.
Unless you are developing real-time systems. And who writes real-time code
in Perl?
I guess I am just too old school.
  • Comment on Re: Best Practices for Exception Handling

Replies are listed 'Best First'.
Re: Best Practices for Exception Handling
by jonadab (Parson) on Jan 29, 2003 at 15:28 UTC

    I tend to agree. A lot of people with a lot more experience than I have rave about how wonderful exception handling is, but they've failed to communicate to me why it is valuable to be able to throw an exception in one place and catch it someplace else rather than handling the problem (assuming it can be handled by any means other than spitting an error message and bailing) in the same block of code where it is detected. It seems to me that having the exception and the handling thereof separated by arbitrarily many lines of code is asking for headaches in terms of code maintenance. Rather than throwing the exception in the first place, wouldn't it be better to handle it en situ? (Sure, sometimes a number of spots can share the same code -- so call a subroutine then, but at least the call is right there.)

    As I said, I have a good deal less experience than a lot of the people who rave about exception handling, so I'm probably missing something...

     --jonadab

      Some reasons I like exceptions:

      1. Robustness. I can forget to check for an returned error value. I cannot forget to check for an exception.

      2. Brevity. I prefer:

        $o->foo->bar->fribble->ni

        to

        $o->foo or return(ERROR_FOO); $o->bar or return(ERROR_BAR); $o->fribble or return(ERROR_FRIBBLE); $o->ni or return(ERROR_NI);
      3. Clarity. With exception based code the "normal" flow of control is more explicit because it is not obscured by error handling code. I think that the first of the two code examples above shows the intent of the code more directly than the second does.

      4. Separation of concerns. The error condition and the error handler are different ideas.

        • You may want an error to be handled in different ways depending on the context.
        • You may also not know how the error should be handled at the point it occurs.
        • You may not know how the error should be handled at the time you write the code.

        With the return-error-code style you end up having to either:

        • propogate error conditions to where the decision on how they should be handled can be made.
        • propogating error handlers down to where the errors may occur

        Both options rapidly become messy if there are many levels of code between the error condition and the error handler.

      5. No confusion between return values and error conditions.

      There are probably some more ;-)

        > Robustness. I can forget to check for an returned error value.
        > I cannot forget to check for an exception.
        > Brevity. I prefer: $o->foo->bar->fribble->ni
        > to $o->foo or return(ERROR_FOO); [snip]

        Huh? Error return codes? Error return codes are basically an inelegant way of doing exactly the same thing -- punting the problem to some other code someplace else. It's that whole approach that I don't understand the value of. Points one, two, and five all seem to be arguing against return codes, which are in my view basically a certain (particularly inelegant) type of exception handling. You don't need to convince me that's a bad way to do it; my question is why it (punting an error to the caller's caller's caller) should ever be done at all.

        Regarding your third point, clarity: one of us is smoking crack, because putting code that handles an error pages away from where the error actually happens is my idea of severe obfuscation. How it could ever conceivably enhance clarity is entirely beyond my ability to fathom.

        Point four, however, I'd like to explore further. Maybe I just haven't encountered the right problem yet. Every error I've had to handle either could be fixed, or it couldn't. If it couldn't be fixed, it either could be logged and ignored and the program proceed less certain functionality, or else it was fatal. That basically leaves three options when an error condition pops up: do stuff to fix the problem, log it and go on, or spit an error message and exit. I have yet to encounter a situation where the caller might be relevant to the question of which of these conditions applies.

        So, back to your point four: can you suggest a real-world example or two of a situation where the error needs to be handled differently depending on where the routine is called or other circumstances not known to the code that finds the error? I've been through this discussion before (not here) and have tried to imagine such a situation, but I can't seem to come up with one.

         --jonadab

      I believe that the idea is that you can't always do something on the spot.

      For example, suppose I am developing a flashcard application that reads flashcards from a file and displays them in a pretty window. To accomplish this, I decide to write a couple of classes that do the actual file reading and parsing, etc., in order to keep that separate from the gui code.

      So what happens if, for example, the gui asks one of those objects to open a non-existant file? An elegant way out of the problem is for the object to raise an exception. The gui can then choose to catch that, and pop up a dialog, or whatever.

      It is definitely not the *only* way to solve the problem, though. You could also, for example, have every method return error codes. Or a hash containing an error code plus whatever other value it wants to return. Exceptions do provide a very clean way to do it, though.

      Just my $.02,
      -Dan

      Java Boutique has an interesting article about exceptions that you might find useful.

      What I like about exceptions is fairly straightforward. If I have a GUI (A) which issues a message to some object (B) which in turn sends a message requesting data from object (C), what happens if C determines that the data is bad due to user input (and not, say, a programming error)? There's probably no point in issuing a warning in the error log (because it's not an error from a programming standpoint) and there's no point in killing the application because a user typed something stupid (an all too common event, though).

      In the above scenario, C throws in exception and B catches it. B then checks the exception to see if anything unusual needs to be done. If the exception is caused by failing to select a required option, no big deal. If the exception is caused by a bad password, perhaps it is a big deal. B can decide whether or not to log the exception and do further processing. B, at this point, rethrows the exception and A catches it. Because A is simply the GUI, A doesn't worry about logging the exception. A simply has to have some method of displaying the exception to the end user in a meaningful way.

      Thus, with an exception based system, particularly if it's multi-tiered, you have full control of the error processing rather than simple die and warns. Further, uncaught exceptions will kill the program when encountered during execution, thus making them morely likely to be reported in testing and feedback -- which in turns means they are more likely to be handled appropriately and leads to more robust code.

      Cheers,
      Ovid

      New address of my CGI Course.
      Silence is Evil (feel free to copy and distribute widely - note copyright text)

        > If I have a GUI (A) which issues a message to some object (B) which
        > in turn sends a message requesting data from object (C), what
        > happens if C determines that the data is bad due to user input

        See, that's where I always get lost. If C is getting user input, and the user input is invalid, C should just reget the user input. When it has valid input, it should pass that back to B. Otherwise, it should continue to ask the user to fix up the input.

        In a slightly different scenerio, C might think the input is okay (there's an integer in this integer field, and some text in this required text field; C does not know the meaning of these data, but they are the kind of data that were requested), but B might discover that it's inconsistent. (There is no thirty-first of February.) In that case, B would possibly explain the problem to the user and then call C again; only when the input is good enough for B to do its job does it then pass the result back to A.

        Do you see where I'm getting hung up? It's not with the question of HOW to pass stuff back to the parent (which was the original topic of the thread, I know), but more with the more basic question of why that is a thing that needs to be done. That's the point I haven't managed to understand yet.

         --jonadab

      Handling the error in place and consulting the calling code for advice on handling it are not necessarily mutually exclusive, for which I point you to the paper referenced in another node of mine in this discussion which (i.e. the paper) describes how things are handled in Common Lisp.

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others meditating upon the Monastery: (4)
As of 2024-04-19 14:58 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found