February 19, 2012
On Sat, Feb 18, 2012 at 05:13:16PM -0600, Andrei Alexandrescu wrote:
> On 2/18/12 4:26 PM, Jonathan M Davis wrote (abridged):
> GetOptException
> FlagArgumentMissingException
> InvalidFlagArgumentException
> UnknownFlagException
> FileException
> FileNotFoundException
> NotFileException
> NotDirException
> AccessDeniedException
> 
> I died inside a little.
[...]

That's because you're missing the hierarchy, which may look more like this:

Error
 +--Exception
     +--GetOptException (bad name, I prefer CommandLineException)
     |   +--FlagArgumentMissingException
     |   +--InvalidFlagArgumentException
     |   +--UnknownFlagException
     +--FileException
     |   +--FileNotFoundException
     |   +--NotFileException
     |   +--NotDirException
     |   +--AccessDeniedException
     +--IOException (OK, I added this branch, just to show the idea)
         +--ReadErrorException
	 +--WriteErrorException

So if all you care about is to handle command-line option parse errors, and don't care which error it is, you could just catch GetOptException. If all you care about is file access exceptions, you can just catch FileException without having to worry which specific exception it is.

Alternatively, if you don't care which exception it is at all, then just catch Exception.

That's the whole point of a (well-designed) exception hierarchy. It lets you be as generic or as specific as you want.

Note also, that an elaborated exception hierarchy lets you attach additional specific information that might be useful in error recovery. For example, FileException may have a field containing the filename that caused the error, so that if the program is processing a list of filenames, it knows which one went wrong. You would not want such information in Exception, because it only applies to FileException's.

Similarly, GetOptException (or CommandLineException) may have additional fields to indicate which option is malformed. Such information, obviously, shouldn't be in other kinds of exceptions but those that inherit GetOptException.


T

-- 
Which is worse: ignorance or apathy? Who knows? Who cares? -- Erich Schubert
February 19, 2012
On Saturday, February 18, 2012 16:36:16 H. S. Teoh wrote:
> On Sat, Feb 18, 2012 at 05:13:16PM -0600, Andrei Alexandrescu wrote:
> > On 2/18/12 4:26 PM, Jonathan M Davis wrote (abridged):
> > GetOptException
> > FlagArgumentMissingException
> > InvalidFlagArgumentException
> > UnknownFlagException
> > FileException
> > FileNotFoundException
> > NotFileException
> > NotDirException
> > AccessDeniedException
> > 
> > I died inside a little.
> 
> [...]
> 
> That's because you're missing the hierarchy, which may look more like this:
> 
> Error
>  +--Exception
>      +--GetOptException (bad name, I prefer CommandLineException)
> 
>      |   +--FlagArgumentMissingException
>      |   +--InvalidFlagArgumentException
>      |   +--UnknownFlagException
> 
>      +--FileException
> 
>      |   +--FileNotFoundException
>      |   +--NotFileException
>      |   +--NotDirException
>      |   +--AccessDeniedException
> 
>      +--IOException (OK, I added this branch, just to show the idea)
>          +--ReadErrorException
> 	 +--WriteErrorException
> 
> So if all you care about is to handle command-line option parse errors, and don't care which error it is, you could just catch GetOptException. If all you care about is file access exceptions, you can just catch FileException without having to worry which specific exception it is.
> 
> Alternatively, if you don't care which exception it is at all, then just catch Exception.
> 
> That's the whole point of a (well-designed) exception hierarchy. It lets you be as generic or as specific as you want.
> 
> Note also, that an elaborated exception hierarchy lets you attach additional specific information that might be useful in error recovery. For example, FileException may have a field containing the filename that caused the error, so that if the program is processing a list of filenames, it knows which one went wrong. You would not want such information in Exception, because it only applies to FileException's.
> 
> Similarly, GetOptException (or CommandLineException) may have additional fields to indicate which option is malformed. Such information, obviously, shouldn't be in other kinds of exceptions but those that inherit GetOptException.

Exactly!

And in order to programmatically handle exceptions rather than simply print out messages, that hierarchy and the extra information that the derived exceptions give is necessary.

- Jonathan M Davis
February 19, 2012
On Sat, Feb 18, 2012 at 06:09:30PM -0600, Andrei Alexandrescu wrote:
> On 2/18/12 6:03 PM, Jonathan M Davis wrote:
[...]
> >If we have GetOptException, it should have a variable for which flag failed. Exception doesn't have that. And what if the flag failed because it was given a bad argument? Then the exception needs a field for that argument. Then you can get something like
> >
> >try
> >     getopt(args, ...)
> >catch(MissingArgumentException mae)
> >{
> >     stderr.writefln("%s is missing an argument", mae.flag);
> >     return -1;
> >}
> >catch(InvalidArgumentException iae)
> >{
> >     stderr.writelfln("%s is not a valid argument for %s. You must give it a
> >%s.", mae.arg, mae.flag, mae.expectedType);
> >     return -1;
> >}
> >catch(UnknownFlagException ufe)
> >{
> >     stderr.writefln("%s is not a known flag.", ufe.ufe);
> >     return -1;
> >}
> >catch(GetOptException goe)
> >{
> >     stderr.writefln("There was an error with %s",  goe.flag);
> >     return -1;
> >}
> >//A delegate that you passed to getopt threw an exception.
> >catch(YourException ye)
> >{
> >     //...
> >}
> >catch(Exception e)
> >{
> >     stderr.writeln("An unexpected error occured.");
> >     return -1;
> >}
> >
> >You can't do _anything_ like that right now.
> 
> Of course I can. They call it toString(). The code above pretty much proves my point with so much wonderful irony.
[...]

One word: internationalization. Then toString() falls flat on its face.

You can't even fix this by having Phobos do the translation internally. You can't expect users of Phobos to only ever support languages that Phobos has been previously translated to, for one thing. That would be a crippling disability.


T

-- 
Valentine's Day: an occasion for florists to reach into the wallets of nominal lovers in dire need of being reminded to profess their hypothetical love for their long-forgotten.
February 19, 2012
On Sat, Feb 18, 2012 at 10:21:17PM -0200, Jose Armando Garcia wrote:
> On Sat, Feb 18, 2012 at 9:13 PM, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote:
> > On 2/18/12 4:26 PM, Jonathan M Davis wrote (abridged):
> > GetOptException
> > FlagArgumentMissingException
> > InvalidFlagArgumentException
> > UnknownFlagException
> > FileException
> > FileNotFoundException
> > NotFileException
> > NotDirException
> > AccessDeniedException
> >
> > I died inside a little.
> 
> Yeah. This gets even worse by the fact that some times you have to import vendor specific modules with their own set of exceptions. For example the JDBC exception model was just a nightmare. Depending on which database driver you used you could have a completely different set of exceptions you had to catch.
> 
> The Spring Framework solved this problem by just throwing just one exception: SQLException (if my memory serves me right). The developer then had to switch based on the driver type and the error code. I don't think this is perfect but I think it works much better in practice.

That simply means Phobos has to be designed such that vendor-specific modules throw exceptions that inherit from SQLException.

This is merely the symptom of a poorly-designed exception hierarchy, not of the fact that there is a hierarchy.


> The other nice thing about this model is that it is easier to know which error you want to handle. Database vendor document their errors using error code not language specific exception. If the Oracle manual says that the database gives error XXXX for such and such condition how in the world do you know to which JDBC exception does that match or D exception, etc.?

Simple. By doing this:

	class OracleException : SQLException {
		OracleErrorCode_t code;
		this(...) {...}
	}

Then if your code just wants to handle generic SQL errors, you catch SQLException. If your code is ready to deal with Oracle error codes, then catch OracleDriverException and process the code.

Just because some other exception *may* derive from OracleException doesn't mean you have to specifically catch that. Just catch the base class and read the error code yourself. That's what a class hierarchy is for!!

I can't believe something this simple has to be explained so elaborately. I thought all of us here knew how to use OO??


T

-- 
"You know, maybe we don't *need* enemies." "Yeah, best friends are about all I can take." -- Calvin & Hobbes
February 19, 2012
On Saturday, February 18, 2012 16:46:43 H. S. Teoh wrote:
> I can't believe something this simple has to be explained so elaborately. I thought all of us here knew how to use OO??

I think that the problem stems from people frequently using exceptions incorrectly, and many of the C++ programmers probably haven't _ever_ seen them used correctly, since I don't think that it's very common for C++ programs to define exception hierarchies - especially not advanced ones like Java has. And when you see a lot of bad exception code, that tends to turn you off to them, and it definitely doesn't show you how to use them correctly.

- Jonathan M Davis
February 19, 2012
Le 19/02/2012 01:09, Andrei Alexandrescu a écrit :
> On 2/18/12 6:03 PM, Jonathan M Davis wrote:
>> On Saturday, February 18, 2012 17:30:10 Andrei Alexandrescu wrote:
>>> The alternative is with virtuals. Do you see a lot of virtuals in base
>>> exceptions? Do you see dramatically different interface for different
>>> exception types?
>>
>> You mean virtual functions? The problem is that each exception type
>> could have
>> information specific to _it_ which makes no sense in a base class. For
>> instance, Exception does to have errno in it. FileException does.
>>
>> If we have GetOptException, it should have a variable for which flag
>> failed.
>> Exception doesn't have that. And what if the flag failed because it
>> was given a
>> bad argument? Then the exception needs a field for that argument. Then
>> you can
>> get something like
>>
>> try
>> getopt(args, ...)
>> catch(MissingArgumentException mae)
>> {
>> stderr.writefln("%s is missing an argument", mae.flag);
>> return -1;
>> }
>> catch(InvalidArgumentException iae)
>> {
>> stderr.writelfln("%s is not a valid argument for %s. You must give it a
>> %s.", mae.arg, mae.flag, mae.expectedType);
>> return -1;
>> }
>> catch(UnknownFlagException ufe)
>> {
>> stderr.writefln("%s is not a known flag.", ufe.ufe);
>> return -1;
>> }
>> catch(GetOptException goe)
>> {
>> stderr.writefln("There was an error with %s", goe.flag);
>> return -1;
>> }
>> //A delegate that you passed to getopt threw an exception.
>> catch(YourException ye)
>> {
>> //...
>> }
>> catch(Exception e)
>> {
>> stderr.writeln("An unexpected error occured.");
>> return -1;
>> }
>>
>> You can't do _anything_ like that right now.
>
> Of course I can. They call it toString(). The code above pretty much
> proves my point with so much wonderful irony.
>
> Andrei
>

Tgat is not even remotely close to a solution. And you'd notice it if you try to write some code about that.
February 19, 2012
On Sat, Feb 18, 2012 at 10:46 PM, H. S. Teoh <hsteoh@quickfur.ath.cx> wrote:
> On Sat, Feb 18, 2012 at 10:21:17PM -0200, Jose Armando Garcia wrote:
>> On Sat, Feb 18, 2012 at 9:13 PM, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote:
>> > On 2/18/12 4:26 PM, Jonathan M Davis wrote (abridged):
>> > GetOptException
>> > FlagArgumentMissingException
>> > InvalidFlagArgumentException
>> > UnknownFlagException
>> > FileException
>> > FileNotFoundException
>> > NotFileException
>> > NotDirException
>> > AccessDeniedException
>> >
>> > I died inside a little.
>>
>> Yeah. This gets even worse by the fact that some times you have to import vendor specific modules with their own set of exceptions. For example the JDBC exception model was just a nightmare. Depending on which database driver you used you could have a completely different set of exceptions you had to catch.
>>
>> The Spring Framework solved this problem by just throwing just one exception: SQLException (if my memory serves me right). The developer then had to switch based on the driver type and the error code. I don't think this is perfect but I think it works much better in practice.
>
> That simply means Phobos has to be designed such that vendor-specific modules throw exceptions that inherit from SQLException.
>
> This is merely the symptom of a poorly-designed exception hierarchy, not of the fact that there is a hierarchy.
>
>
>> The other nice thing about this model is that it is easier to know which error you want to handle. Database vendor document their errors using error code not language specific exception. If the Oracle manual says that the database gives error XXXX for such and such condition how in the world do you know to which JDBC exception does that match or D exception, etc.?
>
> Simple. By doing this:
>
>        class OracleException : SQLException {
>                OracleErrorCode_t code;
>                this(...) {...}
>        }
>

You basically agree that we need error codes inside the exception to handle database errors correctly. What about POSIX error codes? Basically in some cases you are solving the problem using inheritance in other cases you are solving the problem by switching on a variable.

> Then if your code just wants to handle generic SQL errors, you catch SQLException. If your code is ready to deal with Oracle error codes, then catch OracleDriverException and process the code.
>
> Just because some other exception *may* derive from OracleException doesn't mean you have to specifically catch that. Just catch the base class and read the error code yourself. That's what a class hierarchy is for!!
>
> I can't believe something this simple has to be explained so elaborately. I thought all of us here knew how to use OO??
>

Just worry about what you know and don't know...

>
> T
>
> --
> "You know, maybe we don't *need* enemies." "Yeah, best friends are about all I can take." -- Calvin & Hobbes
February 19, 2012
On Sat, 18 Feb 2012 18:40:50 -0600, H. S. Teoh <hsteoh@quickfur.ath.cx> wrote:
> On Sat, Feb 18, 2012 at 06:09:30PM -0600, Andrei Alexandrescu wrote:
>> On 2/18/12 6:03 PM, Jonathan M Davis wrote:
> [...]
>> >If we have GetOptException, it should have a variable for which flag failed.
>> >Exception doesn't have that. And what if the flag failed because it was given a
>> >bad argument? Then the exception needs a field for that argument. Then you can
>> >get something like
>> >
>> >try
>> >     getopt(args, ...)
>> >catch(MissingArgumentException mae)
>> >{
>> >     stderr.writefln("%s is missing an argument", mae.flag);
>> >     return -1;
>> >}
>> >catch(InvalidArgumentException iae)
>> >{
>> >     stderr.writelfln("%s is not a valid argument for %s. You must give it a
>> >%s.", mae.arg, mae.flag, mae.expectedType);
>> >     return -1;
>> >}
>> >catch(UnknownFlagException ufe)
>> >{
>> >     stderr.writefln("%s is not a known flag.", ufe.ufe);
>> >     return -1;
>> >}
>> >catch(GetOptException goe)
>> >{
>> >     stderr.writefln("There was an error with %s",  goe.flag);
>> >     return -1;
>> >}
>> >//A delegate that you passed to getopt threw an exception.
>> >catch(YourException ye)
>> >{
>> >     //...
>> >}
>> >catch(Exception e)
>> >{
>> >     stderr.writeln("An unexpected error occured.");
>> >     return -1;
>> >}
>> >
>> >You can't do _anything_ like that right now.
>>
>> Of course I can. They call it toString(). The code above pretty much
>> proves my point with so much wonderful irony.
> [...]
>
> One word: internationalization. Then toString() falls flat on its face.
>
> You can't even fix this by having Phobos do the translation internally.
> You can't expect users of Phobos to only ever support languages that
> Phobos has been previously translated to, for one thing. That would be a
> crippling disability.

That an argument of an internationalization module as part of the standard library, not for or against a particular exception module. I don't know what a good/robust module would look like, but many open source projects simply use a global string[string] and it seems to work okay for them.
February 19, 2012
Le 19/02/2012 01:36, H. S. Teoh a écrit :
> On Sat, Feb 18, 2012 at 05:13:16PM -0600, Andrei Alexandrescu wrote:
>> On 2/18/12 4:26 PM, Jonathan M Davis wrote (abridged):
>> GetOptException
>> FlagArgumentMissingException
>> InvalidFlagArgumentException
>> UnknownFlagException
>> FileException
>> FileNotFoundException
>> NotFileException
>> NotDirException
>> AccessDeniedException
>>
>> I died inside a little.
> [...]
>
> That's because you're missing the hierarchy, which may look more like
> this:
>
> Error
>   +--Exception
>       +--GetOptException (bad name, I prefer CommandLineException)
>       |   +--FlagArgumentMissingException
>       |   +--InvalidFlagArgumentException
>       |   +--UnknownFlagException
>       +--FileException
>       |   +--FileNotFoundException
>       |   +--NotFileException
>       |   +--NotDirException
>       |   +--AccessDeniedException
>       +--IOException (OK, I added this branch, just to show the idea)
>           +--ReadErrorException
> 	 +--WriteErrorException
>
> So if all you care about is to handle command-line option parse errors,
> and don't care which error it is, you could just catch GetOptException.
> If all you care about is file access exceptions, you can just catch
> FileException without having to worry which specific exception it is.
>
> Alternatively, if you don't care which exception it is at all, then
> just catch Exception.
>
> That's the whole point of a (well-designed) exception hierarchy. It lets
> you be as generic or as specific as you want.
>
> Note also, that an elaborated exception hierarchy lets you attach
> additional specific information that might be useful in error recovery.
> For example, FileException may have a field containing the filename that
> caused the error, so that if the program is processing a list of
> filenames, it knows which one went wrong. You would not want such
> information in Exception, because it only applies to FileException's.
>
> Similarly, GetOptException (or CommandLineException) may have additional
> fields to indicate which option is malformed. Such information,
> obviously, shouldn't be in other kinds of exceptions but those that
> inherit GetOptException.
>
>
> T
>

I hope somebody will listen to you !
February 19, 2012
H. S. Teoh:

> Error
>  +--Exception
>      +--GetOptException (bad name, I prefer CommandLineException)
>      |   +--FlagArgumentMissingException
>      |   +--InvalidFlagArgumentException
>      |   +--UnknownFlagException
>      +--FileException
>      |   +--FileNotFoundException
>      |   +--NotFileException
>      |   +--NotDirException
>      |   +--AccessDeniedException
>      +--IOException (OK, I added this branch, just to show the idea)
>          +--ReadErrorException
> 	 +--WriteErrorException


In Python 3.2 the class hierarchy for built-in exceptions is:

BaseException
 +-- SystemExit
 +-- KeyboardInterrupt
 +-- GeneratorExit
 +-- Exception
      +-- StopIteration
      +-- ArithmeticError
      |    +-- FloatingPointError
      |    +-- OverflowError
      |    +-- ZeroDivisionError
      +-- AssertionError
      +-- AttributeError
      +-- BufferError
      +-- EnvironmentError
      |    +-- IOError
      |    +-- OSError
      |         +-- WindowsError (Windows)
      |         +-- VMSError (VMS)
      +-- EOFError
      +-- ImportError
      +-- LookupError
      |    +-- IndexError
      |    +-- KeyError
      +-- MemoryError
      +-- NameError
      |    +-- UnboundLocalError
      +-- ReferenceError
      +-- RuntimeError
      |    +-- NotImplementedError
      +-- SyntaxError
      |    +-- IndentationError
      |         +-- TabError
      +-- SystemError
      +-- TypeError
      +-- ValueError
      |    +-- UnicodeError
      |         +-- UnicodeDecodeError
      |         +-- UnicodeEncodeError
      |         +-- UnicodeTranslateError
      +-- Warning
           +-- DeprecationWarning
           +-- PendingDeprecationWarning
           +-- RuntimeWarning
           +-- SyntaxWarning
           +-- UserWarning
           +-- FutureWarning
           +-- ImportWarning
           +-- UnicodeWarning
           +-- BytesWarning
           +-- ResourceWarning


Bye,
bearophile