February 18, 2012 The Right Approach to Exceptions | ||||
---|---|---|---|---|
| ||||
There's a discussion that started in a pull request: https://github.com/alexrp/phobos/commit/4b87dcf39efeb4ddafe8fe99a0ef9a529c0dcaca Let's come up with a good doctrine for exception defining and handling in Phobos. From experience I humbly submit that catching by type is most of the time useless. Andrei |
February 18, 2012 Re: The Right Approach to Exceptions | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | On Sat, 18 Feb 2012 19:52:05 +0100, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote: > There's a discussion that started in a pull request: > > https://github.com/alexrp/phobos/commit/4b87dcf39efeb4ddafe8fe99a0ef9a529c0dcaca > > Let's come up with a good doctrine for exception defining and handling in Phobos. From experience I humbly submit that catching by type is most of the time useless. > > > Andrei Exception are useful for handling unknown errors and probably to bundle error handling at a bigger scope. You don't want to use specific exceptions because it couples unrelated code. The distinction of recoverable Exceptions and non-recoverable Errors is good enough in most cases. Typed exception being used for local error recovery is about the same as using error codes but at a bigger expense. On the plus side exception can carry more specific error messages, which could be solved for error codes too. |
February 18, 2012 Re: The Right Approach to Exceptions | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | Le 18/02/2012 19:52, Andrei Alexandrescu a écrit :
> There's a discussion that started in a pull request:
>
> https://github.com/alexrp/phobos/commit/4b87dcf39efeb4ddafe8fe99a0ef9a529c0dcaca
>
>
> Let's come up with a good doctrine for exception defining and handling
> in Phobos. From experience I humbly submit that catching by type is most
> of the time useless.
>
>
> Andrei
I think your oppinion here is shaped by C++. For what I experienced, in C++, exception are only usefull for very important problem you cannot possibly solve, and at the best log the error and exit.
An exemple is std::bad_alloc .
However, in D, I think this is more the role of an Errors. Exception are something « softer ». It will alert you on problems your program encounter, but that are recoverable.
You cannot recover from any exception at any place (sometime, you just cannot at all).
Let's get an exemple : your program ask a file to the user and do some operations with this file. If the file doesn't exists, you can prompt for another file to the user with a meaningful message and start again. However, the first version of your program can just ignore that case and fail with a less specific handler in firsts versions.
You cannot achieve something like that if you don't have a useful type to rely on. Here something like FileNotFoundException is what you want.
The type of the exception must depend on the problem you are facing, not on the module that trhow it. I see a lot of people doing the « MyProgramException » or « MyLibException » but that doesn't make sense. In this case, you are just making things harder.
Back on the original subject, GetOptException is not what you want. As getopt is supposed to handle command line parameters, you'll use exception like : MissingParameterException, WrongFormatException, UnknowParameterException or anything that is meaningful.
Those Exceptions would inherit from something like CommandLineException. This is useful because it describe what you are facing. Because you cant to know what is the problem, not which piece of code face the problem.
If this politic is choosen, then It would make sense to have several modules of phobos throwing exceptions of the same type, or inheriting from the same base class.
Exception type is a convenient way to filter what you catch and what you don't know how to handle at this point.
|
February 18, 2012 Re: The Right Approach to Exceptions | ||||
---|---|---|---|---|
| ||||
Posted in reply to Martin Nowak | Le 18/02/2012 20:12, Martin Nowak a écrit :
> Typed exception being used for local error recovery is about the same as
> using
> error codes but at a bigger expense. On the plus side exception can
> carry more
> specific error messages, which could be solved for error codes too.
The problem with error code is that you have to handle all problems, or none. A class hierarchy of exceptions allow you to handle some type of errors, and not some others.
At this point, I don't think we should think in terms of expansive or not. Exception, as it is named, are for exceptionnal cases. If not, you are probably using them the wrong way. And if it is exceptionnal, then it seems reasoanble to sacrifice some CPU cycles to handle things properly.
|
February 18, 2012 Re: The Right Approach to Exceptions | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | On Saturday, 18 February 2012 at 18:52:05 UTC, Andrei Alexandrescu wrote:
> There's a discussion that started in a pull request:
>
> https://github.com/alexrp/phobos/commit/4b87dcf39efeb4ddafe8fe99a0ef9a529c0dcaca
>
> Let's come up with a good doctrine for exception defining and handling in Phobos. From experience I humbly submit that catching by type is most of the time useless.
>
>
> Andrei
Here's a compromise I would suggest: we have the different exception types for different exceptional behaviors, but still they all descend from a common exception type that has a field with the module name; this way, the client can choose which way they want to go.
It would be nice if there was a mixin template that creates an exception class that acts like this; making similar exception classes is annoying.
|
February 18, 2012 Re: The Right Approach to Exceptions | ||||
---|---|---|---|---|
| ||||
Posted in reply to deadalnix | On 18-02-2012 20:20, deadalnix wrote: > Le 18/02/2012 19:52, Andrei Alexandrescu a écrit : >> There's a discussion that started in a pull request: >> >> https://github.com/alexrp/phobos/commit/4b87dcf39efeb4ddafe8fe99a0ef9a529c0dcaca >> >> >> >> Let's come up with a good doctrine for exception defining and handling >> in Phobos. From experience I humbly submit that catching by type is most >> of the time useless. >> >> >> Andrei > > I think your oppinion here is shaped by C++. For what I experienced, in > C++, exception are only usefull for very important problem you cannot > possibly solve, and at the best log the error and exit. > > An exemple is std::bad_alloc . > > However, in D, I think this is more the role of an Errors. Exception are > something « softer ». It will alert you on problems your program > encounter, but that are recoverable. > > You cannot recover from any exception at any place (sometime, you just > cannot at all). > > Let's get an exemple : your program ask a file to the user and do some > operations with this file. If the file doesn't exists, you can prompt > for another file to the user with a meaningful message and start again. > However, the first version of your program can just ignore that case and > fail with a less specific handler in firsts versions. > > You cannot achieve something like that if you don't have a useful type > to rely on. Here something like FileNotFoundException is what you want. > > The type of the exception must depend on the problem you are facing, not > on the module that trhow it. I see a lot of people doing the « > MyProgramException » or « MyLibException » but that doesn't make sense. > In this case, you are just making things harder. > > Back on the original subject, GetOptException is not what you want. As > getopt is supposed to handle command line parameters, you'll use > exception like : MissingParameterException, WrongFormatException, > UnknowParameterException or anything that is meaningful. > > Those Exceptions would inherit from something like CommandLineException. > This is useful because it describe what you are facing. Because you cant > to know what is the problem, not which piece of code face the problem. Point taken. I suggested GetOptException initially because, based on usage patterns of std.getopt, it seems to be most common to just catch the exception (be it a parsing error, type coercion error, or whatever) and print it. > > If this politic is choosen, then It would make sense to have several > modules of phobos throwing exceptions of the same type, or inheriting > from the same base class. > > Exception type is a convenient way to filter what you catch and what you > don't know how to handle at this point. -- - Alex |
February 18, 2012 Re: The Right Approach to Exceptions | ||||
---|---|---|---|---|
| ||||
Posted in reply to deadalnix | On Sat, 18 Feb 2012 20:24:16 +0100, deadalnix <deadalnix@gmail.com> wrote:
> Le 18/02/2012 20:12, Martin Nowak a écrit :
>> Typed exception being used for local error recovery is about the same as
>> using
>> error codes but at a bigger expense. On the plus side exception can
>> carry more
>> specific error messages, which could be solved for error codes too.
>
> The problem with error code is that you have to handle all problems, or none. A class hierarchy of exceptions allow you to handle some type of errors, and not some others.
>
> At this point, I don't think we should think in terms of expansive or not. Exception, as it is named, are for exceptionnal cases. If not, you are probably using them the wrong way. And if it is exceptionnal, then it seems reasoanble to sacrifice some CPU cycles to handle things properly.
std.file.remove is a good example of how exceptions are used wrong.
It does not allow me to recover from an error, i.e. by changing the file attributes.
Using a cenforce like translator is a much better approach IMO.
|
February 18, 2012 Re: The Right Approach to Exceptions | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | On Sat, Feb 18, 2012 at 12:52:05PM -0600, Andrei Alexandrescu wrote: > There's a discussion that started in a pull request: > > https://github.com/alexrp/phobos/commit/4b87dcf39efeb4ddafe8fe99a0ef9a529c0dcaca > > Let's come up with a good doctrine for exception defining and handling in Phobos. From experience I humbly submit that catching by type is most of the time useless. [...] It's only useless because of a poorly-designed exception hierarchy. Java, for example, has useful things like FileNotFoundException, DiskFullException, etc., that you can catch to handle specific problems in a specific way. They are also part of a well-designed hierarchy. For example, both of the above exceptions are subsumed under IOException, so if you wanted to handle a general I/O exception and don't care which one it is, you could just catch IOException. Now, granted, there are limitations to such a design, for example if you want to catch a category of exceptions that don't map precisely to a node in the inheritance hierarchy. It may be possible to deal with such situations by making Exception an interface instead, although that may introduce other issues. Either that, or allow a list of exceptions in a catch() block, but that would require some further thought, because blindly allowing multiple arguments to catch introduces an initialization problem: try { ... } catch(FileNotFoundException e1, DiskFullException e2) { // which of e1, e2 is actually thrown? } The problem with this approach is the proliferation of of exception classes, many of which differ only in fine ways that most applications wouldn't even care about. The basic problem here is that we are mapping what amounts to error codes to classes. C++ does allow throwing arbitrary objects (IIRC), so in a sense you *could* throw an error code instead of a class object. But that's not necessarily a good thing, because then you end up with different modules throwing different, mutually incompatible enumerated error codes, which brings us back to square two (not square one because at least we don't have to manually propagate error codes in every level of the call stack), where we have a bunch of incompatible error types and we don't necessarily know what to do with them. Having a common root to all exceptions is a good thing. T -- Try to keep an open mind, but not so open your brain falls out. -- theboz |
February 18, 2012 Re: The Right Approach to Exceptions | ||||
---|---|---|---|---|
| ||||
Posted in reply to Martin Nowak | On Sat, Feb 18, 2012 at 08:12:43PM +0100, Martin Nowak wrote: [...] > Exception are useful for handling unknown errors and probably to bundle error handling at a bigger scope. But the way exceptions are used in D, they are more like errors than exceptions. The approach in principle is on the right track, because you want to be able to write: Result_t complicatedFunc(T args) { doComplexProcessing(args[0]); doMoreComplexProcessing(args[1]); doYetMoreComplexProcessing(args[2]); ... return result; } instead of: Result_t complicatedFunc(T args) { if (doComplexProcessing(args[0]) != OK) { return Result_t.FAILURE_A; } if (doMoreComplexProcessing(args[1]) != OK) { return Result_t.FAILURE_B; } if (doYetMoreComplexProcessing(args[2]) != OK) { return Result_t.FAILURE_C; } ... return result; } The assumption is that *usually* the computation should succeed, but exceptional conditions do occur, and sometimes you need to distinguish between them. For example: void doComputation() { try { result = complicatedFunc(...); } catch(FailureA e) { /* recover from FailureA */ } /* FailureB and FailureC considered fatal */ } > You don't want to use specific exceptions because it couples unrelated code. The distinction of recoverable Exceptions and non-recoverable Errors is good enough in most cases. Not true, especially in library code. If you try to open a file, FileNotFoundException should be recoverable, but DiskFailureException should not. You need to distinguish between them. > Typed exception being used for local error recovery is about the same as using error codes but at a bigger expense. On the plus side exception can carry more specific error messages, which could be solved for error codes too. The assumption is that exceptional conditions are, well, exceptional, so it shouldn't matter if you require extra CPU/memory to handle them. The worst thing is if we enforce generic exceptions instead of specific exceptions, and then code that *needs* to distinguish between different types of errors must pattern-match the error message to tell which one it is. That is unmaintainable, full of opportunities for bugs and i18n problems, and is absolutely NOT the way we want to go. T -- The day Microsoft makes something that doesn't suck is probably the day they start making vacuum cleaners... -- Slashdotter |
February 18, 2012 Re: The Right Approach to Exceptions | ||||
---|---|---|---|---|
| ||||
Posted in reply to Nathan M. Swan | On Sat, Feb 18, 2012 at 08:18:53PM +0100, Nathan M. Swan wrote: > On Saturday, 18 February 2012 at 18:52:05 UTC, Andrei Alexandrescu wrote: > >There's a discussion that started in a pull request: > > > >https://github.com/alexrp/phobos/commit/4b87dcf39efeb4ddafe8fe99a0ef9a529c0dcaca > > > >Let's come up with a good doctrine for exception defining and handling in Phobos. From experience I humbly submit that catching by type is most of the time useless. [...] > Here's a compromise I would suggest: we have the different exception types for different exceptional behaviors, but still they all descend from a common exception type that has a field with the module name; this way, the client can choose which way they want to go. I think deadalnix's approach is better. Exceptions should not be categorized by module. What if the module depends on submodules? Then catching only that module's exceptions will miss exceptions thrown from submodules. No, exceptions need to be based on *semantics* rather than modules, like CommandLineException, not GetOptException. > It would be nice if there was a mixin template that creates an exception class that acts like this; making similar exception classes is annoying. +1. This I agree with. T -- Never step over a puddle, always step around it. Chances are that whatever made it is still dripping. |
Copyright © 1999-2021 by the D Language Foundation