jweirich / one.net wrote:

>>>>>>"Tobias" == Tobias DiPasquale <anany / ece.villanova.edu> writes:
>>>>>>
>
>    Tobias> I agree totally. This is absolutely essential to building
>    Tobias> fault-tolerant systems in Ruby. If I don't know what
>    Tobias> exceptions a method will throw, or even if it throws
>    Tobias> exceptions, how can I know how to handle them correctly?
>
>I have a different take on exceptions.  I believe exceptions should be
>thrown when the called method has failed, where failure is defined as
>unable to met its contract (either formally defined via Design by
>Contract or informally defined some other way).  Trying to communicate
>exactly how a method fails through exceptions unnecessarily couples
>the client code to the supplier code.  
>
>For example, suppose we have method do_something() that can throw an
>OutOfRangeError or a InverseNotFoundError exceptions (just examples).
>We carefully handle both of these errors in our client code.  Then our
>code is given an object that implements do_something() via a network
>proxy object.  All of a sudden we could possibly get a NetworkError
>exception as well.  If our code doesn't expect a NetworkError, then we 
>have problems.
>
>We should expect exceptions to happen and handle them, but trying to
>anticipating all the failure modes and responding differently in each
>case is a dangerous path.   I believe that treating all exceptions as
>failures (without distiguishing different failure modes) is a better
>way to go.
>
>Of course, this is just my take on exceptions.  I know others
>disagree.
>
I tend to split my exceptions up into two categories, #1 Control 
Exceptions (for controlling the flow of the application) and #2 Error 
Conditions.  Each exception type then inherits from the appropriate base 
class and comes with the basic required functionality (you catch the 
base class if you don't really care about the specific type of 
exception, and catch the inherited class if you do).

The problem I see with error exceptions (and this is *REALLY* bad in the 
Java world) is that people throw an exception without sending all the 
information necessary to properly identify the problem.  With Java apps, 
you more often than not get a list of three exceptions in a row (each 
providing a stack trace which does little to help when you don't own or 
understand the code) and very terse and crappy error messages.  In all 
honesty, I don't see how that helps.

So what I tend to do is have my exceptions store some sort of hash.  The 
hash contains a list of messages the client should see when they get an 
error.  For instance, if I'm writing a parser and some part of the 
parser fails, it would throw an exception and stick a message into the 
hash stating what happened.  Somewhere up in the parser, the exception 
would get caught, the line number, file number, and the line itself, 
would get stuck in the hash as two new entries and the exception would 
be rethrown.  By the time you get to the top, you have the one exception 
that really counts (where the problem happened) and a list of messages 
pertaining to what went wrong.  Since each hash key uses a unique ID, 
you can then extract the key/values you want, show all of them, or 
explicitly ignore those you don't want (and you can throw helper classes 
along with the exception in the cases where you need to do something 
more complex).

That's how I tend to use exceptions, and so far I've found it works 
quite well.  Anyway, it works better than the Java way of doing things 
(especially if you are really carefull about adding in usefull error 
messages).