www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - The Right Approach to Exceptions

reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
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
Feb 18 2012
next sibling parent reply "Martin Nowak" <dawg dawgfoto.de> writes:
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.
Feb 18 2012
parent deadalnix <deadalnix gmail.com> writes:
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.
Feb 18 2012
prev sibling next sibling parent reply deadalnix <deadalnix gmail.com> writes:
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.
Feb 18 2012
next sibling parent reply =?ISO-8859-1?Q?Alex_R=F8nne_Petersen?= <xtzgzorex gmail.com> writes:
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
Feb 18 2012
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/18/12 1:27 PM, Alex Rnne Petersen wrote:
 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.

In fact for the vast majority of exceptions it seems to be most common to just catch the exception and print it. Tutorial examples never help, either. They go something like: try { ... stuff ... } catch (DatabaseException e) { print("There was a database exception: ", e); } catch (NetworkException e) { print("There was a network exception: ", e); } etc. Andrei
Feb 18 2012
parent reply deadalnix <deadalnix gmail.com> writes:
Le 18/02/2012 22:14, Andrei Alexandrescu a crit :
 On 2/18/12 1:27 PM, Alex Rnne Petersen wrote:
 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.

In fact for the vast majority of exceptions it seems to be most common to just catch the exception and print it. Tutorial examples never help, either. They go something like: try { ... stuff ... } catch (DatabaseException e) { print("There was a database exception: ", e); } catch (NetworkException e) { print("There was a network exception: ", e); } etc. Andrei

This is a dumb example. This show more the fact that Java forcing you to catch every single expression is a bad idea than the fact that having several types of exception is bad. If you have nothing to do with that exception and don't know how to recover from it, just don't catch it. Or catch them all (Pokemon !!!) using catch(Exception e). You know that real life programming is different than a tutorial.
Feb 18 2012
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/18/12 5:30 PM, deadalnix wrote:
 You know that  real life  programming is different than a tutorial.

Problem is (a) all tutorials suggests you have a world of things to do; (b) all real code does one thing. Andrei
Feb 18 2012
next sibling parent deadalnix <deadalnix gmail.com> writes:
Le 19/02/2012 00:26, Andrei Alexandrescu a crit :
 On 2/18/12 5:30 PM, deadalnix wrote:
 You know that  real life  programming is different than a tutorial.

Problem is (a) all tutorials suggests you have a world of things to do; (b) all real code does one thing. Andrei

And usually, one thing has several way to fail.
Feb 18 2012
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/18/12 5:47 PM, Jakob Ovrum wrote:
 you are basically arguing against exceptions here

I must have argued my question and point very poorly. Andrei
Feb 18 2012
next sibling parent "Alf P. Steinbach" <alf.p.steinbach+usenet gmail.com> writes:
On 19.02.2012 02:17, H. S. Teoh wrote:
 The basic problem with C++ exceptions is that it allows you to throw
 literally *anything*. Which is nice and generous, but not having a
 common root to all exceptions cripples the system, because there is no
 functionality that can be depended upon, like toString when what you
 want is an error message.

No, that is a misunderstanding (completely). However, C++ exceptions do have many problems, including: * Until C++11, no support for propagating exceptions through non-exception-aware code (in particular, from C callbacks). * No support for wchar_t exception text, i.e. *nix-specific. * A nonsensical exception class hierarchy with e.g. std::logic_error and std::bad_exception. * No differentiation between recoverable (soft, failure) and unrecoverable (hard, error) exceptions, although some people have argued that in the latter case one is screwed anyway. * Involving std::string arguments, so that in low memory conditions throwing an exception can itself throw. Especially the last point is pretty silly. :-) And then when C++11 added support for propagating error codes, it was designed as *nix-specific (char-based messages only) and overly complex. I haven't heard about anyone actually using that stuff.
 Furthermore, not having a properly designed skeletal hierarchy to
 inherit from also adds to the problem. With no existing standard
 hierarchy to serve as a reference point, library writers just invent
 their own systems, most of which don't interoperate properly with each
 other. And thus the mess that is the C++ exception hierarchy.

 I certainly hope D can do better than that.

Agreed. Cheers, - Alf
Feb 18 2012
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/18/12 6:28 PM, Jonathan M Davis wrote:
 On Saturday, February 18, 2012 17:53:52 Andrei Alexandrescu wrote:
 On 2/18/12 5:47 PM, Jakob Ovrum wrote:
 you are basically arguing against exceptions here

I must have argued my question and point very poorly.

You definitely seem to be arguing for a single Exception type

No. Andrei
Feb 18 2012
next sibling parent "Nick Sabalausky" <a a.a> writes:
"Bernard Helyer" <b.helyer gmail.com> wrote in message 
news:npnjfynxdnpykaemlqrm forum.dlang.org...
 On Sunday, 19 February 2012 at 02:27:07 UTC, Andrei Alexandrescu wrote:
 On 2/18/12 6:28 PM, Jonathan M Davis wrote:
 On Saturday, February 18, 2012 17:53:52 Andrei Alexandrescu wrote:
 On 2/18/12 5:47 PM, Jakob Ovrum wrote:
 you are basically arguing against exceptions here

I must have argued my question and point very poorly.

You definitely seem to be arguing for a single Exception type

No.

Yes. Bernard (This game is fun!)

No it isn't ;)
Feb 18 2012
prev sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/18/12 9:50 PM, Bernard Helyer wrote:
 On Sunday, 19 February 2012 at 02:27:07 UTC, Andrei Alexandrescu wrote:
 On 2/18/12 6:28 PM, Jonathan M Davis wrote:
 On Saturday, February 18, 2012 17:53:52 Andrei Alexandrescu wrote:
 On 2/18/12 5:47 PM, Jakob Ovrum wrote:
 you are basically arguing against exceptions here

I must have argued my question and point very poorly.

You definitely seem to be arguing for a single Exception type

No.

Yes.

Wait, as the guy who actually has the ground truth, don't I have some sort of priority? I have no interest in lying about this. Andrei
Feb 18 2012
prev sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/18/12 7:17 PM, H. S. Teoh wrote:
 On Sat, Feb 18, 2012 at 04:28:25PM -0800, Jonathan M Davis wrote:
 [...]
 C++ is a horrible example of how exceptions should be done, so if
 you're basing what you want off of that, then that makes me think that
 you should be better familiar with how other, more recent languages
 use them (though maybe you're quite familiar with how C# and/or Java
 use Exceptions, I don't know).  From using Java, I think that how it
 handles exceptions in general is _far_ superior to how they're
 frequently dealt with in C++ (though that does tend to depend on who's
 doing the developing, since you _can_ have a decent exception
 hierarchy in C++).

The basic problem with C++ exceptions is that it allows you to throw literally *anything*.

I agree that that was a design mistake, but it's easy to opt out the unnecessary part. All C++ applications I've worked with define exception hierarchies. In fact at Facebook we have a lint rule that prohibits throwing arbitrary stuff. Andrei
Feb 18 2012
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Saturday, February 18, 2012 15:14:04 Andrei Alexandrescu wrote:
 On 2/18/12 1:27 PM, Alex R=C3=B8nne Petersen wrote:
 Point taken. I suggested GetOptException initially because, based o=


 usage patterns of std.getopt, it seems to be most common to just ca=


 the exception (be it a parsing error, type coercion error, or whate=


 and print it.

In fact for the vast majority of exceptions it seems to be most commo=

 to just catch the exception and print it.
=20
 Tutorial examples never help, either. They go something like:
=20
 try {
    ... stuff ...
 } catch (DatabaseException e) {
    print("There was a database exception: ", e);
 } catch (NetworkException e) {
    print("There was a network exception: ", e);
 }
=20
 etc.

If that's what you're doing, then you can just catch Exception. But if = you're=20 actually going to _use_ the fact that you have typed Exceptions, then y= ou can=20 do error handling specific to what went wrong, and it can be very usefu= l. It becomes even more important with more complex programs. If I call a=20= function, and it throws a FileException, then I know that the operation= failed=20 to operate on the file properly and can handle it appropriately, wherea= s if it=20 threw a ParserException, then I know that it failed in parsing the file= and can=20 handle _that_ appropriately. It gets even better when you get more spec= ific=20 exceptions such as FileNotFoundException or FileOpenFailedException, be= cause=20 then you can handle stuff very specific to what exactly what went wrong= . Exception can't give you any of that. All it tells you is that somethin= g went=20 wrong, and the best that you can do is print a message like your exampl= e does. I'd hate to see us have a crippled exception hierarchy just because som= e=20 people mishandle exceptions and simply print out messages on failure in= all=20 cases. Obviously sometimes that's what you have to do, but often you ca= n=20 handle them much better, and that's part of the reason that we have=20= _Exception_ and not just Error. And a proper exception hierarchy can be= very=20 benificial to programmers who actually use it correctly. - Jonathan M Davis
Feb 18 2012
prev sibling parent "Ben Hanson" <Ben.Hanson tikit.com> writes:
On Saturday, 18 February 2012 at 22:35:58 UTC, Jonathan M Davis
wrote:
 I'd hate to see us have a crippled exception hierarchy just 
 because some
 people mishandle exceptions and simply print out messages on 
 failure in all
 cases. Obviously sometimes that's what you have to do, but 
 often you can
 handle them much better, and that's part of the reason that we  
 have
 _Exception_ and not just Error. And a proper exception 
 hierarchy can be very
 benificial to programmers who actually use it correctly.

+1 Inheritance in general can (and is) misused too. Does that mean that should also be banned? Regards, Ben
Mar 05 2012
prev sibling next sibling parent reply =?ISO-8859-1?Q?Alex_R=F8nne_Petersen?= <xtzgzorex gmail.com> writes:
On 18-02-2012 21:36, H. S. Teoh wrote:
 On Sat, Feb 18, 2012 at 08:20:23PM +0100, deadalnix wrote:
 [...]
 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.

I agree. [...]
 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.

Exactly. It's important to distinguish between exceptions sometimes. For example: File promptSaveFile() { do { string filename = readInput(); try { return File(filename); } catch(FileNotFoundException e) { writeln("No such file, please try " "again"); } } while(true); assert(false); } It would not make sense for that catch to be *any* Exception; for example, if the exception was ReadFailureException, you do *not* want to catch that, but you want to propagate it. But sometimes, you *want* to handle ReadFailureException, for example: void salvageBadSector(out ubyte[] sector) { int n_attempts = 10; while(n_attempts> 0) { try { disk.read(sector); } catch(ReadFailureException e) { disk.recalibrate(); n_attempts--; continue; // try again } // If another error occurs, say OutOfMemory, // or some other unexpected problems, you do NOT // want to continue. } writeln("Cannot salvage data from bad sector"); } Having distinct exception types allows you to do this without resorting to hacks, or bypassing the library altogether (because of the use of overly generic exception types). Having a proper exception class hierarchy is also necessary. For example, most programs don't care about the difference between FileNotFoundException and ReadFailureException; they just want to print an error message and cleanup if I/O fails: Data loadData() { try { return readFromFile(); } catch(IOException e) { writeln("I/O error occurred, aborting"); cleanup(); exit(1); } assert(false); } It would be wrong to just catch Exception here, because if it wasn't an I/O error, but something else, it needs to be propagated!
 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.

Agreed. [...]
 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.

Phobos NEEDS to have a sane exception hierarchy. ESPECIALLY since it is the standard library. So you cannot assume your users cares or don't care about distinguishing between exception types. Some applications just catch Exception, cleanup and exit, but other applications need to know the difference between FileNotFoundException and ReadErrorException. So we need to design a good exception hierarchy for Phobos. The worst thing is if Phobos exceptions are too generic, then applications that need to tell between different problems will have to bypass Phobos and hack their own solution. Which is not good. The standard library should be maximally useful to the extent that it can be.

I'd just like to add to this that we don't lose anything per se by introducing specific exception types. People can still catch Exception if they really, really want.
 Exception type is a convenient way to filter what you catch and what
 you don't know how to handle at this point.

Agreed. T

-- - Alex
Feb 18 2012
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 02/18/2012 10:25 PM, Alex Rnne Petersen wrote:
 I'd just like to add to this that we don't lose anything per se by
 introducing specific exception types. People can still catch Exception
 if they really, really want.

We gain executable bloat. I don't know how how significant the amount would be.
Feb 18 2012
parent reply =?ISO-8859-1?Q?Alex_R=F8nne_Petersen?= <xtzgzorex gmail.com> writes:
On 18-02-2012 22:30, Timon Gehr wrote:
 On 02/18/2012 10:25 PM, Alex Rnne Petersen wrote:
 I'd just like to add to this that we don't lose anything per se by
 introducing specific exception types. People can still catch Exception
 if they really, really want.

We gain executable bloat. I don't know how how significant the amount would be.

For a single class that has nothing but a constructor? Negligible IMHO. -- - Alex
Feb 18 2012
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 02/18/2012 10:38 PM, Alex Rnne Petersen wrote:
 On 18-02-2012 22:30, Timon Gehr wrote:
 On 02/18/2012 10:25 PM, Alex Rnne Petersen wrote:
 I'd just like to add to this that we don't lose anything per se by
 introducing specific exception types. People can still catch Exception
 if they really, really want.

We gain executable bloat. I don't know how how significant the amount would be.

For a single class

You want to add only one specific exception type?
 that has nothing but a constructor?

They also have vtbls and TypeInfos.
 Negligible IMHO.

Possible.
Feb 18 2012
parent =?ISO-8859-1?Q?Alex_R=F8nne_Petersen?= <xtzgzorex gmail.com> writes:
On 18-02-2012 23:35, Timon Gehr wrote:
 On 02/18/2012 10:38 PM, Alex Rnne Petersen wrote:
 On 18-02-2012 22:30, Timon Gehr wrote:
 On 02/18/2012 10:25 PM, Alex Rnne Petersen wrote:
 I'd just like to add to this that we don't lose anything per se by
 introducing specific exception types. People can still catch Exception
 if they really, really want.

We gain executable bloat. I don't know how how significant the amount would be.

For a single class

You want to add only one specific exception type?

No, but I have a hard time imagining that even the accumulated amount of exceptions will matter.
 that has nothing but a constructor?

They also have vtbls and TypeInfos.

True. But aren't we micro-optimizing here?
 Negligible IMHO.

Possible.

-- - Alex
Feb 18 2012
prev sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/18/12 3:25 PM, Alex Rnne Petersen wrote:
 I'd just like to add to this that we don't lose anything per se by
 introducing specific exception types.

1. Technical debt 2. Set an example that will be followed 3. Boilerplate Andrei
Feb 18 2012
prev sibling next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Sat, Feb 18, 2012 at 10:38:06PM +0100, Alex Rønne Petersen wrote:
 On 18-02-2012 22:30, Timon Gehr wrote:
On 02/18/2012 10:25 PM, Alex Rønne Petersen wrote:
I'd just like to add to this that we don't lose anything per se by
introducing specific exception types. People can still catch Exception
if they really, really want.

We gain executable bloat. I don't know how how significant the amount would be.

For a single class that has nothing but a constructor? Negligible IMHO.

Especially a ctor that does nothing but forwards arguments to the base class. It will just be inlined 100% of the time. T -- Любишь кататься - люби и саночки возить.
Feb 18 2012
prev sibling next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Sat, Feb 18, 2012 at 03:36:40PM -0600, Andrei Alexandrescu wrote:
 On 2/18/12 3:25 PM, Alex Rnne Petersen wrote:
I'd just like to add to this that we don't lose anything per se by
introducing specific exception types.

1. Technical debt

I don't understand. Could you elaborate?
 2. Set an example that will be followed

If it's a *good* example, it should be followed, no? ;-)
 3. Boilerplate

Can be alleviated if there's a way to use templates to make exception subclasses. But all of this is just the mechanics of the system. We should rather be debating about the conceptual/theoretic merits at this point, than get hung up about the nitty gritty details. That can come later once we've settled on the right conceptual model. T -- Almost all proofs have bugs, but almost all theorems are true. -- Paul Pedersen
Feb 18 2012
prev sibling next sibling parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Saturday, February 18, 2012 20:20:23 deadalnix wrote:
 I think your oppinion here is shaped by C++. For what I experienced, =

 C++, exception are only usefull for very important problem you cannot=

 possibly solve, and at the best log the error and exit.
=20
 An exemple is std::bad_alloc .
=20
 However, in D, I think this is more the role of an Errors. Exception =

 something =C2=AB softer =C2=BB. It will alert you on problems your pr=

 encounter, but that are recoverable.
=20
 You cannot recover from any exception at any place (sometime, you jus=

 cannot at all).
=20
 Let's get an exemple : your program ask a file to the user and do som=

 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 agai=

 However, the first version of your program can just ignore that case =

 fail with a less specific handler in firsts versions.
=20
 You cannot achieve something like that if you don't have a useful typ=

 to rely on. Here something like FileNotFoundException is what you wan=

=20
 The type of the exception must depend on the problem you are facing, =

 on the module that trhow it. I see a lot of people doing the =C2=AB
 MyProgramException =C2=BB or =C2=AB MyLibException =C2=BB but that do=

 In this case, you are just making things harder.
=20
 Back on the original subject, GetOptException is not what you want. A=

 getopt is supposed to handle command line parameters, you'll use
 exception like : MissingParameterException, WrongFormatException,
 UnknowParameterException or anything that is meaningful.
=20
 Those Exceptions would inherit from something like CommandLineExcepti=

 This is useful because it describe what you are facing. Because you c=

 to know what is the problem, not which piece of code face the problem=

=20
 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.
=20
 Exception type is a convenient way to filter what you catch and what =

 don't know how to handle at this point.

In general, I agree with all of this. I very much think that having typ= ed=20 exceptions makes a lot of sense. In general, I think that Java got exce= ptions=20 right (ignoring the issue with checked exceptions). Having typed except= ions=20 works really well. In the case of getopt, what we want is a GetOptException which is for a= nything=20 which goes wrong with getopt so that someone can catch that exception t= ype if=20 they want to just handle the case wheree getopt throws but don't want t= o=20 swallow other exception types by calling exception and pretty much just= want=20 to print an error and exit the program or continue without any comand-l= ine=20 arguments if that's what they prefer to do. Then you have other exception types derived from GetOptException which = deal=20 with specific types of issues - such as FlagArgumentMissingException,=20= InvalidFlagArgumentException, UnknownFlagException. Each of those excep= tion=20 types could then give information which specifically pertains to those = errors -=20 such as the flag that had a problem, the type that the flag is supposed= to=20 receive, the type that the flag actually received, the invalid value th= at the=20 flag was given, etc. Such exceptions can then allow you to properly han= dle and=20 report problems with command-line arguments. Right now, all you know is= that=20 something went wrong, and pretty much the best that you can do is print= out=20 that something went wrong. You can do any decent error handling at all.= You=20 need specific exception types which give you the appropriate informatio= n in=20 order to do that. Another example is FileException. It would be benificial to have except= ions=20 like FileNotFoundException, NotFileException, NotDirException,=20 AccessDeniedException, etc which are derived from FileException. Then p= rograms=20 could handle the specific instance of what went wrong nice and cleanly = rather=20 than having to look at error codes. At least FileException provides an = error=20 code, but that makes for much uglier handling code assuming that you ev= en have=20 any clue what the error codes mean. And since the error codes can vary = from OS=20 to OS, you end up with system-specific error handling code if you try a= nd use=20 errno. Whereas if std.file just translated the error code to the correc= t=20 exception type, you're then very cleanly informed as to what the proble= m was=20 and can catch the exception based on which situtations you can recover = from=20 and which you can't as well as having different catch blocks to handle = different=20 problems differently. Simply having an exception type per module is somewhat better than just= having=20 Exception, because it gives you a better idea of what went wrong (e.g. = you got=20 a UTFException rather than a FileException), but it's still way too gen= eral in=20 a lot of cases, and I can see why some would think that it creates need= less=20 boilerplate code. Also, in some cases, having exception types derive fr= om more=20 general exceptions than what the module focuses on can be useful. For=20= instance, Java has IOException as the base for all IOExceptions. FileEx= ception=20 could be derived from that, and then std.stream and std.stdio could cou= ld have=20 their exception types derive from that as well. Then you could specific= ally=20 handle all exceptions related to I/O together. I'm completely sold on typed exceptions, but I think that we could do b= etter=20 with them than we're currently doing. - Jonathan M Davis
Feb 18 2012
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
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.

Andrei
Feb 18 2012
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/18/12 5:20 PM, Jonathan M Davis wrote:
 On Saturday, February 18, 2012 17:13:16 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.

If you actually want to _handle_ exceptions, how else do you expect to do it? Simply put a code of some kind on the exceptions and then have switch statement to handle them?

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? Andrei
Feb 18 2012
next sibling parent deadalnix <deadalnix gmail.com> writes:
Le 19/02/2012 00:30, Andrei Alexandrescu a écrit :
 On 2/18/12 5:20 PM, Jonathan M Davis wrote:
 On Saturday, February 18, 2012 17:13:16 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.

If you actually want to _handle_ exceptions, how else do you expect to do it? Simply put a code of some kind on the exceptions and then have switch statement to handle them?

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? Andrei

What do you have in mind ?
Feb 18 2012
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
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
Feb 18 2012
next sibling parent deadalnix <deadalnix gmail.com> writes:
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.
Feb 18 2012
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/18/12 6:40 PM, H. S. Teoh wrote:
 One word: internationalization. Then toString() falls flat on its face.

No. I happen to have some expertise in the area as I've participated to two large and heavily internationalized systems. i18n has everything to do with string tables and formatting templates and emphatically nothing to do with exception hierarchies. The only possible link is that exceptions should provide the necessary hooks. Andrei
Feb 18 2012
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/18/12 8:38 PM, Jonathan M Davis wrote:
 On Saturday, February 18, 2012 20:30:45 Andrei Alexandrescu wrote:
 On 2/18/12 6:40 PM, H. S. Teoh wrote:
 One word: internationalization. Then toString() falls flat on its face.

No. I happen to have some expertise in the area as I've participated to two large and heavily internationalized systems. i18n has everything to do with string tables and formatting templates and emphatically nothing to do with exception hierarchies. The only possible link is that exceptions should provide the necessary hooks.

They do in that if you want to print a message because of that exception, you need to print something in the correct language, and that won't work with toString unless the exception type has built-in internationalization of some kind - regardless of what that internationalization mechanism might be.

Of course. toString is not a solution to internationalizing exceptions (or any complex messages). Andrei
Feb 18 2012
prev sibling next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2012-02-19 01:09, Andrei Alexandrescu wrote:
 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

If you actually want to try and recover from the error rather than just simply print the error message, toString is out of the window. -- /Jacob Carlborg
Feb 19 2012
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/19/12 6:48 AM, Jacob Carlborg wrote:
 On 2012-02-19 01:09, Andrei Alexandrescu wrote:
 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

If you actually want to try and recover from the error rather than just simply print the error message, toString is out of the window.

Absolutely. I was answering to the example given. Andrei
Feb 19 2012
prev sibling next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2012-02-19 01:09, Andrei Alexandrescu wrote:
 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

Say you want to do something like what Git does: $ git statu git: 'statu' is not a git command. See 'git --help'. Did you mean this? status $ That would be quite hard with just toString. -- /Jacob Carlborg
Feb 19 2012
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/19/12 6:49 AM, Jacob Carlborg wrote:
 On 2012-02-19 01:09, Andrei Alexandrescu wrote:
 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

Say you want to do something like what Git does: $ git statu git: 'statu' is not a git command. See 'git --help'. Did you mean this? status $ That would be quite hard with just toString.

An exception hierarchy won't make that easier or more modular. What's needed there is the command and a table of all commands. Andrei
Feb 19 2012
parent reply Jacob Carlborg <doob me.com> writes:
On 2012-02-19 15:38, Andrei Alexandrescu wrote:
 On 2/19/12 6:49 AM, Jacob Carlborg wrote:
 On 2012-02-19 01:09, Andrei Alexandrescu wrote:
 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

Say you want to do something like what Git does: $ git statu git: 'statu' is not a git command. See 'git --help'. Did you mean this? status $ That would be quite hard with just toString.

An exception hierarchy won't make that easier or more modular. What's needed there is the command and a table of all commands. Andrei

The command could be in the exception. Then you would have to look it up against all valid commands. For the command to be in the exception you would need some sort of hierarchy because, as others have said, a command property wouldn't make sense for an exception like FileNotFound, and wouldn't make sense in the base class Exception. -- /Jacob Carlborg
Feb 19 2012
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/19/12 10:40 AM, Jacob Carlborg wrote:
 The command could be in the exception. Then you would have to look it up
 against all valid commands. For the command to be in the exception you
 would need some sort of hierarchy because, as others have said, a
 command property wouldn't make sense for an exception like FileNotFound,
 and wouldn't make sense in the base class Exception.

Definitely, no argument there! To restate: all I want is to find ways to figure the "right" number of exception types, and the "right" primitives. Andrei
Feb 19 2012
parent reply deadalnix <deadalnix gmail.com> writes:
Le 19/02/2012 17:48, Andrei Alexandrescu a écrit :
 On 2/19/12 10:40 AM, Jacob Carlborg wrote:
 The command could be in the exception. Then you would have to look it up
 against all valid commands. For the command to be in the exception you
 would need some sort of hierarchy because, as others have said, a
 command property wouldn't make sense for an exception like FileNotFound,
 and wouldn't make sense in the base class Exception.

Definitely, no argument there! To restate: all I want is to find ways to figure the "right" number of exception types, and the "right" primitives. Andrei

Well that must be decided on each case. I think the guideline should be to create a new type when the cause of the exception is useful for the user when it come to handling it. For exemple, on a remove function, you want to have FileNotFoundException specifically. Even if you do if(file_exists(file)) remove(file); a concurent delete can occur. But if your goal is to delete the file, constating that the file is deleted may not be a major problem to you. However, if this operation fail because you don't have access to this file, it is a very different issue, because the file is still here and you still want to delete it. The policy should be something along the line « if it make sense to handle this problem differently than other problems that can occur, then it deserve its own type » I would add that, by thinking at your proposal of exception that may succed if you retry the same thing, phobos should propose a retry function that take as parameter a closure and and limit and will retry the operation until it succeed or that the limit is reached. The more I think of it, the more it make sense to have a property on Exceptions to explicit if a retry may help.
Feb 19 2012
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/19/12 7:53 PM, H. S. Teoh wrote:
 I stand by my objection that if something might succeed if it can be
 retried, then it needs to be retried in the called function, not the
 caller.

If read fails from a socket, it's of no use to try it again. One must close the socket, reconnect, and attempt the whole operation once again. Andrei
Feb 19 2012
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/19/12 8:52 PM, H. S. Teoh wrote:
 On Sun, Feb 19, 2012 at 08:33:04PM -0600, Andrei Alexandrescu wrote:
 On 2/19/12 7:53 PM, H. S. Teoh wrote:
 I stand by my objection that if something might succeed if it can be
 retried, then it needs to be retried in the called function, not the
 caller.

If read fails from a socket, it's of no use to try it again. One must close the socket, reconnect, and attempt the whole operation once again.

Correct, so that would be a recovery strategy at the operation level, say at sendHttpRequest or something like that. There is not enough information available to sendHttpRequest to know whether or not the caller wants the request to be retried if it fails. But if the higher-level code could indicate this by way of a recovery policy delegate, then this retry can be done at the sendHttpRequest level, instead of percolating up the call stack all the way to submitHttpForm, which then has to reparse user data, convert into JSON, say, and then retry the entire operation all over again. I'm really liking the Lisp approach.

Now we're talking. Ideas. Outside the box. Andrei
Feb 19 2012
parent reply deadalnix <deadalnix gmail.com> writes:
Le 20/02/2012 06:57, H. S. Teoh a écrit :
 On Sun, Feb 19, 2012 at 09:12:25PM -0600, Andrei Alexandrescu wrote:
 On 2/19/12 8:52 PM, H. S. Teoh wrote:

 Correct, so that would be a recovery strategy at the operation level,
 say at sendHttpRequest or something like that. There is not enough
 information available to sendHttpRequest to know whether or not the
 caller wants the request to be retried if it fails.

 But if the higher-level code could indicate this by way of a recovery
 policy delegate, then this retry can be done at the sendHttpRequest
 level, instead of percolating up the call stack all the way to
 submitHttpForm, which then has to reparse user data, convert into
 JSON, say, and then retry the entire operation all over again.

 I'm really liking the Lisp approach.

Now we're talking. Ideas. Outside the box.

Alright. This thread has gone on for too long with lots of talk but no down-to-earth, real code. So I decided to code up a quick-n-dirty proof-of-concept implementation of a Lispian scheme of exception handling. The source files are attached. This implementation is more to demonstrate the *semantics* of such a system, rather than a sample code of the real thing. Before anyone jumps on me for writing very bad code. ;-) In a real implementation, I probably wouldn't inherit Condition from Exception, for example. A real implementation would have language-level support for declaring recovery strategies within the function that handles them, rather than the kludge of declaring them separately and abusing the exception system to handle recovery actions. Also, registered handlers would need to be cleaned up upon scope exit, so that they don't cause crazy side-effects in unrelated code later on in the program. But anyway. The point of this code is to demonstrate: (1) How an exception generated in low-level code is recovered within the low-level code without unwinding the stack all the way back to the top-level caller; (2) How the top-level caller can provide a delegate that makes decisions that only the top-level code is qualified to make, yet does so in the context of low-level code so that recovery can proceed without unwinding the stack any more than necessary. I deliberately made the delegate prompt the user for a fixed filename, even allowing the option of aborting if the user wants to give up. This is to show how the system handles different recovery actions. This, of course, isn't the only way to do things; you can programmatically decide on a recovery strategy instead of prompting the user, for example. What do y'all think? T

I read your code with great interest ! I'm not sure about this. This is a lot of code to not achieve that much. OK, the stack hasn't been destroyed, but your delegate has its own scope and cannot do a lot. Isn't a retry function provided by phobos and a transeient property as proposed by Andrei a better alternative ?
Feb 20 2012
parent reply deadalnix <deadalnix gmail.com> writes:
Le 20/02/2012 19:53, H. S. Teoh a crit :
 On Mon, Feb 20, 2012 at 06:37:07PM +0100, deadalnix wrote:
 Le 20/02/2012 06:57, H. S. Teoh a crit :

 Alright. This thread has gone on for too long with lots of talk but
 no down-to-earth, real code. So I decided to code up a quick-n-dirty
 proof-of-concept implementation of a Lispian scheme of exception
 handling. The source files are attached.


 I read your code with great interest ! I'm not sure about this. This
 is a lot of code to not achieve that much. OK, the stack hasn't been
 destroyed, but your delegate has its own scope and cannot do a lot.

The delegate has full access to the scope of main(), or wherever it was registered from, which the low-level error recovery code has no access to.
 Isn't a retry function provided by phobos and a transeient property as
 proposed by Andrei a better alternative ?

The problem is that by the time you regain control at the catch, the stack has already unwound to main(), so if you need to retry, you need to call openDataFile() all over again. In this case it's not too bad, because openDataFile() is very simple. But imagine if this openDataFile() was nested deeply inside a few layers of functions called from main(), then by the time you unwind the stack to main(), all the previous work done is lost, and you have to restart from the beginning. Also, how does the transient property help main() know that it should prompt the user for a different filename? What if a different exception was caught, like NetworkDriveUnreachable? It wouldn't make sense to prompt the user for another filename in this case. T

Yes, I see the point. This has clearly some advantages. Considering the handler, This would be great to see it as scoped. This imply using a scope delegate, and probably something as RAII to create it and delete it when we go out of scope. I'm more doubtfull about what can be done on the throw side. This need to be refined, and to be honnest, the idea is too new in my head to come up with something significant. The concept needs some decantation. I wonder however, how to do something clean. The delegate will need some knowledge of the thrower onternal to do something really usefull, but then, we make the codebase more fragile (because the caler will depend on implementation of the calee, something that we want to avoid).
Feb 20 2012
parent reply deadalnix <deadalnix gmail.com> writes:
Le 20/02/2012 20:34, H. S. Teoh a crit :
 On Mon, Feb 20, 2012 at 08:11:28PM +0100, deadalnix wrote:
 [...]
 Considering the handler, This would be great to see it as scoped.
 This imply using a scope delegate, and probably something as RAII to
 create it and delete it when we go out of scope.

Yeah, that definitely should be done, otherwise a handler installed by a function that's no longer in scope will still kick in when the same exception is triggered by unrelated code. That would be very bad.
 I'm more doubtfull about what can be done on the throw side. This need
 to be refined, and to be honnest, the idea is too new in my head to
 come up with something significant. The concept needs some
 decantation.

 I wonder however, how to do something clean. The delegate will need
 some knowledge of the thrower onternal to do something really usefull,
 but then, we make the codebase more fragile (because the caler will
 depend on implementation of the calee, something that we want to
 avoid).

I've tried to do this by encapsulating the recovery options in the Condition object. So the delegate knows, here are the options I have of recovering, but it doesn't need to know how the low-level function implements those recovery strategies. For example, the FileNotFoundCond object has a restart() method, and an inherited abort() method. So these are the options available to the delegate. These methods just return an opaque object to the recovery system, which passes it to the low-level function. Then the low-level function tries to recover according to which object it receives. So the delegate doesn't actually know how each option is implemented, it just knows these are available options to recover. Also, the restart() method takes a new filename as parameter, so the delegate even knows what data is needed to provide to a particular recovery option in order to help the low-level function recover. Of course, the current implementation is very skeletal, and there are still many open issues: - How does the delegate know *which* low-level function throws a particular Condition? For example, what if main() calls some function that calls openDataFile() many times? What if openDataFile() is called at different points in the function call hierarchy? How does the delegate know which recovery choice is appropriate for every FileNotFoundCond() that it catches? - Every Condition the program might encounter requires a lot of code: you need to define a Condition subclass, populate it with recovery strategies, and then have the throwing code check and handle each option. Is there a way to reduce the amount of code you need to write for every Condition? - Also, Conditions seem to specific to a particular situation. It's not too bad for generic problems like FileNotFoundCond; we know what it means when the OS says "file not found", and we generally know what can be done to fix that. But what about ParseErrorCond? The delegate would need to know what kind of parse error, or even which parser issued that error, otherwise how would it know how to correct the parse error? But this means that every different parser in the program will require its own ParseError subclass, along with the associated recovery options. Seems like a lot of work for only occasional benefit. - Are there general recovery options that apply generally across the majority of Conditions? If so, we can alleviate delegates from having to know the intimate details of a particular operation, which introduces too much coupling between high-level and low-level code. A more minor issue is that the current implementation is not very well written. :) I'll have to think about this more, to know how to better implement it. But I wanted to get the semantics of it out first, so that at least we can start discussing the conceptual aspects of the idea. T

So if I sum up things a bit (and refactor the design a bit according to what is best as I understand it) : 1/ lib don't throw exception, they raise a Condition. 2/ If an handler exists for that condition, then the handler is called, and the condition is passed as parameter. The condition provide an interface so the handler can specify how to handle that condition. 3/ The condition provide a way to throw in its interface. This part of the interface of Condition. 4/ If no handler is found, the throw method of the Condition is called. 5/ The handler is provided in a scoped manner. 6/ This mecanism is provided as a lib. 7/ The mecanism is provided as a richer way to use Exceptions. Is it a solid base to start on ? I seems we need to start working on std.condition ;) BTW, do we need language support to implement that ? I don't think so, but I'm quite new to the issue.
Feb 20 2012
next sibling parent deadalnix <deadalnix gmail.com> writes:
I don't think the Condition should be an Exception. I think it should 
provide a method that throw.

class Condition {
     void throw() pure {           // Or make it abstract
         throw new Exception();
     }
}

If no handler is found, this method is called. The handler can also call it.

It is very important to separate the Condition and the Exception. 
Several Condition may use the same Exception.

BTW, I don't think that something that can be implemented as lib should 
be added in the core language.

Condition may be use using something like raise(new 
MyCondition(params)); with raise from std.condition .

Alternatively, we could go metaprogramming for Condition, just like we 
did for Range.

Le 20/02/2012 21:18, H. S. Teoh a crit :
 6/ This mecanism is provided as a lib.

Not necessarily; it would be nice to have language support. But at least it's implementable as a lib (as I've shown), so we don't have to invest the time and effort to implement language support until we're sure it's a good idea, after thorough testing with the lib.
 7/ The mecanism is provided as a richer way to use Exceptions.

 Is it a solid base to start on ? I seems we need to start working on
 std.condition ;) BTW, do we need language support to implement that ?
 I don't think so, but I'm quite new to the issue.

I'm not sure yet, but probably language support is be needed to reduce the amount of syntax you need to write just to define Conditions. Unless we can figure out a clever way of using templates or mixins or some such to make it easier to write. Ideally, you'd want to declare Conditions inside the catch block of the function, instead of making a separate class definition outside. But I'm not sure how to make such a thing work if the handler needs to know what methods are available in the first place. Perhaps a compromise is to use some templates with mixins to generate code for the Condition subclass and for the custom catch block for handling recovery strategies. Also, another issue that's still unclear to me is, if no handler is found and the Condition gets thrown, does it still make sense to throw the Condition object? Because once the stack unwinds, the recovery methods are probably no longer meaningful. Also, the fact that no handler was found means that no handler will ever be found, so the stack will just unwind to a catch block. And it's doubtful that at that level, the recovery options encoded in the Condition are usable at all. So some parts of this system need some rethinking. T

Feb 20 2012
prev sibling parent deadalnix <deadalnix gmail.com> writes:
PS: The more I think of this solution, the more I like it. It is really 
great !

It is more performant, avoid throwing when not necessary, and allow to 
do more. This is definitively something we should dig.
Feb 20 2012
prev sibling next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 02/20/2012 02:53 AM, H. S. Teoh wrote:
 Perhaps the "ideal exception handling facility" that Andrei is looking
 for is a Lispian model,

+1.
Feb 20 2012
prev sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Mon, Feb 20, 2012 at 08:11:28PM +0100, deadalnix wrote:
[...]
 Considering the handler, This would be great to see it as scoped.
 This imply using a scope delegate, and probably something as RAII to
 create it and delete it when we go out of scope.

Yeah, that definitely should be done, otherwise a handler installed by a function that's no longer in scope will still kick in when the same exception is triggered by unrelated code. That would be very bad.
 I'm more doubtfull about what can be done on the throw side. This need
 to be refined, and to be honnest, the idea is too new in my head to
 come up with something significant. The concept needs some
 decantation.
 
 I wonder however, how to do something clean. The delegate will need
 some knowledge of the thrower onternal to do something really usefull,
 but then, we make the codebase more fragile (because the caler will
 depend on implementation of the calee, something that we want to
 avoid).

I've tried to do this by encapsulating the recovery options in the Condition object. So the delegate knows, here are the options I have of recovering, but it doesn't need to know how the low-level function implements those recovery strategies. For example, the FileNotFoundCond object has a restart() method, and an inherited abort() method. So these are the options available to the delegate. These methods just return an opaque object to the recovery system, which passes it to the low-level function. Then the low-level function tries to recover according to which object it receives. So the delegate doesn't actually know how each option is implemented, it just knows these are available options to recover. Also, the restart() method takes a new filename as parameter, so the delegate even knows what data is needed to provide to a particular recovery option in order to help the low-level function recover. Of course, the current implementation is very skeletal, and there are still many open issues: - How does the delegate know *which* low-level function throws a particular Condition? For example, what if main() calls some function that calls openDataFile() many times? What if openDataFile() is called at different points in the function call hierarchy? How does the delegate know which recovery choice is appropriate for every FileNotFoundCond() that it catches? - Every Condition the program might encounter requires a lot of code: you need to define a Condition subclass, populate it with recovery strategies, and then have the throwing code check and handle each option. Is there a way to reduce the amount of code you need to write for every Condition? - Also, Conditions seem to specific to a particular situation. It's not too bad for generic problems like FileNotFoundCond; we know what it means when the OS says "file not found", and we generally know what can be done to fix that. But what about ParseErrorCond? The delegate would need to know what kind of parse error, or even which parser issued that error, otherwise how would it know how to correct the parse error? But this means that every different parser in the program will require its own ParseError subclass, along with the associated recovery options. Seems like a lot of work for only occasional benefit. - Are there general recovery options that apply generally across the majority of Conditions? If so, we can alleviate delegates from having to know the intimate details of a particular operation, which introduces too much coupling between high-level and low-level code. A more minor issue is that the current implementation is not very well written. :) I'll have to think about this more, to know how to better implement it. But I wanted to get the semantics of it out first, so that at least we can start discussing the conceptual aspects of the idea. T -- They say that "guns don't kill people, people kill people." Well I think the gun helps. If you just stood there and yelled BANG, I don't think you'd kill too many people. -- Eddie Izzard, Dressed to Kill
Feb 20 2012
prev sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Mon, Feb 20, 2012 at 06:37:07PM +0100, deadalnix wrote:
 Le 20/02/2012 06:57, H. S. Teoh a crit :

Alright. This thread has gone on for too long with lots of talk but
no down-to-earth, real code. So I decided to code up a quick-n-dirty
proof-of-concept implementation of a Lispian scheme of exception
handling. The source files are attached.


 I read your code with great interest ! I'm not sure about this. This
 is a lot of code to not achieve that much. OK, the stack hasn't been
 destroyed, but your delegate has its own scope and cannot do a lot.

The delegate has full access to the scope of main(), or wherever it was registered from, which the low-level error recovery code has no access to.
 Isn't a retry function provided by phobos and a transeient property as
 proposed by Andrei a better alternative ?

The problem is that by the time you regain control at the catch, the stack has already unwound to main(), so if you need to retry, you need to call openDataFile() all over again. In this case it's not too bad, because openDataFile() is very simple. But imagine if this openDataFile() was nested deeply inside a few layers of functions called from main(), then by the time you unwind the stack to main(), all the previous work done is lost, and you have to restart from the beginning. Also, how does the transient property help main() know that it should prompt the user for a different filename? What if a different exception was caught, like NetworkDriveUnreachable? It wouldn't make sense to prompt the user for another filename in this case. T -- Nearly all men can stand adversity, but if you want to test a man's character, give him power. -- Abraham Lincoln
Feb 20 2012
prev sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

On Sun, Feb 19, 2012 at 09:12:25PM -0600, Andrei Alexandrescu wrote:
 On 2/19/12 8:52 PM, H. S. Teoh wrote:

Correct, so that would be a recovery strategy at the operation level,
say at sendHttpRequest or something like that. There is not enough
information available to sendHttpRequest to know whether or not the
caller wants the request to be retried if it fails.

But if the higher-level code could indicate this by way of a recovery
policy delegate, then this retry can be done at the sendHttpRequest
level, instead of percolating up the call stack all the way to
submitHttpForm, which then has to reparse user data, convert into
JSON, say, and then retry the entire operation all over again.

I'm really liking the Lisp approach.

Now we're talking. Ideas. Outside the box.

Alright. This thread has gone on for too long with lots of talk but no down-to-earth, real code. So I decided to code up a quick-n-dirty proof-of-concept implementation of a Lispian scheme of exception handling. The source files are attached. This implementation is more to demonstrate the *semantics* of such a system, rather than a sample code of the real thing. Before anyone jumps on me for writing very bad code. ;-) In a real implementation, I probably wouldn't inherit Condition from Exception, for example. A real implementation would have language-level support for declaring recovery strategies within the function that handles them, rather than the kludge of declaring them separately and abusing the exception system to handle recovery actions. Also, registered handlers would need to be cleaned up upon scope exit, so that they don't cause crazy side-effects in unrelated code later on in the program. But anyway. The point of this code is to demonstrate: (1) How an exception generated in low-level code is recovered within the low-level code without unwinding the stack all the way back to the top-level caller; (2) How the top-level caller can provide a delegate that makes decisions that only the top-level code is qualified to make, yet does so in the context of low-level code so that recovery can proceed without unwinding the stack any more than necessary. I deliberately made the delegate prompt the user for a fixed filename, even allowing the option of aborting if the user wants to give up. This is to show how the system handles different recovery actions. This, of course, isn't the only way to do things; you can programmatically decide on a recovery strategy instead of prompting the user, for example. What do y'all think? T -- Nobody is perfect. I am Nobody. -- pepoluan, GKC forum
Feb 19 2012
prev sibling parent deadalnix <deadalnix gmail.com> writes:
Le 20/02/2012 21:12, Gerrit Wichert a crit :
 On 19.02.2012 01:40, H. S. Teoh wrote:
 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

Its even better, i want the exception message to be shown to the user in the current locales language. But for the logs i want it to be in english since i have to maintain systems in other countries we sold our software to. Its easy to connect to a remote system on the other side of the world, but its not so easy to read log files in a foreign language.

grep
Feb 20 2012
prev sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Sun, Feb 19, 2012 at 08:33:04PM -0600, Andrei Alexandrescu wrote:
 On 2/19/12 7:53 PM, H. S. Teoh wrote:
I stand by my objection that if something might succeed if it can be
retried, then it needs to be retried in the called function, not the
caller.

If read fails from a socket, it's of no use to try it again. One must close the socket, reconnect, and attempt the whole operation once again.

Correct, so that would be a recovery strategy at the operation level, say at sendHttpRequest or something like that. There is not enough information available to sendHttpRequest to know whether or not the caller wants the request to be retried if it fails. But if the higher-level code could indicate this by way of a recovery policy delegate, then this retry can be done at the sendHttpRequest level, instead of percolating up the call stack all the way to submitHttpForm, which then has to reparse user data, convert into JSON, say, and then retry the entire operation all over again. I'm really liking the Lisp approach. It nicely combines stack unwinding with in-context recovery by using a high-level delegate to make sound decisions based on factors outside the scope of the low-level function. You can unwind up to the level where a high-level delegate can step in and select a recovery strategy, and then continue on your way, rather than unwinding all the way up to the top and having to restart from square 1. The delegate that makes decisions doesn't have to be at the absolute top level either (that wouldn't make sense, since it would break encapsulation: top-level code needs to know about inner workings of low-level code so that it can decide how to recover from low-level operations). You can have a chain of delegates that make decisions at various levels, and the one nearest to where the exception is generated is invoked. It can then decide to defer to the next handler if it doesn't have enough information to proceed. T -- Дерево держится корнями, а человек - друзьями.
Feb 19 2012
parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Mon, Feb 20, 2012 at 09:45:50PM +0100, deadalnix wrote:
 I don't think the Condition should be an Exception. I think it
 should provide a method that throw.
 
 class Condition {
     void throw() pure {           // Or make it abstract
         throw new Exception();
     }
 }
 
 If no handler is found, this method is called. The handler can also call it.
 
 It is very important to separate the Condition and the Exception.
 Several Condition may use the same Exception.

Good point. Conflating the two will only lead to bad design and confusion later on. Best to keep them separate. So Conditions will be an alternative to Exceptions, and if they are not handled, then revert back to throwing an Exception like before.
 BTW, I don't think that something that can be implemented as lib
 should be added in the core language.

OK. Although if we implement it as a lib, then we'll need to "abuse" the exception throwing mechanism in order to handle error recovery strategies. For example: auto func() { try { retryOperation1: ... raise(new MyCondition); ... retryOperation2: ... raise(new MyOtherCondition); ... } catch(MyConditionRetry e) { goto retryOperation1; } catch(MyOtherConditionRetry e) { goto retryOperation2; } } Alternatively, we can use some mixins that generate goto's, maybe something like this: template BeginRetryBlock(...) {...} template EndRetryBlock(...) {...} template RecoveryBlock(...) {...} template RestartBlock(...) {...} auto func() { BeginRetryBlock!("retryblock1"); int some_parameter; if (!operation1()) raise(new MyCondition); RecoveryBlock!(MyCondition.Strategy1) { RestartBlock!("retryblock1"); } RecoveryBlock!(MyCondition.Strategy2) { fiddleWith(some_parameter); RestartBlock!("retryblock1"); } EndRetryBlock!(); return result; } The templates translate func() into something like this: // generated code auto func() { retryblock1: RecoveryStrategy __rs = void; int some_parameter; if (!operation1()) { __rs = __raise(new MyCondition); goto __recoveryBlock; } goto __success; __recoveryBlock: if (cast(MyCondition.Strategy1) __rs) { goto retryblock1; } if (cast(MyCondition.Strategy2) __rs) { fiddleWith(some_parameter); goto retryblock1; } // No matching strategy, give up throw __currentCondition.getException(); __success: return result; } This way, we don't need to use throw until we actually have to.
 Condition may be use using something like raise(new
 MyCondition(params)); with raise from std.condition .
 
 Alternatively, we could go metaprogramming for Condition, just like we
 did for Range.

I think metaprogramming is the way to go, otherwise this feature will require too much typing to achieve something simple, and people won't like to use it. T -- We are in class, we are supposed to be learning, we have a teacher... Is it too much that I expect him to teach me??? -- RL
Feb 20 2012
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
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
Feb 18 2012
prev sibling next sibling parent deadalnix <deadalnix gmail.com> writes:
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 !
Feb 18 2012
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/18/12 6:36 PM, H. S. Teoh wrote:
 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.

If an exception adds state and/or interface to the table, I agree that may justify its existence as a distinct type. Andrei
Feb 18 2012
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/18/12 8:45 PM, Jonathan M Davis wrote:
 On Saturday, February 18, 2012 20:28:32 Andrei Alexandrescu wrote:
 On 2/18/12 6:36 PM, H. S. Teoh wrote:
 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.

If an exception adds state and/or interface to the table, I agree that may justify its existence as a distinct type.

If all you're arguing is that something like StringException shouldn't exist because it doesn't add any additional member fields, then that's a much more reasonable thing to debate. I'm not quite sure I agree, but some of your responses seem to indicate that you don't want a rich exception hierarchy.

Ideally we'd have the right number of types - not more, not less. The main purpose of this thread is to figure how many exception types are "just right". There has been a tendency in Phobos to just add a module-specific exception to certain modules, without a good reason. I'm trying to figure out the reasons.
 In the case of getopt, at _least_ adding a GetOptException with a field for the
 failed flag would be very valuable, and having additional derived types which
 indicate _why_ it failed would also be valuable.

The additional state sounds fine, but I'm not so sure about adding one type per failure reason.
 And in some cases at least,
 that would lead to more member fields in the derived exceptions (like the value
 of the flag if it were given a bad value).

And aside from that? The universe of possible errors in getopt is finite, closed, and actually fairly small. Can't we do with fewer types?
 I would argue however that there _are_ times when having a derived exception
 which has no additional data beyond its type rather than simply Exception can
 be useful - _especially_ when it's at the base of a larger hierarchy. For
 instance, if we had an IOException, you could know that whatever operation you
 were trying to do went badly because of an IO problem. You would probably need
 to handle it as a subclass of IOException to get any particularly useful
 information on how to handle the problem, but just knowing that it was an I/O
 problem could be enough in some cases.

How about a system in which you can say whether an exception is I/O related, network related, recoverable or not, should be displayed to the user or not, etc. Such is difficult to represent with inheritance alone.
 I really think that whether adding an exception type which adds no additional
 member fields is a good idea should be evaluated on a case-by-case basis. We
 don't want to needlessly create a bunch of useless, uninformative exception
 types, but be we also want a rich enough exception hierarchy that it's
 possible to intelligently recover from exceptions.

I think right now we're erring a bit on the side of defining useless and uninformative exception types. As far as the originating module goes, it makes perfect sense to make that a field in the base class. Anyway, a simple action items right now is improve Exception's offering with things like e.g. custom formatting for i18n, more origin information, cloning, attributes and capabilities (transitory, user-visible etc). Andrei
Feb 18 2012
next sibling parent reply deadalnix <deadalnix gmail.com> writes:
Le 19/02/2012 08:05, Andrei Alexandrescu a écrit :
 How about a system in which you can say whether an exception is I/O
 related, network related, recoverable or not, should be displayed to the
 user or not, etc. Such is difficult to represent with inheritance alone.

That may sound great on the paper, but it isn't. The fact that an exception is recoverable or not depend often on your program and not on the cause of the exception. The piece of code throwing the exception have no clue if you can recover from that or not. Exception that cannot be recovered for sure already exists in D. They are called errors. Additionnaly, you'll find recoverable exception with different recovery point in the program.
Feb 19 2012
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/19/12 5:22 AM, deadalnix wrote:
 Le 19/02/2012 08:05, Andrei Alexandrescu a écrit :
 How about a system in which you can say whether an exception is I/O
 related, network related, recoverable or not, should be displayed to the
 user or not, etc. Such is difficult to represent with inheritance alone.

That may sound great on the paper, but it isn't. The fact that an exception is recoverable or not depend often on your program and not on the cause of the exception.

This is self-evident. Again, the meaning of "recoverable" is "operation may succeed if retried with the same input". It's a hint for the catch code. Of course the program is free to ignore that aspect, retry a number of times, log, display user feedback, and so on. But as far as definition goes the notion is cut and dried. Andrei
Feb 19 2012
parent reply "Nick Sabalausky" <a a.a> writes:
"Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message 
news:jhr0vq$24t0$1 digitalmars.com...
 On 2/19/12 5:22 AM, deadalnix wrote:
 Le 19/02/2012 08:05, Andrei Alexandrescu a crit :
 How about a system in which you can say whether an exception is I/O
 related, network related, recoverable or not, should be displayed to the
 user or not, etc. Such is difficult to represent with inheritance alone.

That may sound great on the paper, but it isn't. The fact that an exception is recoverable or not depend often on your program and not on the cause of the exception.

This is self-evident. Again, the meaning of "recoverable" is "operation may succeed if retried with the same input". It's a hint for the catch code. Of course the program is free to ignore that aspect, retry a number of times, log, display user feedback, and so on. But as far as definition goes the notion is cut and dried.

WTF? "Recoverable" means "can be recovered from". Period. The term doesn't have a damn thing to do with "how", even in the context of exceptions. It *never* has. If you meant it as "operation may succeed if retried with the same input", then fine, but don't pretend that *your* arbitrary definition is "cut and dried".
Feb 19 2012
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/19/12 9:56 AM, Nick Sabalausky wrote:
 "Andrei Alexandrescu"<SeeWebsiteForEmail erdani.org>  wrote in message
 news:jhr0vq$24t0$1 digitalmars.com...
 On 2/19/12 5:22 AM, deadalnix wrote:
 Le 19/02/2012 08:05, Andrei Alexandrescu a crit :
 How about a system in which you can say whether an exception is I/O
 related, network related, recoverable or not, should be displayed to the
 user or not, etc. Such is difficult to represent with inheritance alone.

That may sound great on the paper, but it isn't. The fact that an exception is recoverable or not depend often on your program and not on the cause of the exception.

This is self-evident. Again, the meaning of "recoverable" is "operation may succeed if retried with the same input". It's a hint for the catch code. Of course the program is free to ignore that aspect, retry a number of times, log, display user feedback, and so on. But as far as definition goes the notion is cut and dried.

WTF? "Recoverable" means "can be recovered from". Period. The term doesn't have a damn thing to do with "how", even in the context of exceptions. It *never* has. If you meant it as "operation may succeed if retried with the same input", then fine, but don't pretend that *your* arbitrary definition is "cut and dried".

I think it's a reasonable definition of "can be recovered from" in the context of exceptions. Andrei
Feb 19 2012
next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2012-02-19 17:29, Andrei Alexandrescu wrote:
 On 2/19/12 9:56 AM, Nick Sabalausky wrote:
 "Andrei Alexandrescu"<SeeWebsiteForEmail erdani.org> wrote in message
 news:jhr0vq$24t0$1 digitalmars.com...
 On 2/19/12 5:22 AM, deadalnix wrote:
 Le 19/02/2012 08:05, Andrei Alexandrescu a crit :
 How about a system in which you can say whether an exception is I/O
 related, network related, recoverable or not, should be displayed
 to the
 user or not, etc. Such is difficult to represent with inheritance
 alone.

That may sound great on the paper, but it isn't. The fact that an exception is recoverable or not depend often on your program and not on the cause of the exception.

This is self-evident. Again, the meaning of "recoverable" is "operation may succeed if retried with the same input". It's a hint for the catch code. Of course the program is free to ignore that aspect, retry a number of times, log, display user feedback, and so on. But as far as definition goes the notion is cut and dried.

WTF? "Recoverable" means "can be recovered from". Period. The term doesn't have a damn thing to do with "how", even in the context of exceptions. It *never* has. If you meant it as "operation may succeed if retried with the same input", then fine, but don't pretend that *your* arbitrary definition is "cut and dried".

I think it's a reasonable definition of "can be recovered from" in the context of exceptions.

No it's not. Say you want to save a file. You're saving it in "/" which you don't have permission to write to. The system throws an exception PermissionException, this is a recoverable exception since the application could prompt the user to save the file in a different location. -- /Jacob Carlborg
Feb 19 2012
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/19/12 10:43 AM, Jacob Carlborg wrote:
 No it's not. Say you want to save a file. You're saving it in "/" which
 you don't have permission to write to. The system throws an exception
 PermissionException, this is a recoverable exception since the
 application could prompt the user to save the file in a different location.

That would be a retry with different state. I agree you could define transiency/recoverability differently, but I think my definition is more useful. This is because it provides a notion of transiency that can be handled regardless of the actual problem. Andrei
Feb 19 2012
prev sibling parent reply "Nick Sabalausky" <a a.a> writes:
"Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message 
news:jhr81v$2i3r$3 digitalmars.com...
 On 2/19/12 9:56 AM, Nick Sabalausky wrote:
 "Andrei Alexandrescu"<SeeWebsiteForEmail erdani.org>  wrote in message
 news:jhr0vq$24t0$1 digitalmars.com...
 This is self-evident. Again, the meaning of "recoverable" is "operation
 may succeed if retried with the same input". It's a hint for the catch
 code. Of course the program is free to ignore that aspect, retry a 
 number
 of times, log, display user feedback, and so on. But as far as 
 definition
 goes the notion is cut and dried.

WTF? "Recoverable" means "can be recovered from". Period. The term doesn't have a damn thing to do with "how", even in the context of exceptions. It *never* has. If you meant it as "operation may succeed if retried with the same input", then fine, but don't pretend that *your* arbitrary definition is "cut and dried".

I think it's a reasonable definition of "can be recovered from" in the context of exceptions.

Reasonable maybe, but not obvious. That's all I'm trying to say.
Feb 19 2012
next sibling parent reply <address_is invalid.invalid> writes:
"Nick Sabalausky" <a a.a> wrote:
 "Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message 
 news:jhr81v$2i3r$3 digitalmars.com...
 On 2/19/12 9:56 AM, Nick Sabalausky wrote:
 "Andrei Alexandrescu"<SeeWebsiteForEmail erdani.org>  wrote in message
 news:jhr0vq$24t0$1 digitalmars.com...
 
 This is self-evident. Again, the meaning of "recoverable" is "operation
 may succeed if retried with the same input". It's a hint for the catch
 code. Of course the program is free to ignore that aspect, retry a 
 number
 of times, log, display user feedback, and so on. But as far as 
 definition
 goes the notion is cut and dried.
 

WTF? "Recoverable" means "can be recovered from". Period. The term doesn't have a damn thing to do with "how", even in the context of exceptions. It *never* has. If you meant it as "operation may succeed if retried with the same input", then fine, but don't pretend that *your* arbitrary definition is "cut and dried".

I think it's a reasonable definition of "can be recovered from" in the context of exceptions.

Reasonable maybe, but not obvious. That's all I'm trying to say.

I guess "transient" is more descriptive. Andrei
Feb 19 2012
next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/19/12 12:48 PM, address_is invalid.invalid wrote:
 I guess "transient" is more descriptive.

 Andrei

That was me indeed, just from my phone. Andrei
Feb 19 2012
prev sibling next sibling parent Jim Hewes <jimhewes gmail.com> writes:
On 2/19/2012 10:48 AM, address_is invalid.invalid wrote:

 I guess "transient" is more descriptive.

 Andrei

I suppose “transient” mingles with recoverability and may get confused with it. But the interrelated issue that comes to mind for me is whether a “failure” is a common and typical result of the particular operation or not. I'm thinking of the example of acquiring a mutex. Because the nature of a mutex is to prevent two parties from using a resource at the same time, getting a result of “mutex already in use” is a normal and expected result and probably should not throw an exception. It's transient in that you can try again and it might work. But this behavior is already built in because you can provide a timeout argument which means, “keep trying until this much time elapses, then give up”. Perhaps a network connection would be different because when you send a packet, you expect it to get where it's going. That would be normal program flow. Otherwise it's an error and an exception. Jim
Feb 19 2012
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/19/12 5:28 PM, Jonathan M Davis wrote:
 On Sunday, February 19, 2012 18:48:02 address_is invalid.invalid wrote:
 I guess "transient" is more descriptive.

Actually, thinking on it some more, I don't think that transient will work at all, and the reason is simple. _Which_ operation should you retry?

The application decides.
 You don't
 even necessarily know which function the exception came from out of the
 functions that you called within the try block - let alone which function
 actually threw the exception. Maybe it was thrown 3 functions deep from the
 function that you called, and while retrying that specific call 3 functions
 down might have made sense, retrying the function 3 functions up doesn't
 necessarily make sense at all.

 Whether or not you can retry or retrying makes any sense at all is _highly_
 dependent on who actually catches the exception. In many cases, it may be a
 function which could retry it, but in many it won't be, and so having the
 exception tell the caller that it could retry would just be misleading.

No dependence on context. The bit simply tells you "operation has failed, but due to a transitory matter". That is information local to the thrower. Andrei
Feb 19 2012
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/19/12 7:58 PM, H. S. Teoh wrote:
 On Sun, Feb 19, 2012 at 05:38:23PM -0600, Andrei Alexandrescu wrote:
 On 2/19/12 5:28 PM, Jonathan M Davis wrote:
 On Sunday, February 19, 2012 18:48:02 address_is invalid.invalid wrote:
 I guess "transient" is more descriptive.

Actually, thinking on it some more, I don't think that transient will work at all, and the reason is simple. _Which_ operation should you retry?

The application decides.
 You don't even necessarily know which function the exception came
from out of the functions that you called within the try block - let
 alone which function actually threw the exception. Maybe it was
 thrown 3 functions deep from the function that you called, and while
 retrying that specific call 3 functions down might have made sense,
 retrying the function 3 functions up doesn't necessarily make sense
 at all.

 Whether or not you can retry or retrying makes any sense at all is
 _highly_ dependent on who actually catches the exception. In many
 cases, it may be a function which could retry it, but in many it
 won't be, and so having the exception tell the caller that it could
 retry would just be misleading.

No dependence on context. The bit simply tells you "operation has failed, but due to a transitory matter". That is information local to the thrower.

But *which* transitory matter? A temporary outage on the network? A timeout due to excessive CPU load? A full disk (which is transitory because some other process might remove a large file in the interim)?

Doesn't matter.
 Without knowing the context, this information is of little use.

It is. User code may decide to retry the high-level operation (of which the low-level failure has no knowledge). Andrei
Feb 19 2012
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/19/12 9:06 PM, H. S. Teoh wrote:
 On Sun, Feb 19, 2012 at 08:34:30PM -0600, Andrei Alexandrescu wrote:
 On 2/19/12 7:58 PM, H. S. Teoh wrote:
 On Sun, Feb 19, 2012 at 05:38:23PM -0600, Andrei Alexandrescu wrote:


 No dependence on context. The bit simply tells you "operation has
 failed, but due to a transitory matter". That is information local
 to the thrower.

But *which* transitory matter? A temporary outage on the network? A timeout due to excessive CPU load? A full disk (which is transitory because some other process might remove a large file in the interim)?

Doesn't matter.
 Without knowing the context, this information is of little use.

It is. User code may decide to retry the high-level operation (of which the low-level failure has no knowledge).

But on what basis will it make this decision? All it knows is that something went wrong somewhere deep in the call stack, and it's presented with a binary choice: retry or abort.

No. The information is: "There's been failure, but due to a transitory cause." In a high-level transaction, it doesn't matter which particular step failed. If whatever failure was transitory, the transaction can be attempted again.
 It doesn't know what
 that problem was.

Doesn't have to.
 So how would it know if retrying would help? Saying
 that it "might" help doesn't seem useful to me.

Retrying helps because the error happened because of a temporary cause. That info is known at the raise place, and nicely passed up to the high-level command.
 It's only useful if you present this choice to the *user* along with the
 error message encapsulated in the exception, and let the user make the
 decision on the basis of the error message.

That's up to the application. A server application, for example, can't ask the user. We log and we have configurable number of retries.
 I think we're all agreed that parsing the error message is not a viable
 solution, so basically the catch block is presented with a blind binary
 choice of which it knows nothing about.  Such a choice is meaningless to
 a computer program.

 Do you have a concrete scenario in mind where such a decision would
 actually be useful? Otherwise we'll just end up with boilerplate code
 copy-n-pasted everywhere of the form:

 	auto retries = SomeArbitraryNumber;
 	do {
 		try {
 			...
 		} catch(Exception e) {
 			if (e.is_transient&&  retries-->  0)
 				continue;
 			throw e;
 		}
 	} while(false);

 But since this block is completely independent of what's inside the try
 block, why not just put it where the exception is generated in the first
 place?

I explained this. The raise locus does not have access to the high-level context. Andrei
Feb 19 2012
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday, February 19, 2012 18:48:02 address_is invalid.invalid wrote:
 I guess "transient" is more descriptive.

Actually, thinking on it some more, I don't think that transient will work at all, and the reason is simple. _Which_ operation should you retry? You don't even necessarily know which function the exception came from out of the functions that you called within the try block - let alone which function actually threw the exception. Maybe it was thrown 3 functions deep from the function that you called, and while retrying that specific call 3 functions down might have made sense, retrying the function 3 functions up doesn't necessarily make sense at all. Whether or not you can retry or retrying makes any sense at all is _highly_ dependent on who actually catches the exception. In many cases, it may be a function which could retry it, but in many it won't be, and so having the exception tell the caller that it could retry would just be misleading. - Jonathan M Davis
Feb 19 2012
prev sibling next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Sun, Feb 19, 2012 at 05:38:23PM -0600, Andrei Alexandrescu wrote:
 On 2/19/12 5:28 PM, Jonathan M Davis wrote:
On Sunday, February 19, 2012 18:48:02 address_is invalid.invalid wrote:
I guess "transient" is more descriptive.

Actually, thinking on it some more, I don't think that transient will work at all, and the reason is simple. _Which_ operation should you retry?

The application decides.
You don't even necessarily know which function the exception came
from out of the functions that you called within the try block - let
alone which function actually threw the exception. Maybe it was
thrown 3 functions deep from the function that you called, and while
retrying that specific call 3 functions down might have made sense,
retrying the function 3 functions up doesn't necessarily make sense
at all.

Whether or not you can retry or retrying makes any sense at all is
_highly_ dependent on who actually catches the exception. In many
cases, it may be a function which could retry it, but in many it
won't be, and so having the exception tell the caller that it could
retry would just be misleading.

No dependence on context. The bit simply tells you "operation has failed, but due to a transitory matter". That is information local to the thrower.

But *which* transitory matter? A temporary outage on the network? A timeout due to excessive CPU load? A full disk (which is transitory because some other process might remove a large file in the interim)? Without knowing the context, this information is of little use. I'm really starting to like the Lisp system more, the more I think about this. Let the low-level code provide a list of recovery strategies, and let the high-level code register recovery policies that select between these recovery strategies *in the context of the low-level code*. The runtime matches policy to strategy, and the stack is only unwound when no recovery is possible. T -- Frank disagreement binds closer than feigned agreement.
Feb 19 2012
prev sibling next sibling parent James Miller <james aatch.net> writes:
I agree that the "Lispian" model works well, though I had issues
trying to get my head around it when I encountered it.

I don't know how you'd make a simpler version for D (D lacking Lisps
ridiculous macros) but maybe something that essentially "returns" a
list of recovery codes (which would unfortunately have to be
documented) that can be called depending on the context of the error.

But error-handling is hard, programmers are naturally lazy, and
checking errors is not something exiting. Exceptions are always going
to be a source of contention amongst people. I know people (mostly C
programmers) that hate them, and other people swear by them. I agree
that incorrect parameters should not be Exceptions, and are
contract-level issues, check your parameters before passing them if
there are conditions on them!

its a difficult topic, hence the ridiculously long thread we have
going here. Error codes are not really the way to go, I prefer more of
an Objective-C/Smalltalk null-pattern style, where null can be a valid
argument almost anywhere the type system allows and code handles it
properly, normally by returning null.

Exceptions are a source of contention due to long-range dependencies,
but you can apply that to many things, and if you treat exceptions as
part of the API, then changing an exception can be considered an API
change and therefore something to be done with care.

James Miller
Feb 19 2012
prev sibling next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Sun, Feb 19, 2012 at 08:34:30PM -0600, Andrei Alexandrescu wrote:
 On 2/19/12 7:58 PM, H. S. Teoh wrote:
On Sun, Feb 19, 2012 at 05:38:23PM -0600, Andrei Alexandrescu wrote:


No dependence on context. The bit simply tells you "operation has
failed, but due to a transitory matter". That is information local
to the thrower.

But *which* transitory matter? A temporary outage on the network? A timeout due to excessive CPU load? A full disk (which is transitory because some other process might remove a large file in the interim)?

Doesn't matter.
Without knowing the context, this information is of little use.

It is. User code may decide to retry the high-level operation (of which the low-level failure has no knowledge).

But on what basis will it make this decision? All it knows is that something went wrong somewhere deep in the call stack, and it's presented with a binary choice: retry or abort. It doesn't know what that problem was. So how would it know if retrying would help? Saying that it "might" help doesn't seem useful to me. It's only useful if you present this choice to the *user* along with the error message encapsulated in the exception, and let the user make the decision on the basis of the error message. I think we're all agreed that parsing the error message is not a viable solution, so basically the catch block is presented with a blind binary choice of which it knows nothing about. Such a choice is meaningless to a computer program. Do you have a concrete scenario in mind where such a decision would actually be useful? Otherwise we'll just end up with boilerplate code copy-n-pasted everywhere of the form: auto retries = SomeArbitraryNumber; do { try { ... } catch(Exception e) { if (e.is_transient && retries-- > 0) continue; throw e; } } while(false); But since this block is completely independent of what's inside the try block, why not just put it where the exception is generated in the first place? T -- If the comments and the code disagree, it's likely that *both* are wrong. -- Christopher
Feb 19 2012
prev sibling next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Sun, Feb 19, 2012 at 09:17:23PM -0600, Andrei Alexandrescu wrote:
 On 2/19/12 9:06 PM, H. S. Teoh wrote:

Do you have a concrete scenario in mind where such a decision would
actually be useful? Otherwise we'll just end up with boilerplate code
copy-n-pasted everywhere of the form:

	auto retries = SomeArbitraryNumber;
	do {
		try {
			...
		} catch(Exception e) {
			if (e.is_transient&&  retries-->  0)
				continue;
			throw e;
		}
	} while(false);

But since this block is completely independent of what's inside the
try block, why not just put it where the exception is generated in
the first place?

I explained this. The raise locus does not have access to the high-level context.

This is why I'm liking the Lisp model more and more. The lower level code knows best what recovery strategies are available. But it can't make that decision at that level. So you need the higher level code to make this decision. But if you simply unwind the stack all the way back up to the higher level code, then you've lost the opportunity of reusing the execution context of the lower level code to perform the recovery. You have to start from scratch. You also have no other recovery strategies available, since the original execution context is gone. The only options left are retry or abort. By having the high-level code register a delegate which can make this sorts of decisions, when the low-level code *does* encounter a problem it will be able to perform the chosen recovery procedure immediately, rather than throwing away the entire execution context and perhaps having to reestablish all of it again later. This doesn't mean the low-level network socket that encounters the problem will be able to run the recovery right there; you do have to unwind the stack to some point where intelligent recovery is possible. But the point is that this is often lower down in the call stack than the code which is qualified to make decisions about which recovery strategy should be used. That's why you want to encapsulate the code that makes this decision in a delegate which is run at the lower-level code where intelligent recovery takes place. T -- Life is too short to run proprietary software. -- Bdale Garbee
Feb 19 2012
prev sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Mon, Feb 20, 2012 at 03:57:50PM +1300, James Miller wrote:
 I agree that the "Lispian" model works well, though I had issues
 trying to get my head around it when I encountered it.
 
 I don't know how you'd make a simpler version for D (D lacking Lisps
 ridiculous macros) but maybe something that essentially "returns" a
 list of recovery codes (which would unfortunately have to be
 documented) that can be called depending on the context of the error.

The concept doesn't require Lisp macros to work. D already has the necessary machinery to implement it. All that's needed is some syntactic sugar to make it very easy to use. Personally I'd like language-level support for it, but I suspect judicious use of templates may alleviate even that.
 But error-handling is hard, programmers are naturally lazy, and
 checking errors is not something exiting. Exceptions are always going
 to be a source of contention amongst people. I know people (mostly C
 programmers) that hate them, and other people swear by them. I agree
 that incorrect parameters should not be Exceptions, and are
 contract-level issues, check your parameters before passing them if
 there are conditions on them!

D already supports contracts. The current convention throws AssertError on contract failure, but you could just as easily terminate the program right there.
 its a difficult topic, hence the ridiculously long thread we have
 going here. Error codes are not really the way to go, I prefer more of
 an Objective-C/Smalltalk null-pattern style, where null can be a valid
 argument almost anywhere the type system allows and code handles it
 properly, normally by returning null.

Error codes are pass. The only reason we still have them is because of OS's inheriting the legacy APIs and conventions from 20 years ago. T -- I am Ohm of Borg. Resistance is voltage over current.
Feb 19 2012
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/19/12 2:21 AM, H. S. Teoh wrote:
 Getopt isn't really the best use case for elaborate exception
 hierarchies. Perhaps we should use file I/O or network/socket I/O
 instead.

This is exactly what we should avoid: regressing to well-trodden ground. We shouldn't discuss OOP only in terms of Animal and Mammal, and we shouldn't discuss exceptions only in terms of IOException and NetworkException. We should look at the "uncomfortable" cases. Andrei
Feb 19 2012
parent dennis luehring <dl.soluz gmx.net> writes:
Am 19.02.2012 17:27, schrieb Andrei Alexandrescu:
 On 2/19/12 2:21 AM, H. S. Teoh wrote:
  Getopt isn't really the best use case for elaborate exception
  hierarchies. Perhaps we should use file I/O or network/socket I/O
  instead.

This is exactly what we should avoid: regressing to well-trodden ground. We shouldn't discuss OOP only in terms of Animal and Mammal, and we shouldn't discuss exceptions only in terms of IOException and NetworkException. We should look at the "uncomfortable" cases. Andrei

so there is a need to start with collecting the uncomfortable cases - that would ease the complete discussion a lot
Feb 19 2012
prev sibling next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 2/18/2012 3:13 PM, 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.

I think typed exceptions are a good idea, but something looks wrong with these. (Also, having a large number of exception types is going to produce a lot of program size bloat. Remember, for EVERY class type, you've got the vtbl[], the .init data, and the TypeInfo. Going to town on exception types can really add this up.)
Feb 18 2012
next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Walter:

 (Also, having a large number of exception types is going to produce a lot of 
 program size bloat. Remember, for EVERY class type, you've got the vtbl[], the 
 .init data, and the TypeInfo. Going to town on exception types can really add 
 this up.)

To improve this discussion a small benchmark is useful to see how much bloat this actually causes. Bye, bearophile
Feb 18 2012
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 2/18/2012 8:08 PM, bearophile wrote:
 To improve this discussion a small benchmark is useful to see how much bloat
this actually causes.

It'll increase with reflection and perfect garbage collection.
Feb 18 2012
parent Sean Cavanaugh <WorksOnMyMachine gmail.com> writes:
On 2/18/2012 11:07 PM, Walter Bright wrote:
 On 2/18/2012 8:08 PM, bearophile wrote:
 To improve this discussion a small benchmark is useful to see how much
 bloat this actually causes.

It'll increase with reflection and perfect garbage collection.

Are these coming? :)
Feb 18 2012
prev sibling next sibling parent "Daniel Murphy" <yebblies nospamgmail.com> writes:
"Walter Bright" <newshound2 digitalmars.com> wrote in message 
news:jhpqdj$30t6$1 digitalmars.com...
 (Also, having a large number of exception types is going to produce a lot 
 of program size bloat. Remember, for EVERY class type, you've got the 
 vtbl[], the .init data, and the TypeInfo. Going to town on exception types 
 can really add this up.)

As a counterpoint: With a standard exception heirachy in some cases executable size will be decreased, because it will reduce the number of libraries inventing their own similar heirachy.
Feb 18 2012
prev sibling next sibling parent reply deadalnix <deadalnix gmail.com> writes:
Le 19/02/2012 04:31, Walter Bright a écrit :
 On 2/18/2012 3:13 PM, 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.

I think typed exceptions are a good idea, but something looks wrong with these. (Also, having a large number of exception types is going to produce a lot of program size bloat. Remember, for EVERY class type, you've got the vtbl[], the .init data, and the TypeInfo. Going to town on exception types can really add this up.)

Why is the executable size such a big issue ? Additionnaly, nto providing a convenient Exception hierarchy is going inveitably to lead lib devs to create theire own. And the bloat will be even bigger (due to redundancy) and the result less convenient because the result will be inconsistent for the user of theses libs.
Feb 19 2012
parent deadalnix <deadalnix gmail.com> writes:
Le 19/02/2012 13:32, Jonathan M Davis a écrit :
 On Sunday, February 19, 2012 13:20:19 deadalnix wrote:
 Why is the executable size such a big issue ?

Some people complain bitterly about any additional size to the executable at all, whether they use all of the symbols in it or not, and if they don't use all of the symbols, then they're that much more annoyed by the additional size. It crops of semi-frequently around here that someone complains about how big hello world is. Most of the problem goes away if phobos because a shared library like it's going to eventually, but someone is always going to be complaining about binary size - especially with a systems language. - Jonathan M Davis

I know that people complain. But most of them are unable to explain why it is a problem. Plus, hello world size is a very stupid exemple accroding to me. It is just like comparaing sorting algorithms with an array of 5 elements. The code bloat that Exception would add is in O(1). So it will get irrevealant in every non trivial program toy like hello world. Note that Hello World in Java is about 50Mb. It is because the stdlib is big. And it has much more played in favor of java than against. Plus, we consider adding perfect GC (or at least on heap), which would be O(n) on the code bloat. This may be not noticeable on an hello world program, but this will sure generate more code bloat on any real application.
Feb 19 2012
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday, February 19, 2012 13:20:19 deadalnix wrote:
 Why is the executable size such a big issue ?

Some people complain bitterly about any additional size to the executable at all, whether they use all of the symbols in it or not, and if they don't use all of the symbols, then they're that much more annoyed by the additional size. It crops of semi-frequently around here that someone complains about how big hello world is. Most of the problem goes away if phobos because a shared library like it's going to eventually, but someone is always going to be complaining about binary size - especially with a systems language. - Jonathan M Davis
Feb 19 2012
prev sibling parent Piotr Szturmaj <bncrbme jadamspam.pl> writes:
Walter Bright wrote:
 On 2/18/2012 3:13 PM, 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.

I think typed exceptions are a good idea, but something looks wrong with these. (Also, having a large number of exception types is going to produce a lot of program size bloat. Remember, for EVERY class type, you've got the vtbl[], the .init data, and the TypeInfo. Going to town on exception types can really add this up.)

I feel that druntime might be optimized. 1) I think most of the bloat comes from .init data. For example this program: class A { ubyte[1024 * 1024 * 10] tenMegabytes; } class B : A {} class C : B {} class D : C {} void main() { } compiles to 40 MB exe under windows. Every class has copy of the same 10 MB .init data. I wonder why init is byte[] array. IMHO the better thing would be making it byte[][], i.e. array of byte arrays. Then some derived parts may be shared as byte array slices. This maybe little slower because it adds another level of indirection. By the way, current byte[] array solution doesn't speed up void initializers, because it always overwrites void fields. This simple program: import std.stdio; class A { ubyte[8] test = void; } void main() { writeln((new A()).test); } always prints [0, 0, 0, 0, 0, 0, 0, 0]. With byte[][] array, some slices may have null ptrs so they may be initialized using memset() or not initialized at all (when using void initializer). 2) vtbl[] might also be shared for derivation chains that don't override any functions. But that may slow down program start, because vtables must be constructed at runtime. Please correct me if I'm wrong :) I think that the use of classes should not be limited because of implementation issues. For me it's purely implementation issue, and I suppose it may be addressed with more optimized ABI or druntime.
Feb 21 2012
prev sibling next sibling parent reply Robert Clipsham <robert octarineparrot.com> writes:
On 18/02/2012 23:13, 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.

 Andrei

Perhaps something like: class PhobosException : Exception { this(string _module, uint flags) { ... } } try { doSomething(); } catch(PhobosException e) { // An exception was thrown by phobos if (e.moduleOf == "file") { // An exception was thrown by std.file if (e.flags & FileException.NotFound) { // File wasn't found } } else if (e.moduleOf == "stdio") { // An exception was thrown by std.stdio } // We only want to handle file and stdio exceptions, rethrow throw e; } Some pros/cons: * Catch-all for all phobos exceptions * No binary bloat from having lots of exception classes * Can still handle specific exceptions * e.flags isn't type-safe * It's not particularly pretty * Can only have up to 32 different "exceptions" in a module (64 if you make it a ulong) * It looks stupid if you come from an OOP language like java and you're used to having 5000 catch blocks * No need to decide on any exception hierarchy, just throw a PhobosException -- Robert http://octarineparrot.com/
Feb 19 2012
parent reply "Nick Sabalausky" <a a.a> writes:
"Robert Clipsham" <robert octarineparrot.com> wrote in message 
news:jhref7$bo$1 digitalmars.com...
 On 18/02/2012 23:13, 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.

 Andrei

Perhaps something like: class PhobosException : Exception { this(string _module, uint flags) { ... } } try { doSomething(); } catch(PhobosException e) { // An exception was thrown by phobos if (e.moduleOf == "file") { // An exception was thrown by std.file if (e.flags & FileException.NotFound) { // File wasn't found } } else if (e.moduleOf == "stdio") { // An exception was thrown by std.stdio } // We only want to handle file and stdio exceptions, rethrow throw e; } Some pros/cons: * Catch-all for all phobos exceptions * No binary bloat from having lots of exception classes * Can still handle specific exceptions * e.flags isn't type-safe * It's not particularly pretty * Can only have up to 32 different "exceptions" in a module (64 if you make it a ulong) * It looks stupid if you come from an OOP language like java and you're used to having 5000 catch blocks * No need to decide on any exception hierarchy, just throw a PhobosException

No, exceptions need to be based around semantics, not modules. The former is reusable and is well-proven to be very useful. The latter isn't particularly useful in general. And if anyone does have use for it, we could just add "moduleOf" to Exception, no need for it to be phobos-specific.
Feb 19 2012
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/19/12 12:49 PM, Nick Sabalausky wrote:
 No, exceptions need to be based around semantics, not modules.

Packages and modules are also organized around specific areas. Andrei
Feb 19 2012
next sibling parent reply "Nick Sabalausky" <a a.a> writes:
"Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message 
news:jhrn3r$fie$3 digitalmars.com...
 On 2/19/12 12:49 PM, Nick Sabalausky wrote:
 No, exceptions need to be based around semantics, not modules.

Packages and modules are also organized around specific areas.

And those specific areas do NOT necessarily correspond 1-to-1 to error conditions. 1. A module can still reasonably generate more than one conceptual type of exception. Ex: std.file can be expected to generate both "file not found" and "disk full" conditions (those may both be IO exceptions, but they're still separate issues that are *not* always to be dealt with the same way). You can argue till your keyboard wears out that you don't see the usefulness, but the fact is, many, many, many people DO find it useful. 2. As I *already* explained in further detail, it's perfectly reasonable to expect one specific area to be convered by more than module. Therefore, "Modules" to "Conceptual types of exceptions" are not 1-to-1. They're not even 1-to-many or many-to-1. They're many-to-many. Therefore, module is the wrong basis for an exception. Seriously, how is this not *already* crystal-clear? I feel as if every few weeks you're just coming up with deliberately random shit to argue so the rest of us have to waste our time spelling out the obvious in insanely pedantic detail.
Feb 19 2012
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/19/12 4:00 PM, Nick Sabalausky wrote:
 "Andrei Alexandrescu"<SeeWebsiteForEmail erdani.org>  wrote in message
 news:jhrn3r$fie$3 digitalmars.com...
 On 2/19/12 12:49 PM, Nick Sabalausky wrote:
 No, exceptions need to be based around semantics, not modules.

Packages and modules are also organized around specific areas.

And those specific areas do NOT necessarily correspond 1-to-1 to error conditions.

They don't; the intent here is, again, to cater to the common case.
 1. A module can still reasonably generate more than one conceptual type of
 exception. Ex: std.file can be expected to generate both "file not found"
 and "disk full" conditions (those may both be IO exceptions, but they're
 still separate issues that are *not* always to be dealt with the same way).

Sure. That would mean std.file could define // inside std.file class FileNotFound : public ModuleException!.stringof { ... } class DiskFull : public ModuleException!.stringof { ... } Or could define one: // inside std.file class FileException : public ModuleException!.stringof { Code code; ... }
 2. As I *already* explained in further detail, it's perfectly reasonable to
 expect one specific area to be convered by more than module.

 Therefore, "Modules" to "Conceptual types of exceptions" are not 1-to-1.
 They're not even 1-to-many or many-to-1. They're many-to-many. Therefore,
 module is the wrong basis for an exception.

I think that direction is worth exploring.
 Seriously, how is this not *already* crystal-clear? I feel as if every few
 weeks you're just coming up with deliberately random shit to argue so the
 rest of us have to waste our time spelling out the obvious in insanely
 pedantic detail.

It sometimes happened to me to be reach the hypothesis that my interlocutor must be some idiot. Most often I was missing something. Andrei
Feb 19 2012
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/21/12 5:55 AM, Regan Heath wrote:
 On Sun, 19 Feb 2012 23:04:59 -0000, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:

 On 2/19/12 4:00 PM, Nick Sabalausky wrote:

 Seriously, how is this not *already* crystal-clear? I feel as if
 every few
 weeks you're just coming up with deliberately random shit to argue so
 the
 rest of us have to waste our time spelling out the obvious in insanely
 pedantic detail.

It sometimes happened to me to be reach the hypothesis that my interlocutor must be some idiot. Most often I was missing something.

I get the impression that you find "Devil's advocate" a useful tool for generating debate and out of the box thinking.. there is something to be said for that, but it's probably less annoying to some if you're clear about that from the beginning. :p

Where did it seem I was playing devil's advocate? Thanks. Andrei
Feb 21 2012
parent Don Clugston <dac nospam.com> writes:
On 24/02/12 13:47, Regan Heath wrote:
 On Thu, 23 Feb 2012 15:13:17 -0000, James Miller <james aatch.net> wrote:
 On 23 February 2012 05:09, Regan Heath <regan netmail.co.nz> wrote:
 On Tue, 21 Feb 2012 14:19:17 -0000, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:

 On 2/21/12 5:55 AM, Regan Heath wrote:
 On Sun, 19 Feb 2012 23:04:59 -0000, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:

 On 2/19/12 4:00 PM, Nick Sabalausky wrote:

 Seriously, how is this not *already* crystal-clear? I feel as if
 every few
 weeks you're just coming up with deliberately random shit to
 argue so
 the
 rest of us have to waste our time spelling out the obvious in
 insanely
 pedantic detail.

It sometimes happened to me to be reach the hypothesis that my interlocutor must be some idiot. Most often I was missing something.

I get the impression that you find "Devil's advocate" a useful tool for generating debate and out of the box thinking.. there is something to be said for that, but it's probably less annoying to some if you're clear about that from the beginning. :p

Where did it seem I was playing devil's advocate? Thanks.

"Devil's Advocate" is perhaps not the right term, as you don't seem to ever argue the opposite to what you believe. But, it occasionally seems to me that you imply ignorance on your part, in order to draw more information from other posters on exactly what they think or are proposing. So, some get frustrated as they feel they have to explain "everything" to you (and not just you, there have been times where - for whatever reason - it seems that anything less than a description of every single minute detail results in a miss understanding - no doubt partly due to the medium in which we are communicating). Regan -- Using Opera's revolutionary email client: http://www.opera.com/mail/

I think that is technically called being facetious.

Doesn't seem quite right to me: http://dictionary.reference.com/browse/facetious R

Socratic irony?
Mar 05 2012
prev sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday, February 19, 2012 17:00:24 Nick Sabalausky wrote:
 "Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message
 news:jhrn3r$fie$3 digitalmars.com...
 
 On 2/19/12 12:49 PM, Nick Sabalausky wrote:
 No, exceptions need to be based around semantics, not modules.

Packages and modules are also organized around specific areas.

And those specific areas do NOT necessarily correspond 1-to-1 to error conditions. 1. A module can still reasonably generate more than one conceptual type of exception. Ex: std.file can be expected to generate both "file not found" and "disk full" conditions (those may both be IO exceptions, but they're still separate issues that are *not* always to be dealt with the same way). You can argue till your keyboard wears out that you don't see the usefulness, but the fact is, many, many, many people DO find it useful. 2. As I *already* explained in further detail, it's perfectly reasonable to expect one specific area to be convered by more than module. Therefore, "Modules" to "Conceptual types of exceptions" are not 1-to-1. They're not even 1-to-many or many-to-1. They're many-to-many. Therefore, module is the wrong basis for an exception. Seriously, how is this not *already* crystal-clear? I feel as if every few weeks you're just coming up with deliberately random shit to argue so the rest of us have to waste our time spelling out the obvious in insanely pedantic detail.

While I think that Nick is getting a bit incensed and even a bit rude in some of his posts in this thread, I completely agree with his basic point here. In a well-designed exception hierarchy, the exception types are based on the types of things that went wrong. That way, you can catch them based on what went wrong, and they can provide information specific to that type of problem. As such, there is not necessarily any connection (let alone 1:1) betwen exception types and modules. You could have multiple exception types per module or have several exceptions sharing a module. And if the exception hierarchy is done particularly well, then code which uses Phobos will also throw those exceptions when appropriate. For instance, in other languages, InvalidArgumentException is quite commonly used to indicate that a function was given bad arguments when an exception is thrown for that sort of thing - and code outside of the standard libraries uses it for that purpose all the time. We have as much of a connection between modules and exceptions as we do, because Phobos is generally well-organized according to functionality, and different functionality tends to mean different exception types. But we've also taken it too far in some cases, creating an exception type in a module simply because most Phobos modules have their own exceptions. Ultimately, it's what went wrong that matters, _not_ in which module it happened in. And if anything, we need to redesign what we have so that it better follows that, getting rid of some exceptions, merging others, and in some cases creating new ones. In many cases, you'll still have an exception per module, but in some cases you'll end up with multiple, and in others, you'll end up with none, because those modules are using exceptions from other modules. - Jonathan M Davis
Feb 19 2012
prev sibling next sibling parent Patrick Down <patrick.down using.that.google.mail.thing.com> writes:
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.
 
 Andrei
 

As a lurker watching this debate with interest I just had an idea out of the blue idea that may or may not have value but might reduce the desire to create a new exception type Exception type for every error. What if the catch syntax was extended to take both a type and a condition. try { write(filename, ...); } catch(FileException e; e.errno == EEXIST) { } catch(FileException e; e.errno == EROFS) { } I realize that this is just syntactic sugar for catch(...) { if(...) {}} feel free to shoot it down.
Feb 19 2012
prev sibling parent Jim Hewes <jimhewes gmail.com> writes:
On 2/18/2012 3:13 PM, 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.

 Andrei

IMHO, I would not have made a GetOptException at all. It is too specific to the function. Since this is a type of parse error, I would prefer a ParseException base class. I think has a couple of advantages. One, the same exception can be used not only by the library itself but also by other users. Parsing is not an uncommon activity. Two, the exception hierarchy is orthogonal to the library. So changing the library functions---adding or removing them--- doesn't require changes in the exception hierarchy. Perhaps ParseException could then have a field that highlights the text that could not be parsed. This could be generally for all parsing type application. In separating what should be a distinct type from what should be an attribute, I'd think you can use as a guideline whether the errors are likely to be handled in one place. I would guess that if you're going to try to analyze a parse error you would handle the possibilities of FlagArgumentMissingException, InvalidFlagArgumentException, and UnknownFlagException all in one place. You're not likely to catch one and let the others propagate upward. So those could be attribute you handle with a switch rather than separate types. Jim
Feb 19 2012
prev sibling parent Jacob Carlborg <doob me.com> writes:
On 2012-02-18 23:26, Jonathan M Davis wrote:
 On Saturday, February 18, 2012 20:20:23 deadalnix wrote:
 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.

In general, I agree with all of this. I very much think that having typed exceptions makes a lot of sense. In general, I think that Java got exceptions right (ignoring the issue with checked exceptions). Having typed exceptions works really well. In the case of getopt, what we want is a GetOptException which is for anything which goes wrong with getopt so that someone can catch that exception type if they want to just handle the case wheree getopt throws but don't want to swallow other exception types by calling exception and pretty much just want to print an error and exit the program or continue without any comand-line arguments if that's what they prefer to do.

Isn't that what CommandLineException would be? Or would "getopt" throw other exceptions that would not derive from CommandLineException? So we end up with this: GetOptException / \ / \ / \ CommandLineException SomeKindOfOtherException ?
 Then you have other exception types derived from GetOptException which deal
 with specific types of issues - such as FlagArgumentMissingException,
 InvalidFlagArgumentException, UnknownFlagException. Each of those exception
 types could then give information which specifically pertains to those errors -
 such as the flag that had a problem, the type that the flag is supposed to
 receive, the type that the flag actually received, the invalid value that the
 flag was given, etc. Such exceptions can then allow you to properly handle and
 report problems with command-line arguments. Right now, all you know is that
 something went wrong, and pretty much the best that you can do is print out
 that something went wrong. You can do any decent error handling at all. You
 need specific exception types which give you the appropriate information in
 order to do that.

 Another example is FileException. It would be benificial to have exceptions
 like FileNotFoundException, NotFileException, NotDirException,
 AccessDeniedException, etc which are derived from FileException. Then programs
 could handle the specific instance of what went wrong nice and cleanly rather
 than having to look at error codes. At least FileException provides an error
 code, but that makes for much uglier handling code assuming that you even have
 any clue what the error codes mean. And since the error codes can vary from OS
 to OS, you end up with system-specific error handling code if you try and use
 errno. Whereas if std.file just translated the error code to the correct
 exception type, you're then very cleanly informed as to what the problem was
 and can catch the exception based on which situtations you can recover from
 and which you can't as well as having different catch blocks to handle
different
 problems differently.

 Simply having an exception type per module is somewhat better than just having
 Exception, because it gives you a better idea of what went wrong (e.g. you got
 a UTFException rather than a FileException), but it's still way too general in
 a lot of cases, and I can see why some would think that it creates needless
 boilerplate code. Also, in some cases, having exception types derive from more
 general exceptions than what the module focuses on can be useful. For
 instance, Java has IOException as the base for all IOExceptions. FileException
 could be derived from that, and then std.stream and std.stdio could could have
 their exception types derive from that as well. Then you could specifically
 handle all exceptions related to I/O together.

 I'm completely sold on typed exceptions, but I think that we could do better
 with them than we're currently doing.

 - Jonathan M Davis

Otherwise I completely agree. -- /Jacob Carlborg
Feb 19 2012
prev sibling next sibling parent "Jakob Ovrum" <jakobovrum gmail.com> writes:
On Saturday, 18 February 2012 at 23:26:14 UTC, Andrei 
Alexandrescu wrote:
 (b) all real code does one thing.

 Andrei

No. It's conceivable that *most* cases simply log the exception (e.g. by printing it) then either move back or move on (!). But it's most definitely not *all* and it's when you have a meaningful fall-back or recovery mechanism that exceptions shine and it's what they're designed for (because you are basically arguing against exceptions here, not their proper use, which should be obvious from how they function). The more complex your program becomes, the farther away from uncontrollable factors like user input and otherwise unexpected environments you get, and this is when interesting catch blocks are born. Even so, exceptions are still useful in shallow parts of your program too - you could catch a GetOptException (more specific exception types than this with attached data would be even better, as noted by everyone before me) and re-query the user for new input (not very Unix-y, but that's irrelevant). You can't do that with just an Exception - what if the exception came from one of the callbacks you passed to getopt, or from a bug in std.getopt? All that said, I don't think std.getopt is a good example because it's comparatively trivial code. std.file would be a much better area to examine for examples of interesting catch blocks.
Feb 18 2012
prev sibling next sibling parent Jose Armando Garcia <jsancio gmail.com> writes:
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. 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.? I'll be the first to suggestion something. One possible solution is to have one exception and inside have the concept of a namespace and a error code. Assuming that D had pattern matching you could do: try { errorProneCode(); } catch (Exception(IONamespace, errorCode)) { // errorCode is a variable with the error and the exception must match the value in IONamespace } catch (Exception(DBNamespace, AccessDenied) { // matches the db namespace and the access denied error code } Destroy. Thanks, -Jose
 Andrei

Feb 18 2012
prev sibling next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
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
Feb 18 2012
parent bearophile <bearophileHUGS lycos.com> writes:
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
Feb 18 2012
prev sibling next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
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
Feb 18 2012
parent reply "Nick Sabalausky" <a a.a> writes:
"H. S. Teoh" <hsteoh quickfur.ath.cx> wrote in message 
news:mailman.556.1329612309.20196.digitalmars-d puremagic.com...
 I can't believe something this simple has to be explained so
 elaborately. I thought all of us here knew how to use OO??

My thoughts exactly. Andrei is a very intelligent, very capable programmer, and I do have a lot of respect for him, but it seems that every month or so, we have to collectively teach him something else about basic programming to ensure D doesn't veer off the rails into crazyville.
Feb 18 2012
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/18/12 10:14 PM, Nick Sabalausky wrote:
 "H. S. Teoh"<hsteoh quickfur.ath.cx>  wrote in message
 news:mailman.556.1329612309.20196.digitalmars-d puremagic.com...
 I can't believe something this simple has to be explained so
 elaborately. I thought all of us here knew how to use OO??

My thoughts exactly. Andrei is a very intelligent, very capable programmer, and I do have a lot of respect for him, but it seems that every month or so, we have to collectively teach him something else about basic programming to ensure D doesn't veer off the rails into crazyville.

I knew the day of reckoning would come :o). Andrei
Feb 18 2012
prev sibling next sibling parent Jose Armando Garcia <jsancio gmail.com> writes:
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: =A0 =A0 =A0 =A0class OracleException : SQLException { =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0OracleErrorCode_t code; =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0this(...) {...} =A0 =A0 =A0 =A0}

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

Feb 18 2012
prev sibling next sibling parent "Robert Jacques" <sandford jhu.edu> writes:
On Sat, 18 Feb 2012 23:08:21 -0600, Sean Cavanaugh <WorksOnMyMachine gmail.com>
wrote:
 On 2/18/2012 11:07 PM, Walter Bright wrote:
 On 2/18/2012 8:08 PM, bearophile wrote:
 To improve this discussion a small benchmark is useful to see how much
 bloat this actually causes.

It'll increase with reflection and perfect garbage collection.

Are these coming? :)

Yes. Heap-perfect GC has had patches in Bugzilla, though they've suffered bit-rot. I'd really like a fully precise collector, but that is more work for Walter. Reflection is part of the variant refactoring I've been working on. I've kept the design very limited so far, but it does result in a lot of codegen and slow compile times in certain situations (mostly due to type tuple bugs and Algebraic).
Feb 18 2012
prev sibling next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Sun, Feb 19, 2012 at 04:36:29PM -0500, Patrick Down wrote:
[...]
 As a lurker watching this debate with interest I just had an idea out
 of the blue idea that may or may not have value but might reduce the
 desire to create a new exception type Exception type for every error. 
 
 What if the catch syntax was extended to take both a type and a
 condition.  
 
 try {
   write(filename, ...);
 }
 catch(FileException e; e.errno == EEXIST) {
 }
 catch(FileException e; e.errno == EROFS) {
 }
 
 I realize that this is just syntactic sugar for catch(...) { if(...)
 {}} feel free to shoot it down.

It's not. This avoids the evil side-effect of resetting the stack trace when you rethrow an exception you didn't mean to catch inside the catch block. This is in fact just an alternative syntax for my "catch signature constraint" idea. Another side issue, though: IMHO putting errno inside an exception is not a good thing. It introduces a strong coupling between the caller of write() and the low-level implementation of write(). This is bad because should we decide to change the underlying implementation of write(), it will cause a lot of breakage in all user code that catches errno-specific exceptions. This breaks encapsulation in an ugly way. Instead, what we *should* do is to have Phobos define its own *logical* exception types, rather than implementation-dependent exception types. For example, conceptually speaking a write operation may fail because of the target device is full, or the target file already exists. The users of Phobos *shouldn't* care whether it's errno==EEXIST or it's a Windows-specific error number that describes this condition. Phobos needs to encapsulate this in a platform-independent way. Of course, the implementation-specific info can still be there for system-dependent code that *wants* to check for system-specific statuses, for example, to check for a UNIX-specific error that doesn't exist on Windows, say. But this type of system-specific checking shouldn't be what Phobos users *generally* depend upon. T -- He who sacrifices functionality for ease of use, loses both and deserves neither. -- Slashdotter
Feb 19 2012
prev sibling next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Sun, Feb 19, 2012 at 02:53:04PM -0800, Jim Hewes wrote:
[...]
 Two, the exception hierarchy is orthogonal to the library.

Yes, yes, and yes! You have identified one of the causes of the current problems with the exception hierarchy. [...]
 So changing the library functions---adding or removing them--- doesn't
 require changes in the exception hierarchy. Perhaps ParseException
 could then have a field that highlights the text that could not be
 parsed. This could be generally for all parsing type application.

For a well-defined library like Phobos, we really shouldn't be defining new exceptions in each module. Rather, (almost) all exceptions should be collected in a common place, organized as a clean hierarchy without being unnecessarily bound to any particular module. Modifications to the exception hierarchy need to be properly evaluated before being committed to the codebase. Of course, there are some exceptions that are specific to a module; those obviously belong in their respective module. But generally speaking, exceptions should fit into a clean classification hierarchy independently of which module first introduced them. Just because exception X was first introduced by module M doesn't mean that it's specific to module M; it may encapsulate a larger class of problems that M just happens to touch. By separating the exception hierarchy from the module hierarchy, we can clean up a lot of the mess that resulted from conflating the two. T -- Chance favours the prepared mind. -- Louis Pasteur
Feb 19 2012
prev sibling next sibling parent "Regan Heath" <regan netmail.co.nz> writes:
On Sun, 19 Feb 2012 23:04:59 -0000, Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org> wrote:

 On 2/19/12 4:00 PM, Nick Sabalausky wrote:

 Seriously, how is this not *already* crystal-clear? I feel as if every  
 few
 weeks you're just coming up with deliberately random shit to argue so  
 the
 rest of us have to waste our time spelling out the obvious in insanely
 pedantic detail.

It sometimes happened to me to be reach the hypothesis that my interlocutor must be some idiot. Most often I was missing something.

I get the impression that you find "Devil's advocate" a useful tool for generating debate and out of the box thinking.. there is something to be said for that, but it's probably less annoying to some if you're clear about that from the beginning. :p Regan -- Using Opera's revolutionary email client: http://www.opera.com/mail/
Feb 21 2012
prev sibling next sibling parent "Regan Heath" <regan netmail.co.nz> writes:
On Tue, 21 Feb 2012 14:19:17 -0000, Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org> wrote:

 On 2/21/12 5:55 AM, Regan Heath wrote:
 On Sun, 19 Feb 2012 23:04:59 -0000, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:

 On 2/19/12 4:00 PM, Nick Sabalausky wrote:

 Seriously, how is this not *already* crystal-clear? I feel as if
 every few
 weeks you're just coming up with deliberately random shit to argue so
 the
 rest of us have to waste our time spelling out the obvious in insanely
 pedantic detail.

It sometimes happened to me to be reach the hypothesis that my interlocutor must be some idiot. Most often I was missing something.

I get the impression that you find "Devil's advocate" a useful tool for generating debate and out of the box thinking.. there is something to be said for that, but it's probably less annoying to some if you're clear about that from the beginning. :p

Where did it seem I was playing devil's advocate? Thanks.

"Devil's Advocate" is perhaps not the right term, as you don't seem to ever argue the opposite to what you believe. But, it occasionally seems to me that you imply ignorance on your part, in order to draw more information from other posters on exactly what they think or are proposing. So, some get frustrated as they feel they have to explain "everything" to you (and not just you, there have been times where - for whatever reason - it seems that anything less than a description of every single minute detail results in a miss understanding - no doubt partly due to the medium in which we are communicating). Regan -- Using Opera's revolutionary email client: http://www.opera.com/mail/
Feb 22 2012
prev sibling next sibling parent James Miller <james aatch.net> writes:
On 23 February 2012 05:09, Regan Heath <regan netmail.co.nz> wrote:
 On Tue, 21 Feb 2012 14:19:17 -0000, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:

 On 2/21/12 5:55 AM, Regan Heath wrote:
 On Sun, 19 Feb 2012 23:04:59 -0000, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:

 On 2/19/12 4:00 PM, Nick Sabalausky wrote:

 Seriously, how is this not *already* crystal-clear? I feel as if
 every few
 weeks you're just coming up with deliberately random shit to argue so
 the
 rest of us have to waste our time spelling out the obvious in insanel=





 pedantic detail.

It sometimes happened to me to be reach the hypothesis that my interlocutor must be some idiot. Most often I was missing something.

I get the impression that you find "Devil's advocate" a useful tool for generating debate and out of the box thinking.. there is something to b=



 said for that, but it's probably less annoying to some if you're clear
 about that from the beginning. :p

Where did it seem I was playing devil's advocate? Thanks.

"Devil's Advocate" is perhaps not the right term, as you don't seem to ev=

 argue the opposite to what you believe. =C2=A0But, it occasionally seems =

 that you imply ignorance on your part, in order to draw more information
 from other posters on exactly what they think or are proposing. =C2=A0So,=

 get frustrated as they feel they have to explain "everything" to you (and
 not just you, there have been times where - for whatever reason - it seem=

 that anything less than a description of every single minute detail resul=

 in a miss understanding - no doubt partly due to the medium in which we a=

 communicating).


 Regan

 --
 Using Opera's revolutionary email client: http://www.opera.com/mail/

I think that is technically called being facetious. -- James Miller
Feb 23 2012
prev sibling parent "Regan Heath" <regan netmail.co.nz> writes:
On Thu, 23 Feb 2012 15:13:17 -0000, James Miller <james aatch.net> wrote:
 On 23 February 2012 05:09, Regan Heath <regan netmail.co.nz> wrote:
 On Tue, 21 Feb 2012 14:19:17 -0000, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:

 On 2/21/12 5:55 AM, Regan Heath wrote:
 On Sun, 19 Feb 2012 23:04:59 -0000, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:

 On 2/19/12 4:00 PM, Nick Sabalausky wrote:

 Seriously, how is this not *already* crystal-clear? I feel as if
 every few
 weeks you're just coming up with deliberately random shit to argue  
 so
 the
 rest of us have to waste our time spelling out the obvious in  
 insanely
 pedantic detail.

It sometimes happened to me to be reach the hypothesis that my interlocutor must be some idiot. Most often I was missing something.

I get the impression that you find "Devil's advocate" a useful tool for generating debate and out of the box thinking.. there is something to be said for that, but it's probably less annoying to some if you're clear about that from the beginning. :p

Where did it seem I was playing devil's advocate? Thanks.

"Devil's Advocate" is perhaps not the right term, as you don't seem to ever argue the opposite to what you believe. But, it occasionally seems to me that you imply ignorance on your part, in order to draw more information from other posters on exactly what they think or are proposing. So, some get frustrated as they feel they have to explain "everything" to you (and not just you, there have been times where - for whatever reason - it seems that anything less than a description of every single minute detail results in a miss understanding - no doubt partly due to the medium in which we are communicating). Regan -- Using Opera's revolutionary email client: http://www.opera.com/mail/

I think that is technically called being facetious.

Doesn't seem quite right to me: http://dictionary.reference.com/browse/facetious R -- Using Opera's revolutionary email client: http://www.opera.com/mail/
Feb 24 2012
prev sibling next sibling parent reply "Nathan M. Swan" <nathanmswan gmail.com> writes:
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.
Feb 18 2012
parent reply Ben Davis <entheh cantab.net> writes:
On 18/02/2012 21:07, Andrej Mitrovic wrote:
 On 2/18/12, Nathan M. Swan<nathanmswan gmail.com>  wrote:
 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.

It would be even nicer if we didn't need a mixin for classes that simply forward the ctor call to the base class ctor: class FileException : Exception { } // no-go, have to write a ctor that forwards to super AIUI this has something to do with ctors not being virtual. I think someone mentioned this could be improved one day.

I guess this is a bit off topic, but what you probably want is syntactic sugar that says "declare constructors matching all super constructors and calling through to them" and can be used in ANY class hierarchy (not necessarily exceptions). For example: class Subtype : Supertype { super all; } If you want to expose just specific constructors, then there could also be a shorthand for "declare a constructor matching a specific super constructor and calling through to it" - so you don't have to repeat all the arguments. For example: class Subtype : Supertype { super(); super(string,int); } That would then make it an entirely separate issue and completely not Exception-specific.
Feb 18 2012
parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Saturday, February 18, 2012 17:20:41 H. S. Teoh wrote:
 On Sun, Feb 19, 2012 at 01:10:10AM +0000, Ben Davis wrote:
 [...]
 
 I guess this is a bit off topic, but what you probably want is
 syntactic sugar that says "declare constructors matching all super
 constructors and calling through to them" and can be used in ANY class
 hierarchy (not necessarily exceptions). For example:
 
 class Subtype : Supertype {
 
     super all;
 
 }
 
 If you want to expose just specific constructors, then there could
 also be a shorthand for "declare a constructor matching a specific
 super constructor and calling through to it" - so you don't have to
 repeat all the arguments. For example:
 
 class Subtype : Supertype {
 
     super();
     super(string,int);
 
 }
 
 That would then make it an entirely separate issue and completely
 not Exception-specific.

+1. This is definitely something not specific to Exception. Quite often, you want to create a derived class overriding just one or two members of the base class, but end up having to copy-n-paste most of the many ctors in the base class along with their elaborate arguments just so you can pass through the arguments to them. Having a way of simply saying "the ctors in this class default to the base class ctors" will solve this problem in a very nice and logical way. (I.e., you're sortof "inheriting the base class ctors", although not exactly, of course.)

Feel free to open a pull request. It would certainly be a useful feature for reducing boilerplate code in some class hierarchies (though in many, the constructors in the derived class must be different, in which case it wouldn't work). One potential issue though is that not all base classes necessarily share the same constructors. Which ones would be grabbed? The ones from the immediate base class? All of them? Only the ones common to all? - Jonathan M Davis
Feb 18 2012
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 02/19/2012 02:23 AM, Jonathan M Davis wrote:
 One potential issue though is that not all base classes necessarily share the
 same constructors. Which ones would be grabbed? The ones from the immediate
 base class? All of them? Only the ones common to all?

There is only one base class. Inheriting constructors from anywhere further up the hierarchy cannot work, because then the base class cannot be constructed.
Feb 18 2012
parent Ben Davis <entheh cantab.net> writes:
On 19/02/2012 02:19, Timon Gehr wrote:
 On 02/19/2012 02:23 AM, Jonathan M Davis wrote:
 One potential issue though is that not all base classes necessarily
 share the
 same constructors. Which ones would be grabbed? The ones from the
 immediate
 base class? All of them? Only the ones common to all?

There is only one base class. Inheriting constructors from anywhere further up the hierarchy cannot work, because then the base class cannot be constructed.

To phrase it another way, you could say that the direct base class has already decided which of ITS base class's constructors to wrap and expose. The direct base class's decision is final, so the class extending it further can only base its constructors on that.
Feb 19 2012
prev sibling parent deadalnix <deadalnix gmail.com> writes:
Le 19/02/2012 02:23, Jonathan M Davis a écrit :
 On Saturday, February 18, 2012 17:20:41 H. S. Teoh wrote:
 On Sun, Feb 19, 2012 at 01:10:10AM +0000, Ben Davis wrote:
 [...]

 I guess this is a bit off topic, but what you probably want is
 syntactic sugar that says "declare constructors matching all super
 constructors and calling through to them" and can be used in ANY class
 hierarchy (not necessarily exceptions). For example:

 class Subtype : Supertype {

      super all;

 }

 If you want to expose just specific constructors, then there could
 also be a shorthand for "declare a constructor matching a specific
 super constructor and calling through to it" - so you don't have to
 repeat all the arguments. For example:

 class Subtype : Supertype {

      super();
      super(string,int);

 }

 That would then make it an entirely separate issue and completely
 not Exception-specific.

+1. This is definitely something not specific to Exception. Quite often, you want to create a derived class overriding just one or two members of the base class, but end up having to copy-n-paste most of the many ctors in the base class along with their elaborate arguments just so you can pass through the arguments to them. Having a way of simply saying "the ctors in this class default to the base class ctors" will solve this problem in a very nice and logical way. (I.e., you're sortof "inheriting the base class ctors", although not exactly, of course.)

Feel free to open a pull request. It would certainly be a useful feature for reducing boilerplate code in some class hierarchies (though in many, the constructors in the derived class must be different, in which case it wouldn't work). One potential issue though is that not all base classes necessarily share the same constructors. Which ones would be grabbed? The ones from the immediate base class? All of them? Only the ones common to all? - Jonathan M Davis

Well that should be achievable with compile time refexion and CTFE/mixin or mixin template. Maybe this should be inserted in phobos.
Feb 19 2012
prev sibling next sibling parent "Martin Nowak" <dawg dawgfoto.de> writes:
On Sat, 18 Feb 2012 20:24:16 +0100, deadalnix <deadalnix gmail.com> wrot=
e:

 Le 18/02/2012 20:12, Martin Nowak a =C3=A9crit :
 Typed exception being used for local error recovery is about the same=


 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, o=

 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, yo=

 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.
Feb 18 2012
prev sibling next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
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
Feb 18 2012
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/18/12 1:41 PM, H. S. Teoh wrote:
 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.

It's great that you bring expertise from the Java world. I should note that the above does little in the way of putting an argument to the table. It appeals to subjective qualifications ("poorly designed", "well designed") so one's only recourse to getting convinced is just believing what you say without any evidence. It's equally unimpressive that FileNotFoundException and DiskFullException inherit IOException; seeing that one might say "yeah, sure" but there should be some compelling evidence that the distinction makes a difference.
 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.

From this and other posts I'd say we need to design the base exception classes better, for example by defining an overridable property isTransient that tells caller code whether retrying might help.
 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?
 	}

It seems exceptions still are not entirely understood, and I agree that adding some random mechanism doesn't do good.
 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.

Yes. Types, to be more general. The question is why are various error deserving of their own types.
 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.

I didn't contend the opposite. Andrei
Feb 18 2012
next sibling parent "Nick Sabalausky" <a a.a> writes:
Andrei Alexandrescu wrote:
catching by type is most of the time useless.

Andrei Alexandrescu wrote...
 I should note that the above does little in the way of putting an argument 
 to the table. It appeals to subjective qualifications ("poorly designed", 
 "well designed")

/facepalm Just once, could you be as incredibly pedantic with your own arguments as you are with everyone else's?
Feb 18 2012
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/18/12 8:00 PM, H. S. Teoh wrote:
  From this and other posts I'd say we need to design the base exception
 classes better, for example by defining an overridable property
 isTransient that tells caller code whether retrying might help.

Just because an exception is transient doesn't mean it makes sense to try again. For example, saveFileMenu() might read a filename from the user, then save the data to a file. If the user types an invalid filename, you will get an InvalidFilename exception. From an abstract point of view, an invalid filename is not a transient problem: retrying the invalid filename won't make the problem go away. But the application in this case *wants* to repeat the operation by asking the user for a *different* filename. On the other hand, if the same exception happens in an app that's trying to read a configuration file, then it *shouldn't* retry the operation.

I'm thinking an error is transient if retrying the operation with the same exact data may succeed. That's a definition that's simple, useful, and easy to operate with. Andrei
Feb 18 2012
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/19/12 12:54 AM, H. S. Teoh wrote:
 On Sun, Feb 19, 2012 at 12:43:58AM -0600, Andrei Alexandrescu wrote:
 On 2/18/12 8:00 PM, H. S. Teoh wrote:
  From this and other posts I'd say we need to design the base exception
 classes better, for example by defining an overridable property
 isTransient that tells caller code whether retrying might help.

Just because an exception is transient doesn't mean it makes sense to try again. For example, saveFileMenu() might read a filename from the user, then save the data to a file. If the user types an invalid filename, you will get an InvalidFilename exception. From an abstract point of view, an invalid filename is not a transient problem: retrying the invalid filename won't make the problem go away. But the application in this case *wants* to repeat the operation by asking the user for a *different* filename. On the other hand, if the same exception happens in an app that's trying to read a configuration file, then it *shouldn't* retry the operation.

I'm thinking an error is transient if retrying the operation with the same exact data may succeed. That's a definition that's simple, useful, and easy to operate with.

But if that's the case, what's the use of an exception at all?

Centralization. Andrei
Feb 19 2012
next sibling parent reply deadalnix <deadalnix gmail.com> writes:
Le 19/02/2012 09:02, Andrei Alexandrescu a crit :
 On 2/19/12 12:54 AM, H. S. Teoh wrote:
 On Sun, Feb 19, 2012 at 12:43:58AM -0600, Andrei Alexandrescu wrote:
 On 2/18/12 8:00 PM, H. S. Teoh wrote:
 From this and other posts I'd say we need to design the base exception
 classes better, for example by defining an overridable property
 isTransient that tells caller code whether retrying might help.

Just because an exception is transient doesn't mean it makes sense to try again. For example, saveFileMenu() might read a filename from the user, then save the data to a file. If the user types an invalid filename, you will get an InvalidFilename exception. From an abstract point of view, an invalid filename is not a transient problem: retrying the invalid filename won't make the problem go away. But the application in this case *wants* to repeat the operation by asking the user for a *different* filename. On the other hand, if the same exception happens in an app that's trying to read a configuration file, then it *shouldn't* retry the operation.

I'm thinking an error is transient if retrying the operation with the same exact data may succeed. That's a definition that's simple, useful, and easy to operate with.

But if that's the case, what's the use of an exception at all?

Centralization. Andrei

Please stop answering like that. From the begining of this topic Jonathan M Davis, H. S. Teah (and myself ?) raised very valid points. What do you expect from that discussion if yourself you do not put any arguement on the table ?
Feb 19 2012
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/19/12 6:35 AM, deadalnix wrote:
 Le 19/02/2012 09:02, Andrei Alexandrescu a crit :
 I'm thinking an error is transient if retrying the operation with the
 same exact data may succeed. That's a definition that's simple,
 useful, and easy to operate with.

But if that's the case, what's the use of an exception at all?

Centralization. Andrei

Please stop answering like that. From the begining of this topic Jonathan M Davis, H. S. Teah (and myself ?) raised very valid points. What do you expect from that discussion if yourself you do not put any arguement on the table ?

The answer is meaningful. The purpose of exceptions is allowing for centralized error handling, and a capabilities-based system makes that simple (e.g. you get to make decisions about recoverability in one place, regardless of which part of the exception hierarchy the exception originated. Andrei
Feb 19 2012
parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday, February 19, 2012 14:26:55 H. S. Teoh wrote:
 On Sun, Feb 19, 2012 at 08:35:08AM -0600, Andrei Alexandrescu wrote:
 On 2/19/12 6:35 AM, deadalnix wrote:
Le 19/02/2012 09:02, Andrei Alexandrescu a =C3=A9crit :
I'm thinking an error is transient if retrying the operation wi=






same exact data may succeed. That's a definition that's simple,=






useful, and easy to operate with.

[...] But if that's the case, what's the use of an exception at all?

Centralization. Andrei

Please stop answering like that. From the begining of this topic Jonathan M Davis, H. S. Teah (and myself ?) raised very valid poin=



What do you expect from that discussion if yourself you do not put=



arguement on the table ?

The answer is meaningful. The purpose of exceptions is allowing for=


 centralized error handling, and a capabilities-based system makes t=


 simple (e.g. you get to make decisions about recoverability in one
 place, regardless of which part of the exception hierarchy the
 exception originated.

[...] =20 And how exactly do you propose centralized error handling to work in =

 GUI application, one of the many dialogs of which asks the user for a=

 filename, and retries if the file doesn't exist? If the low-level
 filesystem code throws an exception, which could be anything from fil=

 not found to disk error to out of memory, then how exactly does your
 "centralized error handler" determine what course of action to take? =

 does it know which dialog to retry?
=20
 You're trying to have a centralized error handler that handles *all*
 exceptions regardless of where they come from and what triggered them=

 and independently of what operation might need to be retried if an
 exception does happen? I can't see how such a system could possibly b=

 realized in a concrete way. This sounds like premature generalization=

 me.

I don't think that that's quite what he means. If you're using error co= des in=20 a function, you have to check them on practically every call to another= =20 function. This makes a mess of your code. If you use exceptions, on the= other=20 hand, you don't have any of those checks in your code. Rather, you wrap= the=20 code in a try-catch block, and put your error-handling code in one plac= e=20 within the function. _That_ is centralizing it. In some cases, that means a try-catch block at a higher level without a= ny in=20 sub functions, because it makes the most sense for a function higher up= to=20 handle the exceptions there (e.g. if you're going to want to pop up a d= ialag=20 to ask the user to give a different file, you want that higher up, not = in=20 std.file), and in some cases, you want the try-catch block to be much c= loser to=20 where the throw occurred (sometimes even in the same function). Now, maybe Andrei meant centralizing the error handling even more than = that,=20 but it makes so little sense to have _one_ place in your program handli= ng all=20 of the exceptions that I have a very hard time believing that that's wh= at he=20 really meant. - Jonathan M Davis
Feb 19 2012
prev sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/19/12 6:35 AM, deadalnix wrote:
 Le 19/02/2012 09:02, Andrei Alexandrescu a crit :
 I'm thinking an error is transient if retrying the operation with the
 same exact data may succeed. That's a definition that's simple,
 useful, and easy to operate with.

But if that's the case, what's the use of an exception at all?

Centralization. Andrei

Please stop answering like that. From the begining of this topic Jonathan M Davis, H. S. Teah (and myself ?) raised very valid points. What do you expect from that discussion if yourself you do not put any arguement on the table ?

The answer is meaningful. The purpose of exceptions is allowing for centralized error handling, and a capabilities-based system makes that simple (e.g. you get to make decisions about recoverability in one place, regardless of which part of the exception hierarchy the exception originated. Andrei
Feb 19 2012
prev sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/19/12 3:15 AM, H. S. Teoh wrote:
 I don't understand.  So instead of providing enough information to the
 caller about the nature of the problem, you're essentially handing them
 an anonymous note saying "A generic problem occurred, which _may_ go
 away if you retry. I have no further information for you. Do you want to
 retry?"?

 But without further information, how *can* you even make that decision?
 Without any way of determining what caused the error or even what it is,
 how could you know whether it makes sense to retry it?

 Or is that transient flag intended to mean that it *should* be retried
 since it "might" succeed the next time round? How should the caller
 decide whether or not to go ahead with the retry? Flip a coin? Always
 retry?  Always fail? I can't imagine any sane application where code
 would say "if this operation fails with a transient error, always fail"
 where any arbitrary set of exceptions might potentially be transient?
 What's a "transient error" anyway, from the application's POV anyway?
 What's a "transient error" from a database app's POV? A 3D shooter? A
 text editor? Is it even possible to have a consistent definition of
 "transient" that is meaningful across all applications?

I mentioned the definition a couple of times. Transient means retrying the operation with the same state may succeed. It is a property of the exception regardless of context. Of course, caller code may ignore it or use additional information. Even network errors may be of both kinds. A network timeout error is transient. A DNS lookup error or malformed URL are not.
 It seems to me that if an error is "transient", then the called function
 might as well just always retry it in the first place, instead of
 throwing an exception.

Client code must decide on the appropriate action, including UI cues, a configurable number of retries, logging, and such.
 Such an exception is completely meaningless to
 the caller without further information. Yet it seems that you are
 suggesting that it's more meaningful than FileNotFoundException?

This is a misunderstanding. Transiency is a cross-cutting property. A FileNotFoundException is great (and non-transient btw). Andrei
Feb 19 2012
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/19/12 1:12 AM, Jonathan M Davis wrote:
 On Sunday, February 19, 2012 00:43:58 Andrei Alexandrescu wrote:
 On 2/18/12 8:00 PM, H. S. Teoh wrote:
    From this and other posts I'd say we need to design the base exception

 classes better, for example by defining an overridable property
 isTransient that tells caller code whether retrying might help.

Just because an exception is transient doesn't mean it makes sense to try again. For example, saveFileMenu() might read a filename from the user, then save the data to a file. If the user types an invalid filename, you will get an InvalidFilename exception. From an abstract point of view, an invalid filename is not a transient problem: retrying the invalid filename won't make the problem go away. But the application in this case *wants* to repeat the operation by asking the user for a *different* filename. On the other hand, if the same exception happens in an app that's trying to read a configuration file, then it *shouldn't* retry the operation.

I'm thinking an error is transient if retrying the operation with the same exact data may succeed. That's a definition that's simple, useful, and easy to operate with.

A core problem with the idea is that whether or not it makes sense to try again depends on what the caller is doing. In general, I think that it's best to give the caller as much useful information is possible so that _it_ can decide the best way to handle the exception.

That sounds like "I violently agree". Andrei
Feb 19 2012
next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/19/12 6:27 AM, Juan Manuel Cabo wrote:
 How about adding a string[string] or a variant[string] to the Exception
 class, so one can know details about the subclassed exception without
 downcasting?

That is a must for custom formatting and i18n. Andrei
Feb 19 2012
prev sibling next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2012-02-19 13:27, Juan Manuel Cabo wrote:
 How about adding a string[string] or a variant[string] to the Exception
 class, so one can know details about the subclassed exception without
 downcasting? How ugly would that be?

 For instance:

 ...
 catch (Exception ex) {
 if ("transient" in ex.details) {
 repeatOneMoreTime();
 }
 if ("i18n_code" in ex.details) {
 log(translate(ex.details["i18n_code"]));
 }
 }
 ...

 Details can be standard by convention or otherwise custom.
 (I can see that this can lead to messy proliferation of details, but at
 least solves most of the issues).

How would you know which keys are available in "ex.details", documentation? -- /Jacob Carlborg
Feb 19 2012
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/19/12 8:54 AM, Jacob Carlborg wrote:
 On 2012-02-19 13:27, Juan Manuel Cabo wrote:
 How about adding a string[string] or a variant[string] to the Exception
 class, so one can know details about the subclassed exception without
 downcasting? How ugly would that be?

 For instance:

 ...
 catch (Exception ex) {
 if ("transient" in ex.details) {
 repeatOneMoreTime();
 }
 if ("i18n_code" in ex.details) {
 log(translate(ex.details["i18n_code"]));
 }
 }
 ...

 Details can be standard by convention or otherwise custom.
 (I can see that this can lead to messy proliferation of details, but at
 least solves most of the issues).

How would you know which keys are available in "ex.details", documentation?

Programmatically, too. A string templating engine would take the table and format it for the appropriate language. Andrei
Feb 19 2012
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/19/12 3:17 AM, Jonathan M Davis wrote:
 "As much information as possible" is way more than a transient property. If my
 code is going to retry or do something else or give up, it needs enough
 information to know what went wrong, not just whether the function which was
 called think it might work on a second try.

 Having an exception hierarchy provides some of that information simply with
 the types, and makes it easier to organize code based on what went wrong (e.g.
 separate catch blocks for each type of exception). And having that hierarchy
 also means that the derived types can have additional information beyond their
 type which could be useful but is specific to that problem and so wouldn't make
 sense on a general exception type.

 I really don't see what transient buys you in comparison to that.

A notion of transiency planted fundamentally in all exceptions allows one to act on it regardless of origin and hierarchy. Andrei
Feb 19 2012
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 02/19/2012 05:00 PM, Andrei Alexandrescu wrote:
 On 2/19/12 3:17 AM, Jonathan M Davis wrote:
 "As much information as possible" is way more than a transient
 property. If my
 code is going to retry or do something else or give up, it needs enough
 information to know what went wrong, not just whether the function
 which was
 called think it might work on a second try.

 Having an exception hierarchy provides some of that information simply
 with
 the types, and makes it easier to organize code based on what went
 wrong (e.g.
 separate catch blocks for each type of exception). And having that
 hierarchy
 also means that the derived types can have additional information
 beyond their
 type which could be useful but is specific to that problem and so
 wouldn't make
 sense on a general exception type.

 I really don't see what transient buys you in comparison to that.

A notion of transiency planted fundamentally in all exceptions allows one to act on it regardless of origin and hierarchy. Andrei

Transiency is a powerful concept at the handler side, but the interface it is difficult to fulfil at the point where the actual error occurs. What is important is probably not whether or not transiency is useful if it is there, but more whether or not a sufficient part of the useful exceptions are naturally transient. This is what I doubt. OTOH, as I understand it, introducing the concept would require additional boilerplate in most exception handlers.
Feb 19 2012
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/19/12 11:48 AM, Timon Gehr wrote:
 On 02/19/2012 05:00 PM, Andrei Alexandrescu wrote:
 On 2/19/12 3:17 AM, Jonathan M Davis wrote:
 "As much information as possible" is way more than a transient
 property. If my
 code is going to retry or do something else or give up, it needs enough
 information to know what went wrong, not just whether the function
 which was
 called think it might work on a second try.

 Having an exception hierarchy provides some of that information simply
 with
 the types, and makes it easier to organize code based on what went
 wrong (e.g.
 separate catch blocks for each type of exception). And having that
 hierarchy
 also means that the derived types can have additional information
 beyond their
 type which could be useful but is specific to that problem and so
 wouldn't make
 sense on a general exception type.

 I really don't see what transient buys you in comparison to that.

A notion of transiency planted fundamentally in all exceptions allows one to act on it regardless of origin and hierarchy. Andrei

Transiency is a powerful concept at the handler side, but the interface it is difficult to fulfil at the point where the actual error occurs. What is important is probably not whether or not transiency is useful if it is there, but more whether or not a sufficient part of the useful exceptions are naturally transient. This is what I doubt. OTOH, as I understand it, introducing the concept would require additional boilerplate in most exception handlers.

I'm thinking of simply adding property transient() const pure { return false; } to the base exception class. Most exceptions are not transient. Andrei
Feb 19 2012
prev sibling next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
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
Feb 18 2012
prev sibling next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
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.
Feb 18 2012
prev sibling next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Sat, Feb 18, 2012 at 08:20:23PM +0100, deadalnix wrote:
[...]
 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.

I agree. [...]
 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.

Exactly. It's important to distinguish between exceptions sometimes. For example: File promptSaveFile() { do { string filename = readInput(); try { return File(filename); } catch(FileNotFoundException e) { writeln("No such file, please try " "again"); } } while(true); assert(false); } It would not make sense for that catch to be *any* Exception; for example, if the exception was ReadFailureException, you do *not* want to catch that, but you want to propagate it. But sometimes, you *want* to handle ReadFailureException, for example: void salvageBadSector(out ubyte[] sector) { int n_attempts = 10; while(n_attempts > 0) { try { disk.read(sector); } catch(ReadFailureException e) { disk.recalibrate(); n_attempts--; continue; // try again } // If another error occurs, say OutOfMemory, // or some other unexpected problems, you do NOT // want to continue. } writeln("Cannot salvage data from bad sector"); } Having distinct exception types allows you to do this without resorting to hacks, or bypassing the library altogether (because of the use of overly generic exception types). Having a proper exception class hierarchy is also necessary. For example, most programs don't care about the difference between FileNotFoundException and ReadFailureException; they just want to print an error message and cleanup if I/O fails: Data loadData() { try { return readFromFile(); } catch(IOException e) { writeln("I/O error occurred, aborting"); cleanup(); exit(1); } assert(false); } It would be wrong to just catch Exception here, because if it wasn't an I/O error, but something else, it needs to be propagated!
 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.

Agreed. [...]
 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.

Phobos NEEDS to have a sane exception hierarchy. ESPECIALLY since it is the standard library. So you cannot assume your users cares or don't care about distinguishing between exception types. Some applications just catch Exception, cleanup and exit, but other applications need to know the difference between FileNotFoundException and ReadErrorException. So we need to design a good exception hierarchy for Phobos. The worst thing is if Phobos exceptions are too generic, then applications that need to tell between different problems will have to bypass Phobos and hack their own solution. Which is not good. The standard library should be maximally useful to the extent that it can be.
 Exception type is a convenient way to filter what you catch and what
 you don't know how to handle at this point.

Agreed. T -- The right half of the brain controls the left half of the body. This means that only left-handed people are in their right mind. -- Manoj Srivastava
Feb 18 2012
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 2/18/12, Nathan M. Swan <nathanmswan gmail.com> wrote:
 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.

It would be even nicer if we didn't need a mixin for classes that simply forward the ctor call to the base class ctor: class FileException : Exception { } // no-go, have to write a ctor that forwards to super AIUI this has something to do with ctors not being virtual. I think someone mentioned this could be improved one day.
Feb 18 2012
prev sibling next sibling parent "Martin Nowak" <dawg dawgfoto.de> writes:
 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.

may occur. That remove may fail if you haven't checked the path is clear. What I'm saying is that error returns are more flexible for interfaces that have know error cases. You can then either directly handle them or you may translate them to exceptions using enforce. Always using exceptions forces a certain programming model on you. uint toUint(int value) { if (value < 0) throw new ConvUnderflowException("Negative Value"); return cast(uint)value; } void main(string[] args) { int ival = parse!int(args[1]); uint uval; try { uval = toUint(ival); } catch (ConvUnderflowException) { uval = 0; } }
Feb 18 2012
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Saturday, February 18, 2012 20:18:53 Nathan M. Swan wrote:
 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.

If someone just wants to handle generic exceptions, then they can catch Exception. That's no reason to not actually have a proper exception hierarchy that programmers can use if they want to. Module-specific works on some level precisely because modules generally encompass specific behaviors (e.g. file handling or string handling), but a really well-designed exception hierarchy would not be specifically tied to modules the way that we are now. Rather, we'd have exceptions specific to behaviors, and different modules would reuse exceptions from other modules where appropriate based on what they're doing.
 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.

I've considered creating one (it woud be quite easy), but the problem is that you don't end up with any ddoc documentation for them. - Jonathan M Davis
Feb 18 2012
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Saturday, February 18, 2012 17:13:16 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.

If you actually want to _handle_ exceptions, how else do you expect to do it? Simply put a code of some kind on the exceptions and then have switch statement to handle them? If you want to go beyond simply printing out an error message when an exception occurs, having a solid exception hierarchy is the way to go. Otherwise, all that your program is has is the knowledge that something went wrong and a message that it could print out to the user which might inform them as to want went wrong, but it has _nothing_ which will help the program itself handle the problem appropriately. Honestly, I think that on the whole, this is something that Java got right. They might use exceptions for more than they should (e.g. if the odds of a function failing are very high, don't use an exception, have it return whether it succeeded or not - Java pretty much never does that). But on the whole, I think that they have the correct approach. - Jonathan M Davis
Feb 18 2012
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Saturday, February 18, 2012 17:30:10 Andrei Alexandrescu wrote:
 On 2/18/12 5:20 PM, Jonathan M Davis wrote:
 On Saturday, February 18, 2012 17:13:16 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.

If you actually want to _handle_ exceptions, how else do you expect to do it? Simply put a code of some kind on the exceptions and then have switch statement to handle them?

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. And none of that makes sense as virtual functions. And yes, in this case, you're still printing out in every case rather than doing more dynamic handling, but you get much better error messages here, and it may be that a program would want to do something fancier than what I'm doing here (particularly if you're using delegates with getopt). And of course other exception types lend themselves better to doing extra handling rather than printing messages (e.g. parsing exceptions or file handling exceptions might make it so that the program tries again or tries something else, and the user doesn't get involved at all). And even if all of the exception types that you were dealing with were identical save for their type, they could still be very useful. Knowing that you got an IOException when handling a stream rather than a UTFException gives you some idea of what's wrong even if the exception types aren't giving much in the way of additional useful information (though hopefully, they would). In order to actually handle exceptions rather than just print out messages (or to print out more useful messages like in the case of getopt), you need to have exceptions with types of _some_ kind so that code can know what it's dealing with. Messages are really only of any use for humans. So, you either end up with an exception with a field indicating its type (which really doesn't scale very well - particularly when you don't control all of the code you're dealing with and someone could screw up and reuse the same value for as an existing type) which you then switch on, or you have the exceptions in an inheritance hierarchy to indicate their type, which works much better for giving additional information, because then that information can be in fields in the derived exception type rather than the equivalent of a void* in a field in Exception which then must be cast appropriately based on the type field. - Jonathan M Davis
Feb 18 2012
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Saturday, February 18, 2012 18:09:30 Andrei Alexandrescu wrote:
 Of course I can. They call it toString(). The code above pretty much
 proves my point with so much wonderful irony.

How?!! toString doesn't help _at all_. It just gives whatever getopt can come up with, not what's appropriate for your program. And right now, since there's no GetOptException, you just end up with a ConvException, so you don't even get a semi-decent toString. You have _no idea_ what flag had the error. The best that you can do is print out that _something_ went wrong, not _what_. And if you want to do fancier handling (_especially_ if you want recover from an exception rather than just print a message), you need _far_ more than toString. Maybe I want to look at the flag that you gave me which was invalid and then suggest what you might have meant. Without _at least_ knowing what the flag was that you used (which I can't do with the current getopt), I can't do that. How could you do something even close to my example with getopt as it is now? Perhaps getopt is not the best example, because it's likely to end up with you giving an error to the user regardless. But other exception handling wouldn't necessarily involve the user at all. The primary issue is giving the program the information that it needs to actually handle and potentially recover from the exception. A string doesn't do that. Giving the exception a specific type according to what went wrong and additional member variables with information on what went wrong _can_ do that. What if something goes wrong when processing a file? Did something go wrong with the file operations or in processing the information? A FileException vs another exception type (e.g. UTFException or ParseException) would tell you that. And if you want to recover from the exception and continue processing the file, knowing what type of ParseException it was could help you do that by giving you the information that you need to continue - which could vary quite a bit depending on what went wrong. This gets even more critical the larger your program is. The farther you get away from the code that threw the exception, the more worthless a generic exception type is (be it Exception or even something somewhat more specific like FileException), and the program can't really handle it. It's forced to either ignore it (possibly logging something), retry what it's trying to do, or give up on what it's trying to do. In general, handling the exceptions closer to their source is better, which helps, but that's not always possible. Even a slightly more advanced exception hierarchy (e.g. having GetOptException with a flag, even if you don't actually derive any other exception types from it) would be an improvement over what we have now. - Jonathan M Davis
Feb 18 2012
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Saturday, February 18, 2012 17:53:52 Andrei Alexandrescu wrote:
 On 2/18/12 5:47 PM, Jakob Ovrum wrote:
 you are basically arguing against exceptions here

I must have argued my question and point very poorly.

You definitely seem to be arguing for a single Exception type, which does _not_ make for good error handling code, since all it tells you is that _something_ went wrong, not what. And that goes against how Exceptions are designed. They purposely are designed to have an inheritance hierarchy. I grant you that simply having an exception type per module which is identical to Exception save for its name and the fact that it's derived from Exception rather than Throwable is not the best way to go. I do think that it's somewhat better than just using Exception everywhere, since it gives you a better idea of went wrong (e.g. UTFException rather than a TimeException), but ideally, it would give much better information than that, and we'd have a better exception hierarchy with related exceptions being derived from one another or from a common exception type rather than simply being based on what module they're in. C++ is a horrible example of how exceptions should be done, so if you're basing what you want off of that, then that makes me think that you should be better familiar with how other, more recent languages use them (though maybe you're quite familiar with how C# and/or Java use Exceptions, I don't know).
From using Java, I think that how it handles exceptions in general is _far_ 

depend on who's doing the developing, since you _can_ have a decent exception hierarchy in C++). - Jonathan M Davis
Feb 18 2012
prev sibling next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
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.
Feb 18 2012
parent reply Jim Hewes <jimhewes gmail.com> writes:
On 2/18/2012 5:59 PM, Robert Jacques wrote:
 But you _always_ know what went wrong: An unexpected error occurred
 while trying to do X, where X is whatever is inside the try-catch block.
 Exceptions are for exceptional situations...

Not to jump on you in particular for using the phrase "exceptions are for exceptional situations”, but I've always heard people say this in arguments about exceptions vs. returning error codes. Unfortunately those people usually never go on to define what exceptional situations are, so those trying to learn about using exceptions are left no better off. If exceptional cases are like divide-by-zero or hardware failures, then surely a bad parameter to a function is not “exceptional”. Then what? Is out-of-memory exceptional or something you should expect might happen given that memory is finite? I think of exception handling as tied to contract programming. A function has a specific job that it's supposed to do. If for any reason it cannot do that job successfully, an exception should be thrown. That can include even bad parameters (although if you have bad parameters to internal functions I'd think that is a design bug and could be handled by asserts). Look at the .NET library; it seems to work this way. So I think the term 'exceptional situations' has been kind of useless.
 and not supposed to be used as
 a form of out of band information return (or at least that's what every
 programming book tells me).

If what you mean is that exceptions should not be used to return information when the function is successful, I agree. But it can be used to return extra details about errors when a function fails.
 I see typed exceptions as having a strong
 coupling problem; exception handling that is aware enough to handle a
 typed exception, is so close to the exception generation code that most
 of the information conveyed by the 'type' is also conveyed by the
 context. Now, the further away exception handling gets from exception
 generation, the less information context coveys, but the ability of the
 handler to do anything smarter than log the exception and retry / cancel
 becomes less and less.

I can't say agree there, if I'm understand you right. Not every exception coming out of a function was generated by that function. It may have come from several levels below that. Maybe you can handle the former because it is more immediate but not the latter. So without exception types how would you know the difference between them? You could use error codes and switch on them but it defeats one of the main purposes of exception handling. So I think if there are exceptions generated from further away, it's an argument _for_ exception types rather than against it. I can think of a example. At work I wrote a driver that communicates to a device over USB. If the device is suddenly unplugged, the lowest level of code throws a “connection” exception. And this can happen in several places. Most levels can't do anything about this (other than just be exception safe) and the exception just percolates up where things get closed and cleaned up. However, it would be possible, though not currently implemented, to recover from this such that the driver waits for the device to get reconnected and refreshes handles and all that, and the application and user above never need to know. Where that recovery happens isn't necessarily at the lowest level but somewhere in a middle layer. So I think having a particular exception type is useful here. But I don't want to have to check error codes because under normal circumstances there are just too many places to check for this type of error.
 The other issue I see with typed exceptions is
 that the classic 'is a' relationship seems weak; to be informative and
 useful and exception must map to a specific or small set of
 error/unexpected conditions. However, this specificity naturally leads
 to large number of final classes, which isn't really leveraging OO
 principals. It's hard to see what, if any, benefit the intermediate base
 classes give over a generic exception.

Well, I think what H.S Tech has been saying in this thread makes sense to me. You can have an exception hierarchy where base classes represent groups or classes of errors that you can check for without having to catch all exception. But you don't have thousands of classes where you map each possible error code to a unique class. You can just have more general types and then put the specific information in the exception. For example, just have one BadParameter exception and then store information about which parameter is bad and why in the exception. Jim
Feb 18 2012
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/18/12 11:09 PM, Jim Hewes wrote:
 I think of exception handling as tied to contract programming. A
 function has a specific job that it's supposed to do. If for any reason
 it cannot do that job successfully, an exception should be thrown. That
 can include even bad parameters (although if you have bad parameters to
 internal functions I'd think that is a design bug and could be handled
 by asserts). Look at the .NET library; it seems to work this way. So I
 think the term 'exceptional situations' has been kind of useless.

I think there's a bit of a confusion there. In fact, I dedicated two distinct chapters to error handling and contract programming in TDPL in an attempt to dispel it. Andrei
Feb 18 2012
parent Jim Hewes <jimhewes gmail.com> writes:
On 2/18/2012 10:50 PM, Andrei Alexandrescu wrote:
 On 2/18/12 11:09 PM, Jim Hewes wrote:

 I think there's a bit of a confusion there. In fact, I dedicated two
 distinct chapters to error handling and contract programming in TDPL in
 an attempt to dispel it.

 Andrei

Sorry, I have not read your book. At least I can say that if you felt there was a need to dispel something then I must not be the only one who thinks this. :) Jim
Feb 19 2012
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday, February 19, 2012 00:21:38 Robert Jacques wrote:
 Not to jump on you in particular :) but bad user parameters should never be
 treated as exceptional. Even bad 'internal' parameters that are passed via
 the external API aren't exceptional. Programmers being lazy about input
 parameter checking is how hackers make their money.

I completely disagree. I think that there are many cases where throwing an exception on bad parameters is exactly what you should do. Phobos does this in many places. Often, the function knows best what it wants, and it's the best position to check it. So, having it check the input makes a _lot_ of sense in many cases. For instance, take fromISOExtString on std.datetime.SysTime. Is it at all reasonably to expect that the caller is going to verify that the string is well-formed according to the ISO standard? No. And it's highly probable tha the string being given came from I/O of some kind - be it a file or a socket or whatever. So, it makes _way_ more sense for fromISOExtString to check the string itself and throw on failure. It's also more efficient to do that, because if you checked that the string was valid before passing it to fromISOExtString, then you'd be processing the string twice. There are other cases where you want to use DbC and require that a function is given valid arguments. In those cases, you use assertions, so the checks go away in release mode. But that's not always the best way to go about. And in defensive programming, you end up checking exceptions quite heavily - even in release mode - and throwing when they're bad. The best approach depends on what you're doing, and what the functions are used for. But approaches have their place. - Jonathan M Davis
Feb 18 2012
prev sibling next sibling parent reply deadalnix <deadalnix gmail.com> writes:
Le 19/02/2012 06:09, Jim Hewes a écrit :
 On 2/18/2012 5:59 PM, Robert Jacques wrote:
 But you _always_ know what went wrong: An unexpected error occurred
 while trying to do X, where X is whatever is inside the try-catch block.
 Exceptions are for exceptional situations...

Not to jump on you in particular for using the phrase "exceptions are for exceptional situations”, but I've always heard people say this in arguments about exceptions vs. returning error codes. Unfortunately those people usually never go on to define what exceptional situations are, so those trying to learn about using exceptions are left no better off. If exceptional cases are like divide-by-zero or hardware failures, then surely a bad parameter to a function is not “exceptional”. Then what? Is out-of-memory exceptional or something you should expect might happen given that memory is finite? I think of exception handling as tied to contract programming. A function has a specific job that it's supposed to do. If for any reason it cannot do that job successfully, an exception should be thrown. That can include even bad parameters (although if you have bad parameters to internal functions I'd think that is a design bug and could be handled by asserts). Look at the .NET library; it seems to work this way. So I think the term 'exceptional situations' has been kind of useless.

Well, I think you are messing up between contract and exception. Wrong parameters is a contract problem, not an exceptionnal situation. The exemple you cited below is way more suited for an exception : someone unplug the USB device, then I trhow an Exception and it will cross all layers of my code to reach a point where it is recoverable. Except that contract detail, your post make perfect sense.
Feb 19 2012
next sibling parent Jim Hewes <jimhewes gmail.com> writes:
On 2/19/2012 3:40 AM, deadalnix wrote:
 Le 19/02/2012 06:09, Jim Hewes a écrit :

 Well, I think you are messing up between contract and exception. Wrong
 parameters is a contract problem, not an exceptionnal situation.

 The exemple you cited below is way more suited for an exception :
 someone unplug the USB device, then I trhow an Exception and it will
 cross all layers of my code to reach a point where it is recoverable.

 Except that contract detail, your post make perfect sense.

Then what do you do when bad parameters are passed? Do you return error codes using the function return mechanism? Do you use error codes for some errors and exceptions for others? Why? What's the strictly defined criteria for “exceptional” so that anyone can apply it to their own development and know when to use one or the other? If you use return codes for bad parameters, then does the caller always need to write error handling code to check for the bad parameter error? I did point out that internal and external bad parameters are a different case. If you're talking about an exposed API for others to use, and it's something like a C interface from a DLL where you don't want to propagate exceptions, then of course you would return an error code or something similar. But if you're writing a function that you're going to call with your own code, then I'd say asserts can be used. And if it's a library that's meant to be called by others from the outside, but by the same language, then I say you can use exceptions. Examples are the argument exceptions in the C++ Std Lib and the .NET Framework. I think the fact that these things are called exceptions, and that people then derive the word "exceptional" from that is misleading. But I know these types of things are always debated as religion and I suppose they will be for a while. :) Jim P.S. Sorry, I tend to use 'parameter' and 'argument' interchangeably and probably should not.
Feb 19 2012
prev sibling parent Jim Hewes <jimhewes gmail.com> writes:
On 2/19/2012 3:40 AM, deadalnix wrote:
 Well, I think you are messing up between contract and exception. Wrong
 parameters is a contract problem, not an exceptionnal situation.

Here is an example of what I'm getting at. Let's say you have a Dictionary class. This class has a function called Get(key) which will retrieve an item from the dictionary. But the item may not be found in the dictionary. Should you throw an exception if the item isn't found. Well, the “exceptional situations” crowed will say that since it's common for an item to not be found, this isn't an exceptional situation and you should therefore not throw an exception. An item not being in the dictionary is part of the normal program flow and you don't want exceptions firing off during normal program flow. But then what? You return false or a NotFound error code? But then do I need to check for this error every time I extract an item from the dictionary? You're losing the benefit that exceptions bring. Here is the way I'm thinking exceptions are related to contracts. I think you need to better define what the function is expected to do and not do in order to know when an exception is thrown. I can define Get(key) in two ways. I can declare that the item associated with 'key' must exist in the dictionary. Therefore if the item is not in the dictionary then the contract of the caller is not fulfilled, it's an error, and an exception is thrown. But how is the caller supposed to know an item is not in the dictionary before calling Get(key) and thus avoid exceptions? One solution is to provide a second function called ItemExists(key). You can call this before calling Get(key). ItemExists(key) is then defined to return true of false depending on whether the item is in the dictionary. It doesn't throw an exception if the key doesn't exist because it's main job is to determine if the key exists or not. Obviously this causes bad performance because the item must be looked up twice each time you retrieve it. So the second way to deal with the problem is to define the contract of Get(key) differently such that the argument 'key' is not required to exist in the dictionary. To distinguish this function, call it TryGet(key). This will return false if the item is not in the dictionary. (But it may still throw an exception for other errors such as a null argument or whatever.) So I think whether an exception is thrown depends on the function's contract. It does not have to do with errors being “exceptional” or not. Perhaps I should not even be using the term contract at all and should just say that functions need to be clearly defined. I don't know. I'm always open to learning more about exceptions to create better designs, which is partly why I jumped on this topic (and now so selfishly pulled it off topic). If anyone thinks I'm totally off base then tell me why and I might learn something. By the way, the example I was using actually exists in the .NET Framework and uses both approaches. Look at the System.Collections.Generic.Dictionary class. Instead of Get, ItemExists and TryGet, the functions are named respectively: Item (bracket syntax this[]) ContainsKey TryGetValue (see http://msdn.microsoft.com/en-us/library/s620ab8x.aspx) The KeyNotFoundException is only thrown by the Item property. So, I just put this out there as an example of when a seemingly “non-exceptional” case can use exceptions depending on the contract of the function. Jim
Feb 19 2012
prev sibling next sibling parent "Robert Jacques" <sandford jhu.edu> writes:
On Sun, 19 Feb 2012 01:07:44 -0600, Jonathan M Davis <jmdavisProg gmx.com>
wrote:

 On Sunday, February 19, 2012 00:21:38 Robert Jacques wrote:
 Not to jump on you in particular :) but bad user parameters should never be
 treated as exceptional. Even bad 'internal' parameters that are passed via
 the external API aren't exceptional. Programmers being lazy about input
 parameter checking is how hackers make their money.

I completely disagree. I think that there are many cases where throwing an exception on bad parameters is exactly what you should do. Phobos does this in many places. Often, the function knows best what it wants, and it's the best position to check it. So, having it check the input makes a _lot_ of sense in many cases. For instance, take fromISOExtString on std.datetime.SysTime. Is it at all reasonably to expect that the caller is going to verify that the string is well-formed according to the ISO standard? No. And it's highly probable tha the string being given came from I/O of some kind - be it a file or a socket or whatever. So, it makes _way_ more sense for fromISOExtString to check the string itself and throw on failure. It's also more efficient to do that, because if you checked that the string was valid before passing it to fromISOExtString, then you'd be processing the string twice. There are other cases where you want to use DbC and require that a function is given valid arguments. In those cases, you use assertions, so the checks go away in release mode. But that's not always the best way to go about. And in defensive programming, you end up checking exceptions quite heavily - even in release mode - and throwing when they're bad. The best approach depends on what you're doing, and what the functions are used for. But approaches have their place. - Jonathan M Davis

Most of Phobos is an internal library. I think parsing routines are a bad counter-example to my point; combined validation and conversion is generally part of their mandate and we expect them to fail often. We also don't expect the parsing of user input to live deep inside code base; it's almost always done as close to the input as possible (i.e. in the input text box). All too often you end up with code like: try{ parse(...); } catch {...} Also, although less of a problem in D, not ensuring that in input string actually is a string was the source of many a C exploits.
Feb 19 2012
prev sibling next sibling parent Jim Hewes <jimhewes gmail.com> writes:
On 2/18/2012 10:21 PM, Robert Jacques wrote:
 Not to jump on you in particular :) but bad user parameters should never
 be treated as exceptional. Even bad 'internal' parameters that are
 passed via the external API aren't exceptional. Programmers being lazy
 about input parameter checking is how hackers make their money.

 [snip]

I imagine that you would disagree with the design of the .NET Framework then, which uses ArgumentException all over. http://msdn.microsoft.com/en-us/library/system.argumentexception.aspx There's also a similar exception defined in the C++ Std Lib. So what do you do when a bad parameter is passed? Return an error code in a return value? So you use both exception handling and return codes? How about bad parameters to constructors (in C++)? You may say that bad parameters are not “exceptional”. OK. But my whole point of that paragraph there was that if you're going to declare one particular error is “exceptional” and another is not in a seemingly arbitrary way, then you need to tell us, for all errors, which are “exceptional” and which are not. You need a more strict definition of “exceptional” so that people know when to use an exception and when not to. Then in the cases they are not supposed to use an exception, what do they do?
 Yes, the USB stack has a high-level layer that can recover from a
 connection loss, but the rest of the protocol stack above and below it
 can't understand it and presumably ignore it. And you have just
 introduced a fairly long range dependency between the lowest level of
 your code and middle layer. (To say nothing of the implicit dependences
 implied with the intervening layers). This might be the best/only
 solution to the problem, but it also makes the code base larger and more
 fragile. I wasn't speaking in absolutes, but in relative difficulties.

I guess I don't understand what you're saying here. It sounds like you're arguing against exceptions in general, which I'm sure you're not. My stack *does* know what a connection loss is at any level. Just like it would know what an out-of-memory error is at any level. It just doesn't want to deal with it everywhere. By not handling this exception in the intermediate layers and rather just making them exception safe, the code is smaller not larger. Even at the topmost level it's known what a connection loss is, and the corrective action is to close the interface and reopen when the device is plugged in again.
 Traditional error codes are enums and so what your describe as
 BadParameter is an exception wrapping an error code. I really hope no
 one has been proposing to map error code values to exceptions on a 1:1
 basis.

Maybe not 1:1, but you talked about the need to have a large number of final classes. That's what I was thinking of.
 But your making many of my points; exceptions are (better,
 faster, stronger) error codes.

Yes. But not just that. Exceptions allow you to *not* write error handling code everywhere so that the normal program flow is easier to read. This is assuming you're writing exception safe code.
 My issue regarding the weak 'is a'
 relationship stems from the extra information in any given final typed
 exception being very specific to that particular exception. So its hard
 for me to see the rational between treating some of these as a group
 while not all of them: if MyGroupException is providing no more
 information then Exception, why can a function recover a
 MyGroupException and not a general Exception?

Borrowing part of the the exception hierarchy from another post in this thread... Error +--Exception +--GetOptException (bad name, I prefer CommandLineException) +--FileException +--IOException (OK, I added this branch, just to show the idea) +--ReadErrorException +--WriteErrorException A ReadErrorException 'is a ' IOException which 'is a' Exception.At some point or level in the code, I may want to handle IOExceptions because I know what to do with those, but not FileExceptions because I don't know what to do with those. If I were to catch Exception, then I would be responsible for re-throwing FileException and any other non-IOException, which I don't want to have to do all over. I think a class hierarchy for exceptions is justified. As an analogue, say that you have a GUI class hierarchy where you have a Window class and various kinds of derived window subclasses. You will probably write a function at some point that takes a WindowPtr as a parameter and works on any type of window (but not any class in the whole library). A catch() would work on a similar idea as the function handling Windows, catching all IOExceptions but not any and all exceptions in the hierarchy. I think it is about having a balance between the number of different exception types and the attributes or error codes you put into them. You may decide that there are too many different types derived from IOException and you always end up catching them all anyway. So you can get rid of the ReadErrorException and WriteErrorException and instead just make these as error codes you can extract from the IOException class. But still, IOException is different from Exception and FileException. And I'm assuming that MyGroupException would be different from Exception in a similar way. Maybe it's not a perfect solution, but what is better? Jim
Feb 19 2012
prev sibling parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 02/18/2012 09:09 PM, Jim Hewes wrote:

 I think of exception handling as tied to contract programming.

I think your use of the word 'contract' is colliding with the contract programming feature. What you describe later does not match with the contract programming and I guess is the reason why Andrei is pointing out two chapters from TDPL. I will reread those chapters later today but I think Andrei is referring to the distinction between assert() and std.exception.enforce().
 A
 function has a specific job that it's supposed to do. If for any reason
 it cannot do that job successfully, an exception should be thrown.

Agreed. That is how we have been using exceptions in C++ successfully at where I work. It makes everything simple.
 That
 can include even bad parameters

Yes, enforce() in D is great for that. I think Andrei agrees.
 (although if you have bad parameters to
 internal functions I'd think that is a design bug and could be handled
 by asserts).

Yes. Contract programming uses asserts().
 If what you mean is that exceptions should not be used to return
 information when the function is successful, I agree. But it can be used
 to return extra details about errors when a function fails.

Agreed. Again, this is how we have been using exceptions. We have the equivalents of detailed exception types that Jonathan M Davis has been mentioning. Come to think of it, less than a dozen.
 Not every
 exception coming out of a function was generated by that function. It
 may have come from several levels below that.

Of course. It is very useful.
 Maybe you can handle the
 former because it is more immediate but not the latter. So without
 exception types how would you know the difference between them? You
 could use error codes and switch on them but it defeats one of the main
 purposes of exception handling. So I think if there are exceptions
 generated from further away, it's an argument _for_ exception types
 rather than against it.

Agreed.
 For example, just have one BadParameter exception and then store
 information about which parameter is bad and why in the exception.

Agreed. I would like to add that exceptions are thrown by code that doesn't know how the exception will be useful. For that reason, especially a library function must provide as much information as possible. Going with the same examples in this thread, FileException is not a good exception to throw when the actual problem is a FilePermissionException or a FileNotFoundException. Ali
Feb 21 2012
next sibling parent Jim Hewes <jimhewes gmail.com> writes:
On 2/21/2012 2:29 PM, Ali Çehreli wrote:
 On 02/18/2012 09:09 PM, Jim Hewes wrote:

  > I think of exception handling as tied to contract programming.

 I think your use of the word 'contract' is colliding with the contract
 programming feature. What you describe later does not match with the
 contract programming and I guess is the reason why Andrei is pointing
 out two chapters from TDPL.

 I will reread those chapters later today but I think Andrei is referring
 to the distinction between assert() and std.exception.enforce().

Thanks. I assume the objection is about the bad parameters. In design by contract, a function should not be checking the input, correct? It assumes it's correct. But I was mostly thinking of the case when the functions are more of a public API and you can't trust the input. I did mention using assert for internal functions. But I guess if you are strict, you should never check input. I just shouldn't mention design by contract at all then. :)
Feb 22 2012
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Wednesday, February 22, 2012 22:33:47 Jim Hewes wrote:
 On 2/21/2012 2:29 PM, Ali =C3=87ehreli wrote:
 On 02/18/2012 09:09 PM, Jim Hewes wrote:
  > I think of exception handling as tied to contract programming.
=20
 I think your use of the word 'contract' is colliding with the contr=


 programming feature. What you describe later does not match with th=


 contract programming and I guess is the reason why Andrei is pointi=


 out two chapters from TDPL.
=20
 I will reread those chapters later today but I think Andrei is refe=


 to the distinction between assert() and std.exception.enforce().

Thanks. I assume the objection is about the bad parameters. In design=

 contract, a function should not be checking the input, correct? It
 assumes it's correct. But I was mostly thinking of the case when the
 functions are more of a public API and you can't trust the input. I d=

 mention using assert for internal functions. But I guess if you are
 strict, you should never check input. I just shouldn't mention design=

 contract at all then. :)

In DbC, you use assertions to verify arguments, because it's up to the = caller=20 to ensure that the arguments are valid - otherwise it's breaking the co= ntract.=20 So, in release mode, there are no checks, and in non-release mode, your= =20 program gets killed if the contract is violated. It's considered a bug = in the=20 code if bad arguments are passed to the function. In defensive programming, however, you use exceptions, which is much sa= fer,=20 because the function will never actually execute with bad arguments, bu= t it's=20 slower, because it's always checking, whereas assertions are compiled o= ut in=20 release mode. Also, it's not necessarily a bug when you pass an invalid= =20 argument to a function using defensive programming, because throwing an= =20 failure is part of its normal behavior (and won't be compiled out in re= lease=20 mode) and is recoverable, unlike an AssertError. Rather, the function i= s=20 merely informing the caller that it was given bad arguments and lets th= e=20 caller sort it out. DbC tends to work better with internal stuff where you control both the= caller=20 and the callee, whereas defensive programming works better with public = APIs.=20 But regardless, which is best to use depends on the situtation and what= you're=20 goals are. - Jonathan M Davis
Feb 23 2012
prev sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Thu, Feb 23, 2012 at 02:57:43AM -0800, Jonathan M Davis wrote:
[...]
 DbC tends to work better with internal stuff where you control both
 the caller and the callee, whereas defensive programming works better
 with public APIs.  But regardless, which is best to use depends on the
 situtation and what you're goals are.

The way I understand it, DbC is used for ensuring *program* correctness (ensure that program logic does not get itself into a bad state); defensive programming is for sanitizing *user input* (ensure that no matter what the user does, the program doesn't get into a bad state). That's why DbC is compiled out in release mode -- the assumption is that you have thoroughly tested your program logic and verified there are no logic problems. Input sanitizing is never compiled out, because you never know what users will do, so you always have to check. The two do somewhat overlap, of course. For example, failing to sanitize user input may eventually lead to passing invalid arguments to an internal function. T -- Only boring people get bored. -- JM
Feb 23 2012
prev sibling next sibling parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
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
Feb 18 2012
next sibling parent reply Ben Davis <entheh cantab.net> writes:
On 19/02/2012 00:48, Jonathan M Davis wrote:
 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

I can assure you they get misused in Java too. Most people write this: try { lots and lots and lots of stuff here } catch (Exception e) {} All they wanted to do was get round the compile error because they called Thread.sleep() which throws InterruptedException which you're required to catch, and they know they're never going to call Thread.interrupt(). What they end up doing is catching NullPointerException, ArrayIndexOutOfBoundsException and all the other things that really matter, and nasty bugs can go undiagnosed. Eclipse's auto-fix impoves things a little - it can generate this for you: try { ... } catch (InterruptedException e) { //For example //TODO: auto-generated catch block e.printStackTrace(); } But no one does their TODOs, and no one takes care to keep the console output short enough to be useful. You CAN write "throw new RuntimeException(e)", and you won't be required to catch that one. And then exceptions are incredibly useful, and I've even found rare cases where creating my own checked exception makes an API much safer. (I was processing code, and I had something like an 'uninline' operation that occasionally couldn't work, so the caller had to always have a backup plan in case the exception was thrown.) So it could be a good system, but no one has any idea what they're doing with it. If I had to improve the Java one, I suppose I would: - distinguish between 'bug' exceptions (e.g. null) and 'you're more likely to want to catch this' exceptions (e.g. IO). Maybe the bug ones should have been Errors, since people *usually* don't catch those (and are certainly never required to). As it stands, Errors are reserved for corrupt class files and other VM panic scenarios. - make sure the 'lazy' approach is a good one: the less you type, the fewer exceptions you catch, and the less likely you are to silence those exceptions at runtime. Probably the main problem with the exception hierarchy is that the short names tend to be the more general ones that are more dangerous to catch indiscriminately.
Feb 18 2012
next sibling parent deadalnix <deadalnix gmail.com> writes:
Le 19/02/2012 02:43, Ben Davis a écrit :
 On 19/02/2012 00:48, Jonathan M Davis wrote:
 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

I can assure you they get misused in Java too. Most people write this: try { lots and lots and lots of stuff here } catch (Exception e) {}

You raise 2 prblems of java exception system. First, RuntimeException is a subclass of Exception. Second, you have to catch all Exceptions except RuntimeException and Error. We can consider what is good in Java without keeping the bad part.
Feb 19 2012
prev sibling parent Ben Davis <entheh cantab.net> writes:
On 19/02/2012 01:54, Jonathan M Davis wrote:
 On Sunday, February 19, 2012 01:43:27 Ben Davis wrote:
 - distinguish between 'bug' exceptions (e.g. null) and 'you're more
 likely to want to catch this' exceptions (e.g. IO). Maybe the bug ones
 should have been Errors, since people *usually* don't catch those (and
 are certainly never required to). As it stands, Errors are reserved for
 corrupt class files and other VM panic scenarios.

Well, in D, you'd use exceptions derived from Error (though null pointers still result in a good ol' segfault). Java's situation is marred by the whole thing with checked exceptions. D's basic is better in that regard, I think. It's the lack of a proper exception hierarchy where we're worse.

Indeed, I'd noticed I was getting RangeErrors, not RangeExceptions :)
 - make sure the 'lazy' approach is a good one: the less you type, the
 fewer exceptions you catch, and the less likely you are to silence those
 exceptions at runtime. Probably the main problem with the exception
 hierarchy is that the short names tend to be the more general ones that
 are more dangerous to catch indiscriminately.

You mean, make the standard exception names really long and ugly, and the more specific ones short? e.g. Exception becomes something like StandardExceptionThatYouReallyShouldBeCatchingCatchTheDerivedType whereas specific exceptions are more like SpecifcEx (though that's a bit extreme)? Well, while that's a good sentiment, the very nature of more specific exceptions means that their namesj are likely to be more specific and therefore longer (especially when you have descriptive names). So, I'm not sure that that's ultimately all that reasonable, even if the basic idea is a good one.

LOL - yeah, there's a reason I didn't propose any actual names. It's not an easy problem to solve. But I felt the psychology angle might not have been covered and was worth mentioning all the same. You could name the class "AllExceptions" though. But as the insidious ones like off-by-ones are Errors (or segfaults), it's definitely a much smaller problem in D than in Java.
Feb 19 2012
prev sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/19/12 9:50 AM, so wrote:
 On Sunday, 19 February 2012 at 00:50:07 UTC, Jonathan M Davis wrote:
 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

Problem is, "no one" using exception handling correctly including language experts. There is no consensus on where they are useful or not. Neither articles nor codes help you.

Quite so. That's why slapping with the book won't help - there's no book! :o) Andrei
Feb 19 2012
prev sibling next sibling parent "Robert Jacques" <sandford jhu.edu> writes:
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.
Feb 18 2012
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Saturday, February 18, 2012 19:01:31 Robert Jacques wrote:
 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.

It's an argument against making it so that the only way to get the error information from an exception is toString. Exception handling is far more flexible when the exception's type and member variables gives you the information you need to actually handle the exception - or in the case of internationalization, generate your own error message. But ideally, users wouldn't see the result of an exception's toString regardless (especially since that includes stuff like the stack trace). Not even the msg field is really acceptable for that IMHO. It's great for debugging and maybe log messages, but not dealing with the user. To deal with the user appropriately, you need to know _what_ went wrong in a way that your program can process it and react appropriately, which strings just don't do. - Jonathan M Davis
Feb 18 2012
prev sibling next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Sat, Feb 18, 2012 at 04:28:25PM -0800, Jonathan M Davis wrote:
[...]
 C++ is a horrible example of how exceptions should be done, so if
 you're basing what you want off of that, then that makes me think that
 you should be better familiar with how other, more recent languages
 use them (though maybe you're quite familiar with how C# and/or Java
 use Exceptions, I don't know).  From using Java, I think that how it
 handles exceptions in general is _far_ superior to how they're
 frequently dealt with in C++ (though that does tend to depend on who's
 doing the developing, since you _can_ have a decent exception
 hierarchy in C++).

The basic problem with C++ exceptions is that it allows you to throw literally *anything*. Which is nice and generous, but not having a common root to all exceptions cripples the system, because there is no functionality that can be depended upon, like toString when what you want is an error message. Furthermore, not having a properly designed skeletal hierarchy to inherit from also adds to the problem. With no existing standard hierarchy to serve as a reference point, library writers just invent their own systems, most of which don't interoperate properly with each other. And thus the mess that is the C++ exception hierarchy. I certainly hope D can do better than that. T -- If Java had true garbage collection, most programs would delete themselves upon execution. -- Robert Sewell
Feb 18 2012
prev sibling next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Sun, Feb 19, 2012 at 01:10:10AM +0000, Ben Davis wrote:
[...]
 I guess this is a bit off topic, but what you probably want is
 syntactic sugar that says "declare constructors matching all super
 constructors and calling through to them" and can be used in ANY class
 hierarchy (not necessarily exceptions). For example:
 
 class Subtype : Supertype {
     super all;
 }
 
 If you want to expose just specific constructors, then there could
 also be a shorthand for "declare a constructor matching a specific
 super constructor and calling through to it" - so you don't have to
 repeat all the arguments. For example:
 
 class Subtype : Supertype {
     super();
     super(string,int);
 }
 
 That would then make it an entirely separate issue and completely
 not Exception-specific.

+1. This is definitely something not specific to Exception. Quite often, you want to create a derived class overriding just one or two members of the base class, but end up having to copy-n-paste most of the many ctors in the base class along with their elaborate arguments just so you can pass through the arguments to them. Having a way of simply saying "the ctors in this class default to the base class ctors" will solve this problem in a very nice and logical way. (I.e., you're sortof "inheriting the base class ctors", although not exactly, of course.) T -- Today's society is one of specialization: as you grow, you learn more and more about less and less. Eventually, you know everything about nothing.
Feb 18 2012
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Saturday, February 18, 2012 22:54:37 Jose Armando Garcia wrote:
 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.

There is a world of difference between having a specific exception type have an error code from an external source (e.g. a database or POSIX) and making _all_ exceptions function via error codes. And personally, I'd argue that in many cases, the correct thing to do would be to have a base class exception type which has the error code in it (e.g. FileException) and then have derived exceptions where the one you use depends on what the error code actually was. Then additional information pertaining to that specific error code could be put in the derived exception, and you can catch the exceptions by type rather than having to resort to switching on the error type in the exception. That way, you can actually take advantage of the exception hierarchy for controlling how you handle the exception, and you have additional information pertaining to what went wrong. And if you want access to the original error code, you have it. You can even just catch the base exception type and use a switch if you want to. But you don't have to. - Jonathan M Davis
Feb 18 2012
prev sibling next sibling parent Jose Armando Garcia <jsancio gmail.com> writes:
On Sat, Feb 18, 2012 at 11:19 PM, Jonathan M Davis <jmdavisProg gmx.com> wrote:
 On Saturday, February 18, 2012 22:54:37 Jose Armando Garcia wrote:
 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.

There is a world of difference between having a specific exception type have an error code from an external source (e.g. a database or POSIX) and making _all_ exceptions function via error codes. And personally, I'd argue that in many cases, the correct thing to do would be to have a base class exception type which has the error code in it (e.g. FileException) and then have derived exceptions where the one you use depends on what the error code actually was. Then additional information pertaining to that specific error code could be put in the derived exception, and you can catch the exceptions by type rather than having to resort to switching on the error type in the exception. That way, you can actually take advantage of the exception hierarchy for controlling how you handle the exception, and you have additional information pertaining to what went wrong. And if you want access to the original error code, you have it. You can even just catch the base exception type and use a switch if you want to. But you don't have to.

I think that is a good compromise. The one thing we need to solve is that for example Oracle's error code may collide with POSIX error code. This is why I suggested using namespace but there may be other solutions. Thanks, -Jose
 - Jonathan M Davis

Feb 18 2012
prev sibling next sibling parent reply "Nick Sabalausky" <a a.a> writes:
"Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message 
news:jhos0l$102t$1 digitalmars.com...
 There's a discussion that started in a pull request:

 https://github.com/alexrp/phobos/commit/4b87dcf39efeb4ddafe8fe99a0ef9a529c0dcaca

What the hell...? It says "5 notes on commit", but only 2 are shown (both by alexrp). (And yes, that's with a modern browser with JS on.)
 From experience I humbly submit that catching by type is most of the time 
 useless.

Another one for the file of "Crazy shit Andrei says" ;) From experience, I (and clearly many others here) find a sparse, flat exception hierarchy to be problematic and limiting. But even with a rich detailed exception hierarchy, those (ie, Andrei) who want to limit themselves to catching course-grained exceptions can do so, thanks to the nature of subtyping. So why are we even discussing this?
Feb 18 2012
next sibling parent reply "Zach" <mips intel.arm> writes:
On Sunday, 19 February 2012 at 01:29:40 UTC, Nick Sabalausky 
wrote:
 Another one for the file of "Crazy shit Andrei says" ;)

 From experience, I (and clearly many others here) find a 
 sparse, flat exception hierarchy to be problematic and 
 limiting. But even with a rich detailed exception hierarchy, 
 those (ie, Andrei) who want to limit themselves to catching 
 course-grained exceptions can do so, thanks to the nature of 
 subtyping. So why are we even discussing this?

How about we revisit ancient design decisions which once held true... but no longer is the case due to our "god-language" being more expressive? In my experience an "exception hierarchy" is never good enough, it suffers from the same problems as most frameworks also do... they simplify/destroy too much info of from the original error. ex why don't we throw a closure? Of course we could go crazy with mixins and CTFE,CTTI,RTTI aswell... imho the goal should not be to do as good as java, the goal is progress! Java should copy our design if anything... we could have a very rich exception structure... without the need for a hierarchy. try(name) // try extended to support full closure syntax { DIR* dir = opendir(toStringz(name)); if(dir==0 && errno==ENOTDIR) throw; // throws the entire try block as a closure }
Feb 18 2012
next sibling parent reply Sean Cavanaugh <WorksOnMyMachine gmail.com> writes:
On 2/18/2012 7:56 PM, Zach wrote:
 On Sunday, 19 February 2012 at 01:29:40 UTC, Nick Sabalausky wrote:
 Another one for the file of "Crazy shit Andrei says" ;)

 From experience, I (and clearly many others here) find a sparse, flat
 exception hierarchy to be problematic and limiting. But even with a
 rich detailed exception hierarchy, those (ie, Andrei) who want to
 limit themselves to catching course-grained exceptions can do so,
 thanks to the nature of subtyping. So why are we even discussing this?

How about we revisit ancient design decisions which once held true... but no longer is the case due to our "god-language" being more expressive? In my experience an "exception hierarchy" is never good enough, it suffers from the same problems as most frameworks also do... they simplify/destroy too much info of from the original error. ex why don't we throw a closure? Of course we could go crazy with mixins and CTFE,CTTI,RTTI aswell... imho the goal should not be to do as good as java, the goal is progress! Java should copy our design if anything... we could have a very rich exception structure... without the need for a hierarchy. try(name) // try extended to support full closure syntax { DIR* dir = opendir(toStringz(name)); if(dir==0 && errno==ENOTDIR) throw; // throws the entire try block as a closure }

My C++ experience revolves around some rather large codebases that do not have exceptions enabled at all. The more I look into seeing what it would take to start using them, all I see are some pretty huge downsides: The exact nature of the error for a lot of exceptions for like File I/O are very platform specific. Throwing these objects effectively bubbles up highly platform specific data up to the caller, which infects the caller with the need to deal with platform specific data (posix codes vs GetLastError, vs various return values from the functions that failed etc). This is not a good thing for keeping a codebase isolated from platform differences. There has been seeing a huge shift in needing to make everything I work on threaded on a fine-grained level (game simulation, graphics rendering, various physics based systems and simulations, networking etc). The way exceptions work, unwind the stack until someone 'cares', is not a good model in this environment. Ideally everything is on its own thread or exists as a job in a job queue, or exists as some kind of node in a flow based system. In these environments there is no caller on the _stack_ to unwind to. You have to package up another async message with the failure and handle it somewhere else. In many ways this is superior, as it solves the problem exceptions were created to solve : route errors to the proper code that 'cares'. In the Von Neumann model this has been made difficult by the stack itself. Thinking of exceptions as they are currently implemented in Java, C++, D, etc is automatically artificially constraining how they need to work. Exceptions as they are now exist as a way to jump up the stack some arbitrary amount (until a handler is found). The real problem that needs solving is 'route errors the most appropriate place available' with the constraint of 'keep the program in a working state'. I would prefer a system that works equally well in both kinds of environments, as we are already mixing the styles, and switching code from one to the other requires a large amount of refactoring due to the differences in error handling.
Feb 18 2012
next sibling parent "Nick Sabalausky" <a a.a> writes:
"Sean Cavanaugh" <WorksOnMyMachine gmail.com> wrote in message 
news:jhpr9t$29i$1 digitalmars.com...
 My C++ experience revolves around some rather large codebases that do not 
 have exceptions enabled at all.  The more I look into seeing what it would 
 take to start using them, all I see are some pretty huge downsides:

 The exact nature of the error for a lot of exceptions for like File I/O 
 are very platform specific.  Throwing these objects effectively bubbles up 
 highly platform specific data up to the caller, which infects the caller 
 with the need to deal with platform specific data (posix codes vs 
 GetLastError, vs various return values from the functions that failed 
 etc).  This is not a good thing for keeping a codebase isolated from 
 platform differences.

Sounds like you're just dealing with some notably bad designed exceptions. I've never come across such a problem, and I've used exeptions extensively.
Feb 18 2012
prev sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Sean Cavanaug:

 In the Von Neumann model this has been made difficult by the stack 
 itself.  Thinking of exceptions as they are currently implemented in 
 Java, C++, D, etc is automatically artificially constraining how they 
 need to work.

It's interesting to take a look at how "exceptions" are designed in Lisp: http://www.gigamonkeys.com/book/beyond-exception-handling-conditions-and-restarts.html Bye, bearophile
Feb 18 2012
parent Jacob Carlborg <doob me.com> writes:
On 2012-02-20 02:03, H. S. Teoh wrote:
 On Sat, Feb 18, 2012 at 11:09:23PM -0500, bearophile wrote:
 Sean Cavanaug:

 In the Von Neumann model this has been made difficult by the stack
 itself.  Thinking of exceptions as they are currently implemented in
 Java, C++, D, etc is automatically artificially constraining how
 they need to work.

It's interesting to take a look at how "exceptions" are designed in Lisp: http://www.gigamonkeys.com/book/beyond-exception-handling-conditions-and-restarts.html

I'm surprised nobody responded to this. I read through the article a bit, and it does present some interesting concepts that we may be able to make use of in D. Here's a brief (possibly incomplete) summary: One problem with the try-throw-catch paradigm is that whenever an exception is raised, the stack unwinds up some number of levels in the call stack. By the time it gets to the catch{} block, the context in which the problem happened is already long-gone, and there is no other recourse but to abort the operation, or try it again from scratch. There is no way to recover from the problem by, say, trying to fix it *in the context in which it happened* and then continuing with the operation. Say P calls Q, Q calls R, and R calls S. S finds a problem that prevents it from doing what R expects it to do, so it throws an exception. R doesn't know what to do, so it propagates the exception to Q. Q doesn't know what to do either, so it propagates the exception to P. By the time P gets to know about the problem, the execution context of S is long gone; the operation that Q was trying to perform has already been aborted. There's no way to recover except to repeat a potentially very expensive operation. The way Lisp handles this is by something called "conditions". I won't get into the definitions and stuff (just read the article), but the idea is this: - When D encounters a problem, it signals a "condition". - Along with the condition, it may register 0 or more "restarts", basically predefined methods of recovering from the condition. - The runtime then tries to recover from the condition by: - Checking to see if there's a handler registered for this condition. If there is, invoke the most recently registered one *in the context of the function that triggered the condition*. - If there's no handler, unwind the stack and propagate the condition to the caller. - There are two kinds of handlers: - The equivalent of a "catch": matches some subset of conditions that propagated to that point in the code. Some stack unwinding may already have taken place, so these are equivalent to catch block in D. - Pre-bound handlers: these are registered with the runtime condition handler before the condition is triggered (possibly very high up the call stack). They are invoked *in the context of the code that triggered the condition*. Their primary use is to decide which of the restarts associated with the condition should be used to recover from it. The pre-bound handlers are very interesting. They allow in-place recovery by having high-level callers to decide what to do, *without unwinding the stack*. Here's an example: LoadConfig() is a function that loads an application's configuration files, parses them, and sets up some runtime objects based on configuration file settings. LoadConfig calls a bunch of functions to accomplish what it does, among which is ParseConfig(). ParseConfig() in turn calls ParseConfigItem() for each configuration item in the config file, to set up the runtime objects associated with that item. ParseConfigItem() calls DecodeUTF() to convert the configuration file's text representation from, say, UTF-8 to dchar. So the call stack looks like this: LoadConfig ParseConfig ParseConfigItem DecodeUTF Now suppose the config file has some UTF encoding errors. This causes DecodeUTF to throw a DecodingError. ParseConfigItem can't go on, since that configuration item is mangled. So it propagates DecodingError to ParseConfig. Now, ParseConfig could simply abort, but using the idea of prebound handlers, it can actually offer two ways of recovering: (1) SkipConfigItem, to simply skip the mangled config item and process the rest of the config file as usual, or (2) ReparseConfigItem, to allow custom code to manually fix a bad config item and reprocess it. The problem is, ParseConfig doesn't know which action to take. It's too low-level to make that sort of decision. You need higher-level code, that knows what the application needs to do, to decide that. But ParseConfig can't just propagate the exception to said high-level code, because if it does, parsing of the entire config file is aborted and will have to be restarted from scratch. The solution is to have the higher-level code register a delegate with the exception system. Something like this: // NOTE: not real D code void main() { registerHandler(auto delegate(ParseError e) { if (can_repair_item(e.item)) { return e.ReparseConfigItem( repairConfigItem(e.item)); } else { return e.SkipConfigItem(); } }); ParseConfig(configfile); } Now when ParseConfig encounters a problem, it signals a ParseError object with two options for recovery: ReparseConfigItem and SkipConfigItem. It doesn't try to fix the problem on its own, but it lets the delegate from main() make that decision. The runtime exception system then sees if there's a matching handler, and calls the handler with the ParseError to determine which course of action to take. If no handler is found, or the handler decides to abort, then ParseError is propagated to the caller with stack unwinding. So ParseConfig might look something like this: // NOTE: not real D code auto ParseConfig(...) { foreach (item; config_items) { try { // Note: not real proposed syntax, this is just // to show the semantics of the mechanism: restart: auto objs = ParseConfigItem(item); SetupConfigObjects(objs); } catch(ParseConfigItemError) { // Note: not real proposed syntax, this is just // to show the semantics of the mechanism: ConfigError e; e.ReparseConfigItem = void delegate(ConfigItem fixedItem) { goto restart; }; e.SkipConfigItem = void delegate() { continue; } // This will unwind stack if no handler is // found, or handler decides to propagate // exception. handleError(e); } } } OK, so it looks real ugly right now. But if this mechanism is built into the language, we could have much better syntax, something like this: auto ParseConfig(...) { foreach (item; config_items) { try { auto objs = ParseConfigItem(item); SetupConfigObjects(objs); } recoverBy ReparseConfigItem(fixedItem) { item = fixedItem; restart; // restarts try{} block } recoverBy SkipConfigItem() { setDefaultConfigObjs(); continue; // continues foreach loop } } } This is just a rough sketch syntax, just to show the idea. It can of course be improved upon. T

I was actually thinking something similar, the part about registering exception handlers, i.e. using "registerHandler". -- /Jacob Carlborg
Feb 20 2012
prev sibling parent reply "Nick Sabalausky" <a a.a> writes:
"Zach" <mips intel.arm> wrote in message 
news:tpxbylbgpvarpzzlpuuf forum.dlang.org...
 On Sunday, 19 February 2012 at 01:29:40 UTC, Nick Sabalausky wrote:
 Another one for the file of "Crazy shit Andrei says" ;)

 From experience, I (and clearly many others here) find a sparse, flat 
 exception hierarchy to be problematic and limiting. But even with a rich 
 detailed exception hierarchy, those (ie, Andrei) who want to limit 
 themselves to catching course-grained exceptions can do so, thanks to the 
 nature of subtyping. So why are we even discussing this?

How about we revisit ancient design decisions which once held true... but no longer is the case due to our "god-language" being more expressive?

I'm fine with that when there's an alternative suggested that makes sense, or when there's an *actual* problem identified with the current way.
 In my experience an "exception hierarchy" is never good enough, it suffers 
 from the same problems as most frameworks also do... they simplify/destroy 
 too much info of from the original error.

The only problem I've ever had with them is that there's no templated catch blocks, so you can't handle two different exceptions with the same code without violating DRY or worse: catching the common base type and rethrowing when it's not what you wanted. Toss in templated catch blocks, and I've have no problem at all.
 ex why don't we throw a closure?

Haxe lets you throw literally anything. It's a useless pain in the ass that's along the same lines as VB being "flexible" enough to "allow" you to have arrays indexed from whatever random number you want. Adds no benefit, and just forces people to make sure to handle extra scenarios that aren't useful in the first place.
 Of course we could go crazy with mixins and CTFE,CTTI,RTTI aswell... imho 
 the goal should not be to do as good as java, the goal is progress! Java 
 should copy our design if anything... we could have a very rich exception 
 structure... without the need for a hierarchy.

 try(name) // try extended to support full closure syntax
 {
   DIR* dir = opendir(toStringz(name));

   if(dir==0 && errno==ENOTDIR)
     throw; // throws the entire try block as a closure
 }

What would the use of that be?
Feb 18 2012
next sibling parent reply "Daniel Murphy" <yebblies nospamgmail.com> writes:
"Nick Sabalausky" <a a.a> wrote in message 
news:jhprac$2aj$1 digitalmars.com...
 The only problem I've ever had with them is that there's no templated 
 catch blocks, so you can't handle two different exceptions with the same 
 code without violating DRY or worse: catching the common base type and 
 rethrowing when it's not what you wanted. Toss in templated catch blocks, 
 and I've have no problem at all.

Do you mean something like this? try { something(); } catch (e : ThisException, ThatException, OtherException) { static assert(is(typeof(e) == CommonType!(ThisException, ThatException, OtherException)); } catch (Exception e) { // Every other type derived from Exception } Or do you think the full power to be able to template catch blocks as if they were functions would be useful for something?
Feb 18 2012
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/19/12 12:56 AM, Jonathan M Davis wrote:
 I think that being able to have a catch block which took multiple exception
 types would be plenty. There are times when it would be very valuable to be
 able to use the same catch block for multiple exceptions without having to
 catch their base type (which would then potentially catch other exceptions
 which you didn't want to catch). So, something like that second catch block
 that you have there would be very valuable.

So now hierarchies are not good? Andrei
Feb 19 2012
next sibling parent "Daniel Murphy" <yebblies nospamgmail.com> writes:
The issue here isn't deep vs. flat hierarchy (like the rest of the thread), 
it's about improving code clarity/dry.

This is much like the 'case A, B:' syntax (or fallthrough/goto case) in 
switches.

Even with a flat hierarchy, you might still want to handle a set of 
exceptions deriving from Exception the same way, but don't want to catch 
everything derived from Exception.

"Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message 
news:jhqaf2$tfk$2 digitalmars.com...
 On 2/19/12 12:56 AM, Jonathan M Davis wrote:
 I think that being able to have a catch block which took multiple 
 exception
 types would be plenty. There are times when it would be very valuable to 
 be
 able to use the same catch block for multiple exceptions without having 
 to
 catch their base type (which would then potentially catch other 
 exceptions
 which you didn't want to catch). So, something like that second catch 
 block
 that you have there would be very valuable.

So now hierarchies are not good? Andrei

Feb 19 2012
prev sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/19/12 2:49 AM, H. S. Teoh wrote:
[snip]
 To me, this is a giant hint that OOP is a good solution. You want a
 class hierarchy rooted at Exception. And groups of related exceptions
 probably should be rooted under their respective base classes under
 Exception.

 If you have a better way to solve this, I'm waiting to hear it.

I don't. This thread is predicated on the assumption that the current approaches to exception handling are wanting, so it's time to look outside the box. It's also reasonable to assume that people involved in this thread do have an understanding of current mechanisms, so rehashes thereof are unnecessary. Andrei
Feb 19 2012
prev sibling next sibling parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday, February 19, 2012 16:59:49 Daniel Murphy wrote:
 "Nick Sabalausky" <a a.a> wrote in message
 news:jhprac$2aj$1 digitalmars.com...
 
 The only problem I've ever had with them is that there's no templated
 catch blocks, so you can't handle two different exceptions with the same
 code without violating DRY or worse: catching the common base type and
 rethrowing when it's not what you wanted. Toss in templated catch blocks,
 and I've have no problem at all.

Do you mean something like this? try { something(); } catch (e : ThisException, ThatException, OtherException) { static assert(is(typeof(e) == CommonType!(ThisException, ThatException, OtherException)); } catch (Exception e) { // Every other type derived from Exception } Or do you think the full power to be able to template catch blocks as if they were functions would be useful for something?

I think that being able to have a catch block which took multiple exception types would be plenty. There are times when it would be very valuable to be able to use the same catch block for multiple exceptions without having to catch their base type (which would then potentially catch other exceptions which you didn't want to catch). So, something like that second catch block that you have there would be very valuable. - Jonathan M Davis
Feb 18 2012
parent reply "Daniel Murphy" <yebblies nospamgmail.com> writes:
"Jonathan M Davis" <jmdavisProg gmx.com> wrote in message 
news:mailman.582.1329634661.20196.digitalmars-d puremagic.com...
 On Sunday, February 19, 2012 16:59:49 Daniel Murphy wrote:
 "Nick Sabalausky" <a a.a> wrote in message
 news:jhprac$2aj$1 digitalmars.com...

 The only problem I've ever had with them is that there's no templated
 catch blocks, so you can't handle two different exceptions with the 
 same
 code without violating DRY or worse: catching the common base type and
 rethrowing when it's not what you wanted. Toss in templated catch 
 blocks,
 and I've have no problem at all.

Do you mean something like this? try { something(); } catch (e : ThisException, ThatException, OtherException) { static assert(is(typeof(e) == CommonType!(ThisException, ThatException, OtherException)); } catch (Exception e) { // Every other type derived from Exception } Or do you think the full power to be able to template catch blocks as if they were functions would be useful for something?

I think that being able to have a catch block which took multiple exception types would be plenty. There are times when it would be very valuable to be able to use the same catch block for multiple exceptions without having to catch their base type (which would then potentially catch other exceptions which you didn't want to catch). So, something like that second catch block that you have there would be very valuable. - Jonathan M Davis

I assume you mean the _first_ catch block? By the looks of it java 7 has this with an awful syntax. Maybe we should introduce implicit 'catch fallthrough': void fun() { try { something; } catch (ExceptionA) {} // falls through implicitly catch (ExpcetionB) { // handle both A and B break; } catch (ExceptionC) { // handle exception C break; } }
Feb 18 2012
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 02/19/2012 09:26 AM, H. S. Teoh wrote:
 On Sat, Feb 18, 2012 at 11:52:00PM -0800, Jonathan M Davis wrote:
 [...]
 So, while at first glance, it seems like a good idea, I think that it
 has too many issues as-is to work. It might be possible to adjust the
 idea to make it workable though. Right now, it's possible to do it via
 mixins or calling a function inside the catch, but doing something
 similar to this would certainly be nice, assuming that we could sort
 out the kinks.

I have an idea. What about "signature constraints" for catch, ala template signature constraints? Something like this: try { ... } catch(IOException e) if (e.errno in subsetYouWantToHandle) { ... } Just using IOException as an example. The idea is to allow arbitrary expressions in the constraint so whatever doesn't satisfy the constraint will be regarded as "not caught", even if the base type matches. T

Nice.
Feb 19 2012
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/19/12 7:31 AM, Timon Gehr wrote:
 On 02/19/2012 09:26 AM, H. S. Teoh wrote:
 On Sat, Feb 18, 2012 at 11:52:00PM -0800, Jonathan M Davis wrote:
 [...]
 So, while at first glance, it seems like a good idea, I think that it
 has too many issues as-is to work. It might be possible to adjust the
 idea to make it workable though. Right now, it's possible to do it via
 mixins or calling a function inside the catch, but doing something
 similar to this would certainly be nice, assuming that we could sort
 out the kinks.

I have an idea. What about "signature constraints" for catch, ala template signature constraints? Something like this: try { ... } catch(IOException e) if (e.errno in subsetYouWantToHandle) { ... } Just using IOException as an example. The idea is to allow arbitrary expressions in the constraint so whatever doesn't satisfy the constraint will be regarded as "not caught", even if the base type matches. T

Nice.

That helps. This quite nicely illustrates that types don't necessarily need to proliferate. Something not much more constraining can be done today: try { ... } catch(IOException e) { if (e.errno !in subsetYouWantToHandle) throw e; ... } Andrei
Feb 19 2012
next sibling parent reply =?ISO-8859-1?Q?Alex_R=F8nne_Petersen?= <xtzgzorex gmail.com> writes:
On 19-02-2012 15:41, Andrei Alexandrescu wrote:
 On 2/19/12 7:31 AM, Timon Gehr wrote:
 On 02/19/2012 09:26 AM, H. S. Teoh wrote:
 On Sat, Feb 18, 2012 at 11:52:00PM -0800, Jonathan M Davis wrote:
 [...]
 So, while at first glance, it seems like a good idea, I think that it
 has too many issues as-is to work. It might be possible to adjust the
 idea to make it workable though. Right now, it's possible to do it via
 mixins or calling a function inside the catch, but doing something
 similar to this would certainly be nice, assuming that we could sort
 out the kinks.

I have an idea. What about "signature constraints" for catch, ala template signature constraints? Something like this: try { ... } catch(IOException e) if (e.errno in subsetYouWantToHandle) { ... } Just using IOException as an example. The idea is to allow arbitrary expressions in the constraint so whatever doesn't satisfy the constraint will be regarded as "not caught", even if the base type matches. T

Nice.

That helps. This quite nicely illustrates that types don't necessarily need to proliferate. Something not much more constraining can be done today: try { ... } catch(IOException e) { if (e.errno !in subsetYouWantToHandle) throw e; ... } Andrei

As I pointed out on the pull request, this is *evil*. It resets the stack trace. -- - Alex
Feb 19 2012
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/19/12 8:44 AM, Alex Rnne Petersen wrote:
 On 19-02-2012 15:41, Andrei Alexandrescu wrote:
 That helps. This quite nicely illustrates that types don't necessarily
 need to proliferate. Something not much more constraining can be done
 today:

 try {
 ...
 } catch(IOException e)
 {
 if (e.errno !in subsetYouWantToHandle) throw e;
 ...
 }


 Andrei

As I pointed out on the pull request, this is *evil*. It resets the stack trace.

I understand, that's a good point. Could the matter be considered an implementation issue? Andrei
Feb 19 2012
parent =?ISO-8859-1?Q?Alex_R=F8nne_Petersen?= <xtzgzorex gmail.com> writes:
On 19-02-2012 15:46, Andrei Alexandrescu wrote:
 On 2/19/12 8:44 AM, Alex Rnne Petersen wrote:
 On 19-02-2012 15:41, Andrei Alexandrescu wrote:
 That helps. This quite nicely illustrates that types don't necessarily
 need to proliferate. Something not much more constraining can be done
 today:

 try {
 ...
 } catch(IOException e)
 {
 if (e.errno !in subsetYouWantToHandle) throw e;
 ...
 }


 Andrei

As I pointed out on the pull request, this is *evil*. It resets the stack trace.

I understand, that's a good point. Could the matter be considered an implementation issue? Andrei

Unfortunately, I don't think so. As far as I am aware, there is no good way to solve the problem; you can't know whether the user intended to rethrow (and thus continue the stack trace) as opposed to doing a clean throw. You could say that if the throw statement is "throw new T();", then a new stack trace is created, and in all other cases, the stack trace is continued, however, this doesn't work universally (think: creating and returning an exception object in a function and then throwing it). -- - Alex
Feb 19 2012
prev sibling parent =?ISO-8859-1?Q?Alex_R=F8nne_Petersen?= <xtzgzorex gmail.com> writes:
On 19-02-2012 16:00, Jose Armando Garcia wrote:
 On Sun, Feb 19, 2012 at 12:44 PM, Alex Rnne Petersen
 <xtzgzorex gmail.com>  wrote:
 On 19-02-2012 15:41, Andrei Alexandrescu wrote:
 On 2/19/12 7:31 AM, Timon Gehr wrote:
 On 02/19/2012 09:26 AM, H. S. Teoh wrote:
 On Sat, Feb 18, 2012 at 11:52:00PM -0800, Jonathan M Davis wrote:
 [...]
 So, while at first glance, it seems like a good idea, I think that it
 has too many issues as-is to work. It might be possible to adjust the
 idea to make it workable though. Right now, it's possible to do it via
 mixins or calling a function inside the catch, but doing something
 similar to this would certainly be nice, assuming that we could sort
 out the kinks.

[...] I have an idea. What about "signature constraints" for catch, ala template signature constraints? Something like this: try { ... } catch(IOException e) if (e.errno in subsetYouWantToHandle) { ... } Just using IOException as an example. The idea is to allow arbitrary expressions in the constraint so whatever doesn't satisfy the constraint will be regarded as "not caught", even if the base type matches. T

Nice.

That helps. This quite nicely illustrates that types don't necessarily need to proliferate. Something not much more constraining can be done today: try { ... } catch(IOException e) { if (e.errno !in subsetYouWantToHandle) throw e; ... } Andrei

As I pointed out on the pull request, this is *evil*. It resets the stack trace.

What? Is there a technical reason why throw resets the stack? Java doesn't work this way. In java the stack is created when the object Throwable is created: "A throwable contains a snapshot of the execution stack of its thread at the time it was created. It can also contain a message string that gives more information about the error. Finally, it can contain a cause: another throwable that caused this throwable to get thrown. The cause facility is new in release 1.4. It is also known as the chained exception facility, as the cause can, itself, have a cause, and so on, leading to a "chain" of exceptions, each caused by another. " We should consider changing this to work more like Java. This allows for patterns like: // Log the stack but don't throw auto e = new Exception(); writefln(e.stack); Thanks, -Jose
 --
 - Alex


If Java really works that way, I'm sorry, but that just makes me think even worse of the language/VM. If you create an exception object in some utility function (or chain of such), you don't want those in your stack trace. You want the stack trace to start from where you throw the object, not where you created it. Anything else is just confusing (and perhaps explains the several hundred lines long stack traces Java programs sometimes spit out...). -- - Alex
Feb 19 2012
prev sibling parent deadalnix <deadalnix gmail.com> writes:
Le 19/02/2012 15:41, Andrei Alexandrescu a crit :
 On 2/19/12 7:31 AM, Timon Gehr wrote:
 On 02/19/2012 09:26 AM, H. S. Teoh wrote:
 On Sat, Feb 18, 2012 at 11:52:00PM -0800, Jonathan M Davis wrote:
 [...]
 So, while at first glance, it seems like a good idea, I think that it
 has too many issues as-is to work. It might be possible to adjust the
 idea to make it workable though. Right now, it's possible to do it via
 mixins or calling a function inside the catch, but doing something
 similar to this would certainly be nice, assuming that we could sort
 out the kinks.

I have an idea. What about "signature constraints" for catch, ala template signature constraints? Something like this: try { ... } catch(IOException e) if (e.errno in subsetYouWantToHandle) { ... } Just using IOException as an example. The idea is to allow arbitrary expressions in the constraint so whatever doesn't satisfy the constraint will be regarded as "not caught", even if the base type matches. T

Nice.

That helps. This quite nicely illustrates that types don't necessarily need to proliferate. Something not much more constraining can be done today: try { ... } catch(IOException e) { if (e.errno !in subsetYouWantToHandle) throw e; ... } Andrei

This wouldn't work because you'll erase teh stack trace. An alternative is to create a new exception and chain with e. You discussed that in TDPL already. But It is worse than creating new types of Exception IMO, because now, you ends up creating 2 Exceptions. And you introduce the risk of someone silenting Exception, something that you don't want !
Feb 19 2012
prev sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Sun, Feb 19, 2012 at 02:04:50AM -0600, Andrei Alexandrescu wrote:
 On 2/19/12 12:56 AM, Jonathan M Davis wrote:
I think that being able to have a catch block which took multiple
exception types would be plenty. There are times when it would be
very valuable to be able to use the same catch block for multiple
exceptions without having to catch their base type (which would then
potentially catch other exceptions which you didn't want to catch).
So, something like that second catch block that you have there would
be very valuable.

So now hierarchies are not good?

It's not a simple black-or-white decision we're dealing with here. :) Although exception hierarchies are currently the best we have, it's not perfect, and it does have its own flaws. If we can figure out a better system that fixes those flaws without compromising the good points of using a class hierarchy, that would be even better. The core of the problem is this: when an exception happens, it's within a specific context with the associated domain-specific data. This context and domain-specific data can be totally disparate types. For example, a FileNotFound exception occurs in the filesystem, and is associated with a filename. A SeekError also occurs in the filesystem, is associated with a file (not necessarily equivalent to a filename), and a seek offset. By contrast, a NetworkTimeout error happens in the network stack, is associated with a particular protocol (say TCP), a particular network socket (with its own associated IP address, etc.), and a particular timeout value. It has no associated filename, or seek offset. So you have a bunch of exceptions, each of which is associated with a bunch of info that, in general, have no relation with any info associated with another exception. The problem at hand is: how do you represent this info in a useful way? Generally speaking, the only common thing between all exceptions is the fact that they are, well, exceptions. And some of them are more closely related to each other than others -- file-related exceptions may, for example, share the fact that they are tied to a specific file. To me, this is a giant hint that OOP is a good solution. You want a class hierarchy rooted at Exception. And groups of related exceptions probably should be rooted under their respective base classes under Exception. If you have a better way to solve this, I'm waiting to hear it. T -- Gone Chopin. Bach in a minuet.
Feb 19 2012
prev sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Sat, Feb 18, 2012 at 11:09:23PM -0500, bearophile wrote:
 Sean Cavanaug:
 
 In the Von Neumann model this has been made difficult by the stack
 itself.  Thinking of exceptions as they are currently implemented in
 Java, C++, D, etc is automatically artificially constraining how
 they need to work.

It's interesting to take a look at how "exceptions" are designed in Lisp: http://www.gigamonkeys.com/book/beyond-exception-handling-conditions-and-restarts.html

I'm surprised nobody responded to this. I read through the article a bit, and it does present some interesting concepts that we may be able to make use of in D. Here's a brief (possibly incomplete) summary: One problem with the try-throw-catch paradigm is that whenever an exception is raised, the stack unwinds up some number of levels in the call stack. By the time it gets to the catch{} block, the context in which the problem happened is already long-gone, and there is no other recourse but to abort the operation, or try it again from scratch. There is no way to recover from the problem by, say, trying to fix it *in the context in which it happened* and then continuing with the operation. Say P calls Q, Q calls R, and R calls S. S finds a problem that prevents it from doing what R expects it to do, so it throws an exception. R doesn't know what to do, so it propagates the exception to Q. Q doesn't know what to do either, so it propagates the exception to P. By the time P gets to know about the problem, the execution context of S is long gone; the operation that Q was trying to perform has already been aborted. There's no way to recover except to repeat a potentially very expensive operation. The way Lisp handles this is by something called "conditions". I won't get into the definitions and stuff (just read the article), but the idea is this: - When D encounters a problem, it signals a "condition". - Along with the condition, it may register 0 or more "restarts", basically predefined methods of recovering from the condition. - The runtime then tries to recover from the condition by: - Checking to see if there's a handler registered for this condition. If there is, invoke the most recently registered one *in the context of the function that triggered the condition*. - If there's no handler, unwind the stack and propagate the condition to the caller. - There are two kinds of handlers: - The equivalent of a "catch": matches some subset of conditions that propagated to that point in the code. Some stack unwinding may already have taken place, so these are equivalent to catch block in D. - Pre-bound handlers: these are registered with the runtime condition handler before the condition is triggered (possibly very high up the call stack). They are invoked *in the context of the code that triggered the condition*. Their primary use is to decide which of the restarts associated with the condition should be used to recover from it. The pre-bound handlers are very interesting. They allow in-place recovery by having high-level callers to decide what to do, *without unwinding the stack*. Here's an example: LoadConfig() is a function that loads an application's configuration files, parses them, and sets up some runtime objects based on configuration file settings. LoadConfig calls a bunch of functions to accomplish what it does, among which is ParseConfig(). ParseConfig() in turn calls ParseConfigItem() for each configuration item in the config file, to set up the runtime objects associated with that item. ParseConfigItem() calls DecodeUTF() to convert the configuration file's text representation from, say, UTF-8 to dchar. So the call stack looks like this: LoadConfig ParseConfig ParseConfigItem DecodeUTF Now suppose the config file has some UTF encoding errors. This causes DecodeUTF to throw a DecodingError. ParseConfigItem can't go on, since that configuration item is mangled. So it propagates DecodingError to ParseConfig. Now, ParseConfig could simply abort, but using the idea of prebound handlers, it can actually offer two ways of recovering: (1) SkipConfigItem, to simply skip the mangled config item and process the rest of the config file as usual, or (2) ReparseConfigItem, to allow custom code to manually fix a bad config item and reprocess it. The problem is, ParseConfig doesn't know which action to take. It's too low-level to make that sort of decision. You need higher-level code, that knows what the application needs to do, to decide that. But ParseConfig can't just propagate the exception to said high-level code, because if it does, parsing of the entire config file is aborted and will have to be restarted from scratch. The solution is to have the higher-level code register a delegate with the exception system. Something like this: // NOTE: not real D code void main() { registerHandler(auto delegate(ParseError e) { if (can_repair_item(e.item)) { return e.ReparseConfigItem( repairConfigItem(e.item)); } else { return e.SkipConfigItem(); } }); ParseConfig(configfile); } Now when ParseConfig encounters a problem, it signals a ParseError object with two options for recovery: ReparseConfigItem and SkipConfigItem. It doesn't try to fix the problem on its own, but it lets the delegate from main() make that decision. The runtime exception system then sees if there's a matching handler, and calls the handler with the ParseError to determine which course of action to take. If no handler is found, or the handler decides to abort, then ParseError is propagated to the caller with stack unwinding. So ParseConfig might look something like this: // NOTE: not real D code auto ParseConfig(...) { foreach (item; config_items) { try { // Note: not real proposed syntax, this is just // to show the semantics of the mechanism: restart: auto objs = ParseConfigItem(item); SetupConfigObjects(objs); } catch(ParseConfigItemError) { // Note: not real proposed syntax, this is just // to show the semantics of the mechanism: ConfigError e; e.ReparseConfigItem = void delegate(ConfigItem fixedItem) { goto restart; }; e.SkipConfigItem = void delegate() { continue; } // This will unwind stack if no handler is // found, or handler decides to propagate // exception. handleError(e); } } } OK, so it looks real ugly right now. But if this mechanism is built into the language, we could have much better syntax, something like this: auto ParseConfig(...) { foreach (item; config_items) { try { auto objs = ParseConfigItem(item); SetupConfigObjects(objs); } recoverBy ReparseConfigItem(fixedItem) { item = fixedItem; restart; // restarts try{} block } recoverBy SkipConfigItem() { setDefaultConfigObjs(); continue; // continues foreach loop } } } This is just a rough sketch syntax, just to show the idea. It can of course be improved upon. T -- Nobody is perfect. I am Nobody. -- pepoluan, GKC forum
Feb 19 2012
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Saturday, February 18, 2012 23:26:35 Jose Armando Garcia wrote:
 I think that is a good compromise. The one thing we need to solve is
 that for example Oracle's error code may collide with POSIX error
 code. This is why I suggested using namespace but there may be other
 solutions.

What relation would POSIX and Oracle codes have though? I'd expect them to be on completely different exception types. POSIX error codes would make sense on something like FileException, whereas Oracle codes would make sense on something like DatabaseException or OracleException. The problem you run into is when you also have PostgresException and SQLLiteException which derive from DatabaseException. You could have an error code in it, but it wouldn't necessarily mean much if it weren't tied to the specific database exception type. The hierarchy that can be ignored is when OracleException or PostgresException have derived types based on the error code (which then hopefully provide additional information beyond just the error code). But you still end up with a minimal exception hierarchy that everyone has to use if they want to stick to error codes. People can use FileException and its error code rather than FileNotFoundException and NotFileException, but they'd still have to use FileException rather than Exception to get the error code. - Jonathan M Davis
Feb 18 2012
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday, February 19, 2012 01:43:27 Ben Davis wrote:
 I can assure you they get misused in Java too. Most people write this:

Oh yes. It's not like the fact that their exception hierarchy is good means that people don't misuse it. Far too many people try and ignore errors in any language. And a solid exception hierarchy does nothing to stop people from being stupid and simply catching Exception to ignore. But if you have a good exception hierarchy, then you _can_ properly handle errors if you want to. My point was that because C++ doesn't have a standard exception hierarchy (even worse, they let you throw literally anything), it's much rarer to see one in C++ programs, let alone a good one. So, it's something that many C++ programmers have not been exposed to. Java programmers do not have that excuse. So, it's arguably that much worse when they misuse exceptions.
 - distinguish between 'bug' exceptions (e.g. null) and 'you're more
 likely to want to catch this' exceptions (e.g. IO). Maybe the bug ones
 should have been Errors, since people *usually* don't catch those (and
 are certainly never required to). As it stands, Errors are reserved for
 corrupt class files and other VM panic scenarios.

Well, in D, you'd use exceptions derived from Error (though null pointers still result in a good ol' segfault). Java's situation is marred by the whole thing with checked exceptions. D's basic is better in that regard, I think. It's the lack of a proper exception hierarchy where we're worse.
 - make sure the 'lazy' approach is a good one: the less you type, the
 fewer exceptions you catch, and the less likely you are to silence those
 exceptions at runtime. Probably the main problem with the exception
 hierarchy is that the short names tend to be the more general ones that
 are more dangerous to catch indiscriminately.

You mean, make the standard exception names really long and ugly, and the more specific ones short? e.g. Exception becomes something like StandardExceptionThatYouReallyShouldBeCatchingCatchTheDerivedType whereas specific exceptions are more like SpecifcEx (though that's a bit extreme)? Well, while that's a good sentiment, the very nature of more specific exceptions means that their namesj are likely to be more specific and therefore longer (especially when you have descriptive names). So, I'm not sure that that's ultimately all that reasonable, even if the basic idea is a good one. - Jonathan M Davis
Feb 18 2012
prev sibling next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Sat, Feb 18, 2012 at 05:47:58PM -0600, Andrei Alexandrescu wrote:
 On 2/18/12 1:41 PM, H. S. Teoh wrote:

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.

It's great that you bring expertise from the Java world.

You flatter me, but I should make it clear that I'm no Java expert. I haven't used it in a significant way for years. But I do know enough about it to know, at least in principle, the merits of its exception class hierarchy.
 I should note that the above does little in the way of putting an
 argument to the table. It appeals to subjective qualifications
 ("poorly designed", "well designed") so one's only recourse to getting
 convinced is just believing what you say without any evidence. It's
 equally unimpressive that FileNotFoundException and DiskFullException
 inherit IOException; seeing that one might say "yeah, sure" but there
 should be some compelling evidence that the distinction makes a
 difference.

I wanted to talk about the principles of using an exception hierarchy, not get lost in the details of it, that's why I didn't go into the details of how exactly the hierarchy should be designed. As for this particular case, I was only trying to illustrate the point with a concrete example; I wasn't suggesting that we must implement those exact three exception classes. Nevertheless, I would say the decision to make FileNotFoundException and DiskFullException inherit from IOException can be based on: 1) The fact that they are based on a common set of OS primitives, i.e., the filesystem, thereby providing a logical set of related exceptions; 2) It makes sense to put errno in IOException, whereas it doesn't make sense to put errno in Exception, since not all Exception's have any relation to errno. 3) From a more abstract POV, it makes sense to group file-related exceptions under a common root, command-line parsing exceptions under another root, numerical exceptions under yet another root, etc.. And how do we decide which exception belongs where? By checking whether an application might want to catch all exceptions in a group as a whole, e.g., a numerical app wants to handle any numerical errors, but don't care about I/O errors (which should just propagate). So it wouldn't make sense to put, say, ArithmeticOverflowException under 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.

From this and other posts I'd say we need to design the base exception classes better, for example by defining an overridable property isTransient that tells caller code whether retrying might help.

Just because an exception is transient doesn't mean it makes sense to try again. For example, saveFileMenu() might read a filename from the user, then save the data to a file. If the user types an invalid filename, you will get an InvalidFilename exception. From an abstract point of view, an invalid filename is not a transient problem: retrying the invalid filename won't make the problem go away. But the application in this case *wants* to repeat the operation by asking the user for a *different* filename. On the other hand, if the same exception happens in an app that's trying to read a configuration file, then it *shouldn't* retry the operation. The bottomline is, you can't expect a couple of properties in the base class (most generic level) will be helpful in all cases. In some cases, you *want* to know exactly what the exception was programmatically, so that the program knows how to react properly. [...]
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.

Yes. Types, to be more general. The question is why are various error deserving of their own types.

I have to say, I'm not wedded to the idea of Exception class hierarchies, but currently it's the best choice on the table. Basically you *need* some kind of polymorphic object to represent an exception, because errors come in all shapes and forms, and it's simply not good enough to have what amounts to a binary system (fatal uncatchable exception and non-fatal exception that you may catch and attempt to continue if you dare). Whether it's a class, a template, or whatever, doesn't really matter in the end, but you do want to have: 1) Genericity: if I don't care what error it is, just that an error happened, I should be able to catch exceptions in general and deal with them. 2) Specificity: if I have a very precise error (or a very precise set of errors) that I can handle, I want to be able to catch those, and only those, errors, and deal with them. The ones I can't handle, I want to just propagate. 3) Programmatic information: the fact that I want to handle a certain set of errors means that I know how to deal with them, provided I have enough information about them. Which means the exceptions I catch need to come with domain-specific information (such as errno for OS-related exceptions, or Oracle error numbers for the Oracle API library, etc.). Having toString() as the only way to extract information out of the exception is not good enough for recovering from errors programmatically. (And obviously sticking errno, Oracle errors, etc., into a giant union in Exception isn't the right solution either.) Currently, an elaborated exception class hierarchy, for all its warts and the objections against it, best fits the bill. If you have a better way of doing this, I'm all ears. But the current "fatal"/"nonfatal" dichotomy is simply too coarse to be of use in large applications. T -- Food and laptops don't mix.
Feb 18 2012
prev sibling next sibling parent "Robert Jacques" <sandford jhu.edu> writes:
On Sat, 18 Feb 2012 19:13:02 -0600, Jonathan M Davis <jmdavisProg gmx.com>
wrote:

 On Saturday, February 18, 2012 19:01:31 Robert Jacques wrote:
 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.

It's an argument against making it so that the only way to get the error information from an exception is toString. Exception handling is far more flexible when the exception's type and member variables gives you the information you need to actually handle the exception - or in the case of internationalization, generate your own error message. But ideally, users wouldn't see the result of an exception's toString regardless (especially since that includes stuff like the stack trace). Not even the msg field is really acceptable for that IMHO. It's great for debugging and maybe log messages, but not dealing with the user. To deal with the user appropriately, you need to know _what_ went wrong in a way that your program can process it and react appropriately, which strings just don't do. - Jonathan M Davis

But you _always_ know what went wrong: An unexpected error occurred while trying to do X, where X is whatever is inside the try-catch block. Exceptions are for exceptional situations and not supposed to be used as a form of out of band information return (or at least that's what every programming book tells me). I see typed exceptions as having a strong coupling problem; exception handling that is aware enough to handle a typed exception, is so close to the exception generation code that most of the information conveyed by the 'type' is also conveyed by the context. Now, the further away exception handling gets from exception generation, the less information context coveys, but the ability of the handler to do anything smarter than log the exception and retry / cancel becomes less and less. The other issue I see with typed exceptions is that the classic 'is a' relationship seems weak; to be informative and useful and exception must map to a specific or small set of error/unexpected conditions. However, this specificity naturally leads to large number of final classes, which isn't really leveraging OO principals. It's hard to see what, if any, benefit the intermediate base classes give over a generic exception.
Feb 18 2012
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 2/19/12, Ben Davis <entheh cantab.net> wrote:
 That would then make it an entirely separate issue and completely not
 Exception-specific.

Yes I meant in the general case not just Exception. Exception is just a regular class.
Feb 18 2012
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Saturday, February 18, 2012 19:59:52 Robert Jacques wrote:
 But you _always_ know what went wrong:

No you don't. Take getopt for instance. If I want to appropriately handle what went wrong that caused getopt to throw an exception, I need information on what went wrong. I need to know what flag failed and why it failed (e.g. unknown or it was given an invalid value). Exception doesn't give you any of that. You need a more specific exception type to get that information.
 An unexpected error occurred while
 trying to do X, where X is whatever is inside the try-catch block.
 Exceptions are for exceptional situations and not supposed to be used as a
 form of out of band information return (or at least that's what every
 programming book tells me).

Exceptions are for handling when something goes wrong in your program - frequently from interacting with users or I/O. It's much cleaner to write code which assumes that opening a file and reading for it will work than to check every operation to see whether it succeeded or not. Then you can just set up a try-catch block around it, and handle the error if it occurs. Whether the exception is "unexpected" is debatable. It _is_ expected in the sense that if something goes wrong with opening and reading the file, you're going to get an exception, and that's not a bug in your program, but it's not expected to happen in the common case. It's still expected to happen at least some of the time though. It's not like "something went wrong and we don't know what to do." If that's the case, you're moving towards Error territory, though that depends on if you can recover from a failed operation even if you have no clue what went wrong. When you avoid using exceptions is when there's a high chance that the operation will fail. Then you have the function return whether it succeeded or not, and exceptions would just slow the program down (I believe that a number of socket operations fall in this category for instance). But if the normal case is that it will work but occasionally it won't, an exception is the better way to go, since it leads to cleaner code. What's truly horrible is when you throw exceptions for normal situations (an extreme example being that you throw when an operation succeeds). _That_ is completely misusing exceptions, but most people don't do that. Exceptions are for error handling. If anything, I think that talk about "exceptional" cases leads people to completely misuse exceptions, because they often end up using them only for cases where Error should be used - that is unrecoverable situations. Personally, I'm against the use of error codes and returning whether an operation succeeded instead of using exceptions in the general case and that those should only be used when failure is _likely_. As long as it's at all reasonable to assume that an operation will succeed, using an exception to handle when it doesn't is far better than using the return value to indicate failure. - Jonathan M Davis
Feb 18 2012
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Saturday, February 18, 2012 20:30:45 Andrei Alexandrescu wrote:
 On 2/18/12 6:40 PM, H. S. Teoh wrote:
 One word: internationalization. Then toString() falls flat on its face.

No. I happen to have some expertise in the area as I've participated to two large and heavily internationalized systems. i18n has everything to do with string tables and formatting templates and emphatically nothing to do with exception hierarchies. The only possible link is that exceptions should provide the necessary hooks.

They do in that if you want to print a message because of that exception, you need to print something in the correct language, and that won't work with toString unless the exception type has built-in internationalization of some kind - regardless of what that internationalization mechanism might be. Regardless, it's going to generally be better to generate an application- specific error message than just print out whatever Exception outputs with toString (internationalized or otherwise). And you can't do that if all you have to work with is toString. To do that, you need actual data on what went wrong, which requires additional member variables, which means using a specific exception type. - Jonathan M Davis
Feb 18 2012
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Saturday, February 18, 2012 20:28:32 Andrei Alexandrescu wrote:
 On 2/18/12 6:36 PM, H. S. Teoh wrote:
 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.

If an exception adds state and/or interface to the table, I agree that may justify its existence as a distinct type.

If all you're arguing is that something like StringException shouldn't exist because it doesn't add any additional member fields, then that's a much more reasonable thing to debate. I'm not quite sure I agree, but some of your responses seem to indicate that you don't want a rich exception hierarchy. In the case of getopt, at _least_ adding a GetOptException with a field for the failed flag would be very valuable, and having additional derived types which indicate _why_ it failed would also be valuable. And in some cases at least, that would lead to more member fields in the derived exceptions (like the value of the flag if it were given a bad value). I would argue however that there _are_ times when having a derived exception which has no additional data beyond its type rather than simply Exception can be useful - _especially_ when it's at the base of a larger hierarchy. For instance, if we had an IOException, you could know that whatever operation you were trying to do went badly because of an IO problem. You would probably need to handle it as a subclass of IOException to get any particularly useful information on how to handle the problem, but just knowing that it was an I/O problem could be enough in some cases. I really think that whether adding an exception type which adds no additional member fields is a good idea should be evaluated on a case-by-case basis. We don't want to needlessly create a bunch of useless, uninformative exception types, but be we also want a rich enough exception hierarchy that it's possible to intelligently recover from exceptions. - Jonathan M Davis
Feb 18 2012
prev sibling next sibling parent "Bernard Helyer" <b.helyer gmail.com> writes:
On Sunday, 19 February 2012 at 02:27:07 UTC, Andrei Alexandrescu 
wrote:
 On 2/18/12 6:28 PM, Jonathan M Davis wrote:
 On Saturday, February 18, 2012 17:53:52 Andrei Alexandrescu 
 wrote:
 On 2/18/12 5:47 PM, Jakob Ovrum wrote:
 you are basically arguing against exceptions here

I must have argued my question and point very poorly.

You definitely seem to be arguing for a single Exception type

No.

Yes. Bernard (This game is fun!)
Feb 18 2012
prev sibling next sibling parent "Robert Jacques" <sandford jhu.edu> writes:
On Sat, 18 Feb 2012 20:21:46 -0600, Jonathan M Davis <jmdavisProg gmx.com>
wrote:

 On Saturday, February 18, 2012 19:59:52 Robert Jacques wrote:
 But you _always_ know what went wrong:

No you don't. Take getopt for instance. If I want to appropriately handle what went wrong that caused getopt to throw an exception, I need information on what went wrong. I need to know what flag failed and why it failed (e.g. unknown or it was given an invalid value). Exception doesn't give you any of that. You need a more specific exception type to get that information.

There's a difference between knowing what went wrong and knowing how to fix it. That said, I fully agree with your excellent post. My intent wasn't to propose or support the use of error codes, but to observe that effective use typed exceptions tends to look a lot like error code. For instance, the function calling getop knows how to use getop, what getop's exceptions are and can (hopefully) use the exception's extra information to recover gracefully. But now consider the function that calls the function that calls getop. It probably doesn't even know that getop can be called and that its exceptions can be thrown. Even if it did, it probably doesn't have the access to getop needed utilize that information in the recovery process. Worse, if function A could leverage the type exception, it probably means A and B's implementations are heavily coupled leading to long term maintenance issues. With the number of times 'bad exception hierarchy' has come up in this discussion and the number of times I've seen MyClassException be a simple wrapper around whatever happens to throw inside MyClass, I guess I'm just ready to explorer solutions out of the box. All my brains have been able to come up with so far is a) beef up exception to aid generic recovery code and b) add a TupleException/VariantException that would allow you to express enforce(isFlagGood[i], i, flags[i], "My flag error message"); or something. i.e. a way to provide extra info when available in the leanest possible manner.
Feb 18 2012
prev sibling next sibling parent "Robert Jacques" <sandford jhu.edu> writes:
On Sat, 18 Feb 2012 23:09:17 -0600, Jim Hewes <jimhewes gmail.com> wrote:
 On 2/18/2012 5:59 PM, Robert Jacques wrote:
 But you _always_ know what went wrong: An unexpected error occurred
 while trying to do X, where X is whatever is inside the try-catch block.
 Exceptions are for exceptional situations...

Not to jump on you in particular for using the phrase "exceptions are for exceptional situations”, but I've always heard people say this in arguments about exceptions vs. returning error codes. Unfortunately those people usually never go on to define what exceptional situations are, so those trying to learn about using exceptions are left no better off. If exceptional cases are like divide-by-zero or hardware failures, then surely a bad parameter to a function is not “exceptional”. Then what? Is out-of-memory exceptional or something you should expect might happen given that memory is finite? I think of exception handling as tied to contract programming. A function has a specific job that it's supposed to do. If for any reason it cannot do that job successfully, an exception should be thrown. That can include even bad parameters (although if you have bad parameters to internal functions I'd think that is a design bug and could be handled by asserts). Look at the .NET library; it seems to work this way. So I think the term 'exceptional situations' has been kind of useless.

Not to jump on you in particular :) but bad user parameters should never be treated as exceptional. Even bad 'internal' parameters that are passed via the external API aren't exceptional. Programmers being lazy about input parameter checking is how hackers make their money. [snip]
 I see typed exceptions as having a strong
 coupling problem; exception handling that is aware enough to handle a
 typed exception, is so close to the exception generation code that most
 of the information conveyed by the 'type' is also conveyed by the
 context. Now, the further away exception handling gets from exception
 generation, the less information context coveys, but the ability of the
 handler to do anything smarter than log the exception and retry / cancel
 becomes less and less.

I can't say agree there, if I'm understand you right. Not every exception coming out of a function was generated by that function. It may have come from several levels below that. Maybe you can handle the former because it is more immediate but not the latter. So without exception types how would you know the difference between them? You could use error codes and switch on them but it defeats one of the main purposes of exception handling. So I think if there are exceptions generated from further away, it's an argument _for_ exception types rather than against it. I can think of a example. At work I wrote a driver that communicates to a device over USB. If the device is suddenly unplugged, the lowest level of code throws a “connection” exception. And this can happen in several places. Most levels can't do anything about this (other than just be exception safe) and the exception just percolates up where things get closed and cleaned up. However, it would be possible, though not currently implemented, to recover from this such that the driver waits for the device to get reconnected and refreshes handles and all that, and the application and user above never need to know. Where that recovery happens isn't necessarily at the lowest level but somewhere in a middle layer. So I think having a particular exception type is useful here. But I don't want to have to check error codes because under normal circumstances there are just too many places to check for this type of error.

Yes, the USB stack has a high-level layer that can recover from a connection loss, but the rest of the protocol stack above and below it can't understand it and presumably ignore it. And you have just introduced a fairly long range dependency between the lowest level of your code and middle layer. (To say nothing of the implicit dependences implied with the intervening layers). This might be the best/only solution to the problem, but it also makes the code base larger and more fragile. I wasn't speaking in absolutes, but in relative difficulties.
  > The other issue I see with typed exceptions is
 that the classic 'is a' relationship seems weak; to be informative and
 useful and exception must map to a specific or small set of
 error/unexpected conditions. However, this specificity naturally leads
 to large number of final classes, which isn't really leveraging OO
 principals. It's hard to see what, if any, benefit the intermediate base
 classes give over a generic exception.

Well, I think what H.S Tech has been saying in this thread makes sense to me. You can have an exception hierarchy where base classes represent groups or classes of errors that you can check for without having to catch all exception. But you don't have thousands of classes where you map each possible error code to a unique class. You can just have more general types and then put the specific information in the exception. For example, just have one BadParameter exception and then store information about which parameter is bad and why in the exception. Jim

Traditional error codes are enums and so what your describe as BadParameter is an exception wrapping an error code. I really hope no one has been proposing to map error code values to exceptions on a 1:1 basis. But your making many of my points; exceptions are (better, faster, stronger) error codes. My issue regarding the weak 'is a' relationship stems from the extra information in any given final typed exception being very specific to that particular exception. So its hard for me to see the rational between treating some of these as a group while not all of them: if MyGroupException is providing no more information then Exception, why can a function recover a MyGroupException and not a general Exception?
Feb 18 2012
prev sibling next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Sun, Feb 19, 2012 at 12:43:58AM -0600, Andrei Alexandrescu wrote:
 On 2/18/12 8:00 PM, H. S. Teoh wrote:
 From this and other posts I'd say we need to design the base exception
classes better, for example by defining an overridable property
isTransient that tells caller code whether retrying might help.

Just because an exception is transient doesn't mean it makes sense to try again. For example, saveFileMenu() might read a filename from the user, then save the data to a file. If the user types an invalid filename, you will get an InvalidFilename exception. From an abstract point of view, an invalid filename is not a transient problem: retrying the invalid filename won't make the problem go away. But the application in this case *wants* to repeat the operation by asking the user for a *different* filename. On the other hand, if the same exception happens in an app that's trying to read a configuration file, then it *shouldn't* retry the operation.

I'm thinking an error is transient if retrying the operation with the same exact data may succeed. That's a definition that's simple, useful, and easy to operate with.

But if that's the case, what's the use of an exception at all? Why doesn't the function concerned simply retry on its own? Network stack code does that. It would be nightmarish to program network applications if you always have to implement retry on your own (much less use *exceptions* to handle them)! T -- Let's not fight disease by killing the patient. -- Sean 'Shaleh' Perry
Feb 18 2012
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday, February 19, 2012 00:43:58 Andrei Alexandrescu wrote:
 On 2/18/12 8:00 PM, H. S. Teoh wrote:
  From this and other posts I'd say we need to design the base exception
 
 classes better, for example by defining an overridable property
 isTransient that tells caller code whether retrying might help.

Just because an exception is transient doesn't mean it makes sense to try again. For example, saveFileMenu() might read a filename from the user, then save the data to a file. If the user types an invalid filename, you will get an InvalidFilename exception. From an abstract point of view, an invalid filename is not a transient problem: retrying the invalid filename won't make the problem go away. But the application in this case *wants* to repeat the operation by asking the user for a *different* filename. On the other hand, if the same exception happens in an app that's trying to read a configuration file, then it *shouldn't* retry the operation.

I'm thinking an error is transient if retrying the operation with the same exact data may succeed. That's a definition that's simple, useful, and easy to operate with.

A core problem with the idea is that whether or not it makes sense to try again depends on what the caller is doing. In general, I think that it's best to give the caller as much useful information is possible so that _it_ can decide the best way to handle the exception. - Jonathan M Davis
Feb 18 2012
prev sibling next sibling parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday, February 19, 2012 18:44:30 Daniel Murphy wrote:
 I assume you mean the _first_ catch block?

Yes. I should have been more specific.
 By the looks of it java 7 has this with an awful syntax.
 
 Maybe we should introduce implicit 'catch fallthrough':
 
 void fun()
 {
     try { something; }
     catch (ExceptionA) {} // falls through implicitly
     catch (ExpcetionB)
     { // handle both A and B
         break;
     }
     catch (ExceptionC)
     {
         // handle exception C
         break;
     }
 }

That wouldn't work, because it would make it so that ignore an exception in the middle if you wanted to (much as that's generally bad practice). It would also have the problem that you couldn't access the exception itself, and while you obviously wouldn't be operating on the exception's exact type regardless, you might want to iteract with the functions on a common based class. So, while at first glance, it seems like a good idea, I think that it has too many issues as-is to work. It might be possible to adjust the idea to make it workable though. Right now, it's possible to do it via mixins or calling a function inside the catch, but doing something similar to this would certainly be nice, assuming that we could sort out the kinks. - Jonathan M Davis
Feb 18 2012
parent reply "Daniel Murphy" <yebblies nospamgmail.com> writes:
"Jonathan M Davis" <jmdavisProg gmx.com> wrote in message 
news:mailman.585.1329637995.20196.digitalmars-d puremagic.com...
 On Sunday, February 19, 2012 18:44:30 Daniel Murphy wrote:
 I assume you mean the _first_ catch block?

Yes. I should have been more specific.
 By the looks of it java 7 has this with an awful syntax.

 Maybe we should introduce implicit 'catch fallthrough':

 void fun()
 {
     try { something; }
     catch (ExceptionA) {} // falls through implicitly
     catch (ExpcetionB)
     { // handle both A and B
         break;
     }
     catch (ExceptionC)
     {
         // handle exception C
         break;
     }
 }

That wouldn't work, because it would make it so that ignore an exception in the middle if you wanted to (much as that's generally bad practice). It would also have the problem that you couldn't access the exception itself, and while you obviously wouldn't be operating on the exception's exact type regardless, you might want to iteract with the functions on a common based class. So, while at first glance, it seems like a good idea, I think that it has too many issues as-is to work. It might be possible to adjust the idea to make it workable though. Right now, it's possible to do it via mixins or calling a function inside the catch, but doing something similar to this would certainly be nice, assuming that we could sort out the kinks. - Jonathan M Davis

I wasn't really serious about implicit fallthrough. Out of the syntaxes I could come up with: catch(Ex1, Ex2 e) catch(e : Ex1, Ex2) catch(Ex1 | Ex2 e) // java 7 syntax, horrible I like (e : list) the best. Naturally it would also accept a type tuple of exceptions. http://d.puremagic.com/issues/show_bug.cgi?id=7540
Feb 19 2012
next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2012-02-19 10:26, Jonathan M Davis wrote:
 On Sunday, February 19, 2012 19:00:20 Daniel Murphy wrote:
 I wasn't really serious about implicit fallthrough.

Lately, it seems like I can never tell whether anyone's being serious or not online. :)
 Out of the syntaxes I could come up with:
 catch(Ex1, Ex2 e)
 catch(e : Ex1, Ex2)
 catch(Ex1 | Ex2 e) // java 7 syntax, horrible

 I like (e : list) the best.  Naturally it would also accept a type tuple of
 exceptions.

 http://d.puremagic.com/issues/show_bug.cgi?id=7540

LOL. Personally, I actually think that the Java 7 syntax looks great (I'd never seen it before), but catch(e : Ex1, Ex2) is just as good and more consistent with the language as a whole, since it doesn't try to give any operators a new meaning (as Java's does). - Jonathan M Davis

How is "catch(e : Ex1, Ex2)" consistent with the language? It's completely backwards. catch-block are written as follows: catch (Exception e) {} Not catch (e : Exception) {} -- /Jacob Carlborg
Feb 19 2012
parent Jacob Carlborg <doob me.com> writes:
On 2012-02-19 23:44, Jonathan M Davis wrote:
 On Sunday, February 19, 2012 16:07:27 Jacob Carlborg wrote:
 On 2012-02-19 10:26, Jonathan M Davis wrote:
 On Sunday, February 19, 2012 19:00:20 Daniel Murphy wrote:
 I wasn't really serious about implicit fallthrough.

Lately, it seems like I can never tell whether anyone's being serious or not online. :)
 Out of the syntaxes I could come up with:
 catch(Ex1, Ex2 e)
 catch(e : Ex1, Ex2)
 catch(Ex1 | Ex2 e) // java 7 syntax, horrible

 I like (e : list) the best.  Naturally it would also accept a type tuple
 of
 exceptions.

 http://d.puremagic.com/issues/show_bug.cgi?id=7540

LOL. Personally, I actually think that the Java 7 syntax looks great (I'd never seen it before), but catch(e : Ex1, Ex2) is just as good and more consistent with the language as a whole, since it doesn't try to give any operators a new meaning (as Java's does). - Jonathan M Davis

How is "catch(e : Ex1, Ex2)" consistent with the language? It's completely backwards. catch-block are written as follows: catch (Exception e) {} Not catch (e : Exception) {}

I meant the meaning of the : operator vs the meaning of the | operator. : has to do with derived types already, whereas | is for bitwise operations. Doing something like

Oh, as in template constrains, I see. Forgot about that.
 catch(Ex1, Ex2 : Ex0 e)

 would be even more consistent though for the reasons that you point out.

 - Jonathan m Davs

-- /Jacob Carlborg
Feb 20 2012
prev sibling next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/19/12 4:30 AM, Juan Manuel Cabo wrote:
 Hello D community! This is my first post!! I hope I can bring clarity to
 all this. If not, I apologize.

Thanks for an insightful post. If you found the time, it would be great if you could translate your paper on exceptions from Spanish. Andrei
Feb 19 2012
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/19/12 3:26 AM, Jonathan M Davis wrote:
 On Sunday, February 19, 2012 19:00:20 Daniel Murphy wrote:
 I wasn't really serious about implicit fallthrough.

Lately, it seems like I can never tell whether anyone's being serious or not online. :)
 Out of the syntaxes I could come up with:
 catch(Ex1, Ex2 e)
 catch(e : Ex1, Ex2)
 catch(Ex1 | Ex2 e) // java 7 syntax, horrible

 I like (e : list) the best.  Naturally it would also accept a type tuple of
 exceptions.

 http://d.puremagic.com/issues/show_bug.cgi?id=7540

LOL. Personally, I actually think that the Java 7 syntax looks great (I'd never seen it before), but catch(e : Ex1, Ex2) is just as good and more consistent with the language as a whole, since it doesn't try to give any operators a new meaning (as Java's does). - Jonathan M Davis

The Java7 syntax looks meaningful to me, too - you want to catch the union type. A possibility that wouldn't change the language for us would be to catch Algebraic!(Ex1, Ex2). Andrei
Feb 19 2012
next sibling parent "Daniel Murphy" <yebblies nospamgmail.com> writes:
"Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message 
news:jhr67g$2dup$4 digitalmars.com...
 The Java7 syntax looks meaningful to me, too - you want to catch the union 
 type. A possibility that wouldn't change the language for us would be to 
 catch Algebraic!(Ex1, Ex2).

 Andrei

That would still require changing the language, just not the syntax. (E : A, B) was just the syntax for listing base classes.
Feb 19 2012
prev sibling parent "Nick Sabalausky" <a a.a> writes:
"Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message 
news:jhr67g$2dup$4 digitalmars.com...
 The Java7 syntax looks meaningful to me, too - you want to catch the union 
 type. A possibility that wouldn't change the language for us would be to 
 catch Algebraic!(Ex1, Ex2).

I'd argue that a templated catch block would be better so you wouldn't have to query what type it is in order to use it: // With a little compler magic: catch(TypeTuple!(Ex1, Ex2) e) { writeln(e.msg); // Look ma, no querying! } // Mimicking D1-style template contraints: catch(E : Ex1, Ex2)(E e) { writeln(e.msg); }
Feb 19 2012
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/19/12 4:30 AM, Juan Manuel Cabo wrote:
 Now, expanding on the hierarchy: I said that it makes no sense to
 subclass UnrecoverableExceptions. Recoverable exceptions on the other
 hand, need to be subclassed _with_the_catch_on_your_mind_. You are
 passing info from the throw site to the catch site. The catch block is
 the interpreter of the info, the observer. You are communicating
 something to the catch block.

 So please, do not create a new types if there is no value in writing a
 catch that only cathes that exception and that can recover from that
 exception. Otherwise, use an existing type.

I think this is a very sensible point. I wonder to what extent it clashes with a simple idea that just occurred to me. It would be very easy to define a hierarchy as follows: class ModuleException(string theModule) : PackageException(packageOf(theModule)) { ... } Then any module could issue throw new ModuleException!(.stringof); That way the exception hierarchy would parallel the module hierarchy. (A module is free to define more specific exception inheriting ModuleException!(.stringof)). The catcher would catch things like ModuleException!"acme.text.io" and such. In that approach, all exceptions thrown from Phobos would inherit PackageException!"std" and all exceptions thrown from std.getopt would be (or inherit) ModuleException!"std.getopt". This would save a lot on the boilerplate while still keeping open the option of defining fine-grained hierarchies. Any thoughts, please chime. Andrei
Feb 19 2012
parent reply "Nick Sabalausky" <a a.a> writes:
"Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message 
news:jhrave$2nas$1 digitalmars.com...
 On 2/19/12 4:30 AM, Juan Manuel Cabo wrote:
 Now, expanding on the hierarchy: I said that it makes no sense to
 subclass UnrecoverableExceptions. Recoverable exceptions on the other
 hand, need to be subclassed _with_the_catch_on_your_mind_. You are
 passing info from the throw site to the catch site. The catch block is
 the interpreter of the info, the observer. You are communicating
 something to the catch block.

 So please, do not create a new types if there is no value in writing a
 catch that only cathes that exception and that can recover from that
 exception. Otherwise, use an existing type.

I think this is a very sensible point. I wonder to what extent it clashes with a simple idea that just occurred to me. It would be very easy to define a hierarchy as follows: class ModuleException(string theModule) : PackageException(packageOf(theModule)) { ... } Then any module could issue throw new ModuleException!(.stringof); That way the exception hierarchy would parallel the module hierarchy. (A module is free to define more specific exception inheriting ModuleException!(.stringof)). The catcher would catch things like ModuleException!"acme.text.io" and such. In that approach, all exceptions thrown from Phobos would inherit PackageException!"std" and all exceptions thrown from std.getopt would be (or inherit) ModuleException!"std.getopt". This would save a lot on the boilerplate while still keeping open the option of defining fine-grained hierarchies. Any thoughts, please chime.

That wouldn't be as useful. What the catcher is typically interested in is *what* happened, not *where* it happened. For example, if I want to do something upon a network error, minimum 99 times out of 100 I don't give a shit if it came from libNetworkFoo or libNetworkBar, and I don't *want* to care. What I care is whether or not there was a "network" error and possibly what *conceptual* type of network error. Furthurmore, what if I change some implementation detail to use a different module? Then I have to go changing all my catch blocks even though it's conceptually the same fucking error handled the same way. However, I wouldn't object to the idea of an "originatingModule" member being added to Exception that's automatically filled by the runtime (perhaps lazily). Although really, I think what would be more useful that that would be "Does xxx module/package exist in the portion of the callstack that's been unwound?" As far as "when to add or not add an exception class", it's perfectly reasonable to err on the side of too many: If there's an unnecessary class, you can just ignore it. Problem solved. If there's a missing exception class, you're shit out of luck. Case closed. I can't shake the feeling that we're desperately trying to reinvent the wheel here. The round wheel is solid technology with a proven track record, we don't need to waste time evaluating all these square and oval wheels just for the fuck of it.
Feb 19 2012
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/19/12 1:19 PM, Nick Sabalausky wrote:
 That wouldn't be as useful. What the catcher is typically interested in is
 *what* happened, not *where* it happened.

But module organization is partitioned by functional areas.
 For example, if I want to do something upon a network error, minimum 99
 times out of 100 I don't give a shit if it came from libNetworkFoo or
 libNetworkBar, and I don't *want* to care. What I care is whether or not
 there was a "network" error and possibly what *conceptual* type of network
 error.

Then it wouldn't help if each defined its own hierarchy. The way I see it is, a "well-designed" package and module hierarchy would naturally engender a "well-designed" exception hierarchy. This is because packages and modules are organized on functional areas, so e.g. there is an "std.net" package that has its own exception types etc. There would be some special cases indeed (e.g. a module initiating an exception defined in another), so it's good those are possible too. I want to automate the common case.
 Furthurmore, what if I change some implementation detail to use a different
 module? Then I have to go changing all my catch blocks even though it's
 conceptually the same fucking error handled the same way.

That is an issue regardless. Occasional exception translation is a fact of life.
 However, I wouldn't object to the idea of an "originatingModule" member
 being added to Exception that's automatically filled by the runtime (perhaps
 lazily). Although really, I think what would be more useful that that would
 be "Does xxx module/package exist in the portion of the callstack that's
 been unwound?"

That's why PackageException!"tango.io" inherits PackageException!"tango". That's all automatic. Essentially there's 1:1 correspondence between package/module hierarchy and exception hierarchy.
 As far as "when to add or not add an exception class", it's perfectly
 reasonable to err on the side of too many: If there's an unnecessary class,
 you can just ignore it. Problem solved. If there's a missing exception
 class, you're shit out of luck. Case closed.

I disagree that having too many exception types comes at no cost.
 I can't shake the feeling that we're desperately trying to reinvent the
 wheel here. The round wheel is solid technology with a proven track record,
 we don't need to waste time evaluating all these square and oval wheels just
 for the fuck of it.

The wheel is not round. We just got used to thinking it is. Exceptions are wanting and it's possible and desirable to improve them. Andrei
Feb 19 2012
next sibling parent reply "Nick Sabalausky" <a a.a> writes:
"Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message 
news:jhrnn5$h1l$1 digitalmars.com...
 On 2/19/12 1:19 PM, Nick Sabalausky wrote:
 That wouldn't be as useful. What the catcher is typically interested in 
 is
 *what* happened, not *where* it happened.

But module organization is partitioned by functional areas.
 For example, if I want to do something upon a network error, minimum 99
 times out of 100 I don't give a shit if it came from libNetworkFoo or
 libNetworkBar, and I don't *want* to care. What I care is whether or not
 there was a "network" error and possibly what *conceptual* type of 
 network
 error.

Then it wouldn't help if each defined its own hierarchy.

Right, and yet that's exactly what your suggestion of tying exceptions to modules would imply.
 Furthurmore, what if I change some implementation detail to use a 
 different
 module? Then I have to go changing all my catch blocks even though it's
 conceptually the same fucking error handled the same way.

That is an issue regardless. Occasional exception translation is a fact of life.

You're suggestion exacerbates the problem for no user benefit. This isn't the first time you've tried to push a negligably-time-saving scheme into Phobos at the expense of user code.
 That's why PackageException!"tango.io" inherits PackageException!"tango". 
 That's all automatic. Essentially there's 1:1 correspondence between 
 package/module hierarchy and exception hierarchy.

But there *isn't* a 1:1 correspondence. A "file not found" is damn "file not found" no matter what lib you're using: phobos, tango, fooBarFileLib, what-the-hell-ever. You're "1:1" pretends that a non-existent file is somehow different from one lib to another.
 As far as "when to add or not add an exception class", it's perfectly
 reasonable to err on the side of too many: If there's an unnecessary 
 class,
 you can just ignore it. Problem solved. If there's a missing exception
 class, you're shit out of luck. Case closed.

I disagree that having too many exception types comes at no cost.

Jesus christ. You pull out the pedantic "that's not a perfectly-stated argument" whacking stick at every opportunity, and yet you yourself put forth arguments like "I disagree"? What the fuck?
 I can't shake the feeling that we're desperately trying to reinvent the
 wheel here. The round wheel is solid technology with a proven track 
 record,
 we don't need to waste time evaluating all these square and oval wheels 
 just
 for the fuck of it.

The wheel is not round. We just got used to thinking it is. Exceptions are wanting and it's possible and desirable to improve them.

They're wanting? What's the problem with them? I see no problem, and I haven't seen you state any real problem.
Feb 19 2012
next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/19/12 4:20 PM, Nick Sabalausky wrote:
 "Andrei Alexandrescu"<SeeWebsiteForEmail erdani.org>  wrote in message
 The wheel is not round. We just got used to thinking it is. Exceptions are
 wanting and it's possible and desirable to improve them.

They're wanting? What's the problem with them? I see no problem, and I haven't seen you state any real problem.

I mentioned the issues here and there, but it's worth collecting them in one place. 1. Type multiplicity. Too many handwritten types are harmful. They exacerbate boilerplate and favor code duplication. 2. Using types and inheritance excessively to represent simple categorical information. 3. Forcing cross-cutting properties into a tree structure. The way I see exceptions is a semantic graph, and making it into a tree forces things like repeated catch clauses for distinct types coming from distinct parts of the hierarchy. 4. Front-loaded design. Very finely-grained hierarchies are built on the off chance someone may need something AND would want to use a type for that. 5. Unclear on the protocol. The "well-designed" phrase has come about often in this thread, but the specifics are rather vague. 6. Bottom-heavy base interface. Class hierarchies should have significant functionality at upper levels, which allows generic code to do significant reusable work using the base class. Instead, exceptions add functionality toward the bottom of the hierarchy, which encourages non-reusable code that deals with specifics. There might be a couple more, but I think they can be considered derivative. Andrei
Feb 19 2012
prev sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday, February 19, 2012 17:35:31 Andrei Alexandrescu wrote:
 On 2/19/12 4:20 PM, Nick Sabalausky wrote:
 "Andrei Alexandrescu"<SeeWebsiteForEmail erdani.org>  wrote in message
 
 The wheel is not round. We just got used to thinking it is. Exceptions
 are
 wanting and it's possible and desirable to improve them.

They're wanting? What's the problem with them? I see no problem, and I haven't seen you state any real problem.

I mentioned the issues here and there, but it's worth collecting them in one place. 1. Type multiplicity. Too many handwritten types are harmful. They exacerbate boilerplate and favor code duplication.

Assuming that the exception provides additional information via member variables rather than just being a new exception type which is essentially identical to Exception save for its type, then you have to write them by hand anyway. It's only the ones where their only point of existence is their type (which therefore indicates the type of problem that occurred but isn't able for whatever reason to give any further useful information) which result in boilerplate problems. And since in general we really should be adding member variables with additional information, the boilerplate should be minimized. And if we _do_ want such classes, we can use a mixin to generate them (the downside being that they don't end up in the ddoc - though that could be fixed, and it probably should be). We shouldn't go to town and create tons and tons of exception types, but we should also have them where they're useful.
 2. Using types and inheritance excessively to represent simple
 categorical information.

try-catch blocks are _designed_ to take advantage of types and inheritance. So, moving away from using derived types for exceptions makes it harder to write exception handling code. It also works very well with inheritance to have someone catch a more general exception and ignore the existance of the more specific ones if they don't care about the specifics of what went wrong. So, the hierarchy ends up being very useful. And how do intend to _add_ additional information to exceptions if not through derived types which can then hold additional member variables? I don't see how you could have the relevant information without going out of your way to avoid inheritance by using type codes and something equivalent to void* for the data. It would just be cleaner - and better suited to how catch blocks work - to use an exception hierarchy. It seems to me that the very design of exceptions in the language is geared towards having a class hierarchy of exceptions. And I contend that doing that works quite well. Certainly, in my experience, it does. It's when programmers try to avoid handling exceptions that things go awry (e.g. by catching Exception and then ignoring it), and that can happen regardless of your design. You can't prevent programmers from being stupid.
 3. Forcing cross-cutting properties into a tree structure. The way I see
 exceptions is a semantic graph, and making it into a tree forces things
 like repeated catch clauses for distinct types coming from distinct
 parts of the hierarchy.

Except that in most cases, you want to do handle exceptions differently if they come from different parts of the hierarchy. And making it so that you could do something like catch(Ex1, Ex2 : Ex e) would cover the other cases quite well.
 4. Front-loaded design. Very finely-grained hierarchies are built on the
 off chance someone may need something AND would want to use a type for that.

There's definitely some truth to that. However, it's very easy to add more exception types later without disrupting code. For instance, if you added IOException, and made all of the exsisting exception types which would make sense with that as their base class then derive from it, then new code could choose to call either IOException or the more specific exception types. And existing could would _already_ call the more specific exception types, so nothing would be disrupted. And if you added more specific exceptions (e.g. creating FileNotFoundException and making it derive from FileException), then new code could choose to catch the more specific exception instead of FileException, but existing code would continue to catch FileException without a problem. So, we can organize our exception hierarchy on a fairly rough level to begin with and add exceptions to it (both common and derived) as appropriate. And while we'd still end up with some up front design, because we'd have to create at least some of the hierarchy to begin with, the fact that we can add to the hierarchy later without breaking code minimizes the need to add stuff that we think that we _might_ need. It's removing exception types and moving them from one part of the hierarchy to another which is disruptive. And while avoiding those _does_ mean doing a good job with how you initially put exceptions into a hierarchy, I don't think that it's that hard to avoid once you've got a basic hierarchy, especially if we start with a minimal number of exception types and add more later as appropriate rather than creating a large, complex hierarchy to begin with. The main breakage would be in moving from the module-specific exceptions to a hierarchy, and much of that wouldn't break anything, because as we don't have much of a hierarchy right now, it would only be an issue with the ones that we decided to remove.
 5. Unclear on the protocol. The "well-designed" phrase has come about
 often in this thread, but the specifics are rather vague.

As with any design, we'd have to look at concrete examples and examine their pros and cons. I think that looking at C# and Java is a good place to start. That should give us at least a good idea of what _they_ think is a well- designed hierarchy, and we can take the pieces that best apply to us and our situation. We don't need to start with anything as large as they have, but it's something to work from.
 6. Bottom-heavy base interface. Class hierarchies should have
 significant functionality at upper levels, which allows generic code to
 do significant reusable work using the base class. Instead, exceptions
 add functionality toward the bottom of the hierarchy, which encourages
 non-reusable code that deals with specifics.

True. But I don't see a way around that. The more specific the problem, the more information that you have. The more general the problem, the less information that you have. So, you naturally end up with more data in the derived classes. And for the most part, it's the data that you want, not polymorphic functions. Polymorphism is of limited usefulness with exceptions. It's the hierarchy which is useful. In fact, you could probably have exceptions be non-polymorphic if we had a construct in the language which had inheritance but not polymorphism. But we don't. So, while I think that you make a valid point, I still think that using classes with an inheritance hierarchy is the best fit. - Jonathan M Davis
Feb 19 2012
prev sibling next sibling parent deadalnix <deadalnix gmail.com> writes:
Le 19/02/2012 21:57, Andrei Alexandrescu a crit :
 On 2/19/12 1:19 PM, Nick Sabalausky wrote:
 That wouldn't be as useful. What the catcher is typically interested
 in is
 *what* happened, not *where* it happened.

But module organization is partitioned by functional areas.

Modules are organized by what the do, not how they fail. This hierarchy make no sense to me. Worse, it will not be reusable in user code.
Feb 19 2012
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/20/12 12:44 AM, foobar wrote:
 I just died a little reading this. Are you suggesting that in order
 to handle IO exceptions I need to: try { ...whatever... } catch
 (PackageException!"std.io") {...} } catch
 (PackageException!"tango.io") {...} } catch
 (PackageException!"otherLib.io") {...} ...

 What the hell is wrong with just using an IOException?

There's nothing wrong, but there's a possible misunderstanding. If tango.io and otherLib.io cooperate with std, then they'd originate exceptions in std.io (as opposed to their own). Do note that the issue is exactly the same if libraries use IOException - they all must agree on using the same nomenclature, whether it's called PackageException!"std.io" or IOException. ModuleException and PackageException have one important thing going for them: they automate away a good amount of boilerplate, which makes them interesting for me to look at, and worth sharing as long as we're brainstorming. The associated issues as clear as the advantages. Probably ModuleException is too specific to be interesting, but PackageException seems useful.
 AS Nick wrote, it seems you have a complete lack of understanding of
  how exceptions work which is unsurprising coming from a c++ expert.

Always eager to learn (so please come with all you've got), but quite honest I hope in a way you're exaggerating, seeing as a lot of the stuff I authored for D (the scope statement, exception chaining, a full chapter in TDPL) would be completely broken.
 Also, this entire discussion you started about "improving" exceptions
 looks to me like a combination of NIH syndrome sparkled with heavy
 doses of premature optimization.

What is being optimized here? Thanks, Andrei
Feb 19 2012
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/20/12 1:38 AM, Jonathan M Davis wrote:
 On Monday, February 20, 2012 01:10:39 Andrei Alexandrescu wrote:
 ModuleException and PackageException have one important thing going for
 them: they automate away a good amount of boilerplate, which makes them
 interesting for me to look at, and worth sharing as long as we're
 brainstorming. The associated issues as clear as the advantages.
 Probably ModuleException is too specific to be interesting, but
 PackageException seems useful.

It saves no boilerplate at the catch point, because you have to know what you're catching to do anything useful with it. It could be that you choose to catch a common exception rather than a specific one (e.g. IOException instead of FileException), but regardless of whether you use templates or mixins or whatever to generate the exception type's source code, you still need to write the handling code by hand. Handling code is _not_ the sort of thing that can be automated.

Absolutely. For catching we're looking for allowing centralization, not for automation.
 And as for saving boilerplate in defining exceptions, well some of that could
 be done via mixins, but since useful exceptions often have additional member
 variables, you're going to have to write many of them by hand anyway.

Again, I think this thread clarified we need the "Variant[string] info;" member however we define the hierarchy. Also, I think we can do better than defining the boilerplate constructor (see e.g. https://github.com/D-Programming-Language/phobos/pull/439). It's just a function. Consider: // this goes in the stdlib void raise(ConcreteException)(string message, Throwable t = null, string f = __FILE__, size_t l = __LINE__) { auto r = new ConcreteException; r.message = message; r.file = f; r.line = l; r.next = t; throw r; } class AcmeException : Exception {} Now whenever you want to raise AcmeException, you say raise!AcmeException("message"). Also, raise may accept additional data that fills the Variant[string]. That makes exception definitions one-liners.
 And as has been said before, ultimately the module that an exception comes
 from doesn't mean much. It's what went wrong that matters. And ideally, the
 standard library would have exception types that user code would throw or
 derive its own exception types from, in which case the exception types get
 even more divorced from the modules that they're declared in. So, ultimately,
 tying exceptions to modules or packages is _not_ a good idea in the general
 case. Sometimes it makes sense - particularly if you're talking about making a
 base exception type which a project as a whole uses (e.g. a PostgresException
 for a postgres library) - but in general, it's a bad approach, and it's one
 that we should be moving away from.

I tend to agree, particularly because it's easier for people to remember express names instead of origins encoded by module. Ironically, many of Phobos' exceptions today follow the exception-to-module correspondence. Still I think it's worth keeping in mind the possibility of having the PackageException as a base trail for various exceptions specific to a package. Andrei
Feb 20 2012
next sibling parent reply dennis luehring <dl.soluz gmx.net> writes:
 Again, I think this thread clarified we need the "Variant[string] info;"
 member however we define the hierarchy.

to use an mighty hyper map capable of holding all informative "values" will just follow in the same amount of non-using code, and the using code will be filled up with info["blub"], info["blab"], evil castings, sensless const key-string an still no proper way to show/use the information in an generic and typesafe way sorry but feels like throwing anway the signature conecpt and replace it with an runtime thing ... maybe we should also get rid of function signatures with "Variant[string] parameter" :}
Feb 20 2012
next sibling parent "Nick Sabalausky" <a a.a> writes:
"dennis luehring" <dl.soluz gmx.net> wrote in message 
news:jhtrh1$113l$1 digitalmars.com...
 Again, I think this thread clarified we need the "Variant[string] info;"
 member however we define the hierarchy.

to use an mighty hyper map capable of holding all informative "values" will just follow in the same amount of non-using code, and the using code will be filled up with info["blub"], info["blab"], evil castings, sensless const key-string an still no proper way to show/use the information in an generic and typesafe way sorry but feels like throwing anway the signature conecpt and replace it with an runtime thing ... maybe we should also get rid of function signatures with "Variant[string] parameter" :}

Exactly. It smacks of abandoning normal code in favor of JS and other such ultra-dynamic toy/scripting langauges where *everything* is a mutable AA just for the fuck of it.
Feb 20 2012
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/20/12 10:15 AM, dennis luehring wrote:
 Again, I think this thread clarified we need the "Variant[string] info;"
 member however we define the hierarchy.

to use an mighty hyper map capable of holding all informative "values" will just follow in the same amount of non-using code, and the using code will be filled up with info["blub"], info["blab"], evil castings, sensless const key-string an still no proper way to show/use the information in an generic and typesafe way sorry but feels like throwing anway the signature conecpt and replace it with an runtime thing ... maybe we should also get rid of function signatures with "Variant[string] parameter" :}

I understand this seems loose, but again, when you want to do custom formatting or i18n this is the way to go. The job of rendering the exception as a string must be outsourced (heh) outside the exception, and Variant[string] is a simple mechanism to do so. Andrei
Feb 20 2012
next sibling parent dennis luehring <dl.soluz gmx.net> writes:
Am 20.02.2012 17:36, schrieb Andrei Alexandrescu:
 On 2/20/12 10:15 AM, dennis luehring wrote:
  Again, I think this thread clarified we need the "Variant[string] info;"
  member however we define the hierarchy.

to use an mighty hyper map capable of holding all informative "values" will just follow in the same amount of non-using code, and the using code will be filled up with info["blub"], info["blab"], evil castings, sensless const key-string an still no proper way to show/use the information in an generic and typesafe way sorry but feels like throwing anway the signature conecpt and replace it with an runtime thing ... maybe we should also get rid of function signatures with "Variant[string] parameter" :}

I understand this seems loose, but again, when you want to do custom formatting or i18n this is the way to go. The job of rendering the exception as a string must be outsourced (heh) outside the exception, and Variant[string] is a simple mechanism to do so.

it does not seem - it is special_exception --> special_exception_rendere is the way or else you need to define that clean,compile-time-safe and all the other stuff isn't needed anymore
Feb 20 2012
prev sibling parent reply dennis luehring <dl.soluz gmx.net> writes:
Am 20.02.2012 17:36, schrieb Andrei Alexandrescu:
 I understand this seems loose, but again, when you want to do custom
 formatting or i18n this is the way to go. The job of rendering the
 exception as a string must be outsourced (heh) outside the exception,
 and Variant[string] is a simple mechanism to do so.

 Andrei

it is loose and Variant[string] is a simple mechanism for data transport, but evil in form of compile-time checks, the complete signature of your information is then (per se) only runtime-checked, how to prevent difference in the info[] filling and info[] usage over time? i just mistyped my key, removed a key, used a different, etc... multiply that by the amount of needed Exceptions and their info-key-variants ... hell on earth in the end you will have a bunch of modules wich const-string-keys for the exception creation and rendering, and then i don't see any real difference to a specialize exception
Feb 20 2012
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/20/12 12:11 PM, dennis luehring wrote:
 Am 20.02.2012 17:36, schrieb Andrei Alexandrescu:
 I understand this seems loose, but again, when you want to do custom
 formatting or i18n this is the way to go. The job of rendering the
 exception as a string must be outsourced (heh) outside the exception,
 and Variant[string] is a simple mechanism to do so.

 Andrei

it is loose and Variant[string] is a simple mechanism for data transport, but evil in form of compile-time checks, the complete signature of your information is then (per se) only runtime-checked, how to prevent difference in the info[] filling and info[] usage over time? i just mistyped my key, removed a key, used a different, etc... multiply that by the amount of needed Exceptions and their info-key-variants ... hell on earth

This is not hell on earth, it is the classic type dilution when you need to abide to binary interfaces.
 in the end you will have a bunch of modules wich const-string-keys for
 the exception creation and rendering, and then i don't see any real
 difference to a specialize exception

No, you end up with centralized formatting facilities and smaller code. Andrei
Feb 20 2012
prev sibling next sibling parent reply "Nick Sabalausky" <a a.a> writes:
"Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message 
news:jhtq31$u8q$1 digitalmars.com...
 Again, I think this thread clarified we need the "Variant[string] info;" 
 member however we define the hierarchy.

I disagree. I don't see a need for that.
 Also, I think we can do better than defining the boilerplate constructor 
 (see e.g. https://github.com/D-Programming-Language/phobos/pull/439). It's 
 just a function. Consider:

 // this goes in the stdlib
 void raise(ConcreteException)(string message, Throwable t = null, string f 
 = __FILE__, size_t l = __LINE__)
 {
   auto r = new ConcreteException;
   r.message = message;
   r.file = f;
   r.line = l;
   r.next = t;
   throw r;
 }

 class AcmeException : Exception {}

 Now whenever you want to raise AcmeException, you say 
 raise!AcmeException("message"). Also, raise may accept additional data 
 that fills the Variant[string]. That makes exception definitions 
 one-liners.

So instead of solving a much more general problem with a clean definition-side-only solution, you'd rather tell people "Our langauge has 'throw' like any other modern langauge. Exept that many exceptions need to be used with this 'raise' thing instead of 'throw'." Why do you keep optimizing for API creators at the expense of API users?
Feb 20 2012
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/20/12 10:16 AM, Nick Sabalausky wrote:
 "Andrei Alexandrescu"<SeeWebsiteForEmail erdani.org>  wrote in message
 news:jhtq31$u8q$1 digitalmars.com...
 Again, I think this thread clarified we need the "Variant[string] info;"
 member however we define the hierarchy.

I disagree. I don't see a need for that.

How would we address custom formatting of e.g. error messages? Andrei
Feb 20 2012
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/20/12 11:05 AM, foobar wrote:
 On Monday, 20 February 2012 at 16:37:28 UTC, Andrei Alexandrescu wrote:
 On 2/20/12 10:16 AM, Nick Sabalausky wrote:
 "Andrei Alexandrescu"<SeeWebsiteForEmail erdani.org> wrote in message
 news:jhtq31$u8q$1 digitalmars.com...
 Again, I think this thread clarified we need the "Variant[string]
 info;"
 member however we define the hierarchy.

I disagree. I don't see a need for that.

How would we address custom formatting of e.g. error messages? Andrei

Separation of concerns - exceptions are meant to notify the *developer* of errors. User facing error messages is a separate concern that exceptions should not be responsible for. it's not just outsourcing the translation strings, it's the developer's job to determine what if at all should be done with the exception.

At the end of the day, a human-readable error message must be properly formatted given some exception that signaled an error. So I disagree that exceptions are meant for the developer. They are mechanism, a means to an end.
 Trivial example: My mom enters a misspelled URL (e.g. goggle.com) in her
 browser, she does not know or care what 404 means. instead she gets a
 formated page suggesting her to check her spelling and probably a
 suggestion to try google.com instead.

Sure, and the question is how the message gets created.
 the exception notifies the developer of the error, the developer does
 extra processing (e.g. to suggest similar valid websites) and the user
 get a friendly notification. clearly it doesn't make sense to put all
 this into the exception.

That extra processing must format the message given the information passed by the exception. _Definitely_ it doesn't make sense to put the formatting processing in the exception. That's why shipping the information outside the exception in a generic format is necessary, hence the Variant[string]. Andrei
Feb 20 2012
next sibling parent reply deadalnix <deadalnix gmail.com> writes:
Le 20/02/2012 18:11, Andrei Alexandrescu a écrit :
 On 2/20/12 11:05 AM, foobar wrote:
 On Monday, 20 February 2012 at 16:37:28 UTC, Andrei Alexandrescu wrote:
 On 2/20/12 10:16 AM, Nick Sabalausky wrote:
 "Andrei Alexandrescu"<SeeWebsiteForEmail erdani.org> wrote in message
 news:jhtq31$u8q$1 digitalmars.com...
 Again, I think this thread clarified we need the "Variant[string]
 info;"
 member however we define the hierarchy.

I disagree. I don't see a need for that.

How would we address custom formatting of e.g. error messages? Andrei

Separation of concerns - exceptions are meant to notify the *developer* of errors. User facing error messages is a separate concern that exceptions should not be responsible for. it's not just outsourcing the translation strings, it's the developer's job to determine what if at all should be done with the exception.

At the end of the day, a human-readable error message must be properly formatted given some exception that signaled an error. So I disagree that exceptions are meant for the developer. They are mechanism, a means to an end.
 Trivial example: My mom enters a misspelled URL (e.g. goggle.com) in her
 browser, she does not know or care what 404 means. instead she gets a
 formated page suggesting her to check her spelling and probably a
 suggestion to try google.com instead.

Sure, and the question is how the message gets created.
 the exception notifies the developer of the error, the developer does
 extra processing (e.g. to suggest similar valid websites) and the user
 get a friendly notification. clearly it doesn't make sense to put all
 this into the exception.

That extra processing must format the message given the information passed by the exception. _Definitely_ it doesn't make sense to put the formatting processing in the exception. That's why shipping the information outside the exception in a generic format is necessary, hence the Variant[string]. Andrei

And so variant is the way to go ? Clearly, this is a very strong arguement in favor of typed Exception, that provide usefull information about what went wrong. This is a safe approach. Because this Variant stuff is going to require massive ducktyping of Exceptions, with all possible errors involved. The keys in the Variant[string] will depend on the Exception the dev is facing. This should be avoided and should warn us about the requirement of typed Exceptions.
Feb 20 2012
next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/20/12 11:55 AM, deadalnix wrote:
 Le 20/02/2012 18:11, Andrei Alexandrescu a écrit :
 On 2/20/12 11:05 AM, foobar wrote:
 On Monday, 20 February 2012 at 16:37:28 UTC, Andrei Alexandrescu wrote:
 On 2/20/12 10:16 AM, Nick Sabalausky wrote:
 "Andrei Alexandrescu"<SeeWebsiteForEmail erdani.org> wrote in message
 news:jhtq31$u8q$1 digitalmars.com...
 Again, I think this thread clarified we need the "Variant[string]
 info;"
 member however we define the hierarchy.

I disagree. I don't see a need for that.

How would we address custom formatting of e.g. error messages? Andrei

Separation of concerns - exceptions are meant to notify the *developer* of errors. User facing error messages is a separate concern that exceptions should not be responsible for. it's not just outsourcing the translation strings, it's the developer's job to determine what if at all should be done with the exception.

At the end of the day, a human-readable error message must be properly formatted given some exception that signaled an error. So I disagree that exceptions are meant for the developer. They are mechanism, a means to an end.
 Trivial example: My mom enters a misspelled URL (e.g. goggle.com) in her
 browser, she does not know or care what 404 means. instead she gets a
 formated page suggesting her to check her spelling and probably a
 suggestion to try google.com instead.

Sure, and the question is how the message gets created.
 the exception notifies the developer of the error, the developer does
 extra processing (e.g. to suggest similar valid websites) and the user
 get a friendly notification. clearly it doesn't make sense to put all
 this into the exception.

That extra processing must format the message given the information passed by the exception. _Definitely_ it doesn't make sense to put the formatting processing in the exception. That's why shipping the information outside the exception in a generic format is necessary, hence the Variant[string]. Andrei

And so variant is the way to go ? Clearly, this is a very strong arguement in favor of typed Exception, that provide usefull information about what went wrong. This is a safe approach.

This does not pit typed exceptions against uniform interfaces. The hash simply provides information in a uniform format across exception types. That way, code that needs to extract and format information in a generic manner can be written in one place.
 Because this Variant stuff is going to require massive ducktyping of
 Exceptions, with all possible errors involved. The keys in the
 Variant[string] will depend on the Exception the dev is facing. This
 should be avoided and should warn us about the requirement of typed
 Exceptions.

I agree. Again, this is not one against the other. Andrei
Feb 20 2012
prev sibling next sibling parent dennis luehring <dl.soluz gmx.net> writes:
Am 20.02.2012 19:02, schrieb H. S. Teoh:
 Exactly! Just because you use a Variant doesn't magically free you from
 needing to know what exactly is that exception that you caught in the
 first place. To make any sense of what's stored in the Variant, you
 still have to know what is the exception type, and based on the type
 interpret the Variant. So you end up with:

 	catch(Exception e) {
 		switch(e.type) {
 			case BlahException:
 				handleBlahException(e.data.blah_info);
 				break;
 			case BlehException:
 				handleBlehException(e.data.bleh_info);
 				break;
 			case BluhException:
 				handleBluhException(e.data.bluh_info);
 				break;
 			...
 		}
 	}

Thx Teoh - i also don't know how Andreis Variant[string] would help here in any way
Feb 20 2012
prev sibling next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/20/12 12:02 PM, H. S. Teoh wrote:
 On Mon, Feb 20, 2012 at 06:55:13PM +0100, deadalnix wrote:
 Le 20/02/2012 18:11, Andrei Alexandrescu a crit :

 That extra processing must format the message given the information
 passed by the exception. _Definitely_ it doesn't make sense to put
 the formatting processing in the exception. That's why shipping the
 information outside the exception in a generic format is necessary,
 hence the Variant[string].


 Andrei

And so variant is the way to go ? Clearly, this is a very strong arguement in favor of typed Exception, that provide usefull information about what went wrong. This is a safe approach. Because this Variant stuff is going to require massive ducktyping of Exceptions, with all possible errors involved. The keys in the Variant[string] will depend on the Exception the dev is facing. This should be avoided and should warn us about the requirement of typed Exceptions.

Exactly! Just because you use a Variant doesn't magically free you from needing to know what exactly is that exception that you caught in the first place.

I think this is a confusion. You seem to advocate that exceptions are somehow exempt from class hierarchy design.
 To make any sense of what's stored in the Variant, you
 still have to know what is the exception type, and based on the type
 interpret the Variant. So you end up with:

 	catch(Exception e) {
 		switch(e.type) {
 			case BlahException:
 				handleBlahException(e.data.blah_info);
 				break;
 			case BlehException:
 				handleBlehException(e.data.bleh_info);
 				break;
 			case BluhException:
 				handleBluhException(e.data.bluh_info);
 				break;
 			...
 		}
 	}

 which is exactly the kind of ugliness that class hierarchies were
 invented to solve.

No, you don't end up with that at all. In fact it's your code that "knows exactly" the concrete type that suffers from duplication. I'm sorry, I think this is a complete confusion. Hopefully my post of a minute ago will help it. Andrei
Feb 20 2012
prev sibling next sibling parent reply Juan Manuel Cabo <juanmanuel.cabo gmail.com> writes:
 And so variant is the way to go ?
 
 Clearly, this is a very strong arguement in favor of typed Exception, that
provide usefull information about what went
 wrong. This is a safe approach.
 
 Because this Variant stuff is going to require massive ducktyping of
Exceptions, with all possible errors involved. The
 keys in the Variant[string] will depend on the Exception the dev is facing.
This should be avoided and should warn us
 about the requirement of typed Exceptions.

Some of the things that characterize an exception, their traits, are transversal to any hierachy that you can imagine, now and in the future. You can choose to keep changing a hierarchy, or build in some mechanism to the Exception base class, that will allow you to get your traits without downcasting. Say that at your place of work the boss decides that all exception classes should have a COM error code, or that all exception classes should provide the URL of the request that generated it. You know what will happen? Your boss will require you to derive all your exception classes from COMException or from WebRequestException and then redefine FileNotFound as a subclass of them. So you will have your FileNotFoundException different than the standard library exception. Don't believe me? It happened to .NET... they've got a COMException that wraps any other kind of error during a COM call. --jm
Feb 20 2012
parent reply Juan Manuel Cabo <juanmanuel.cabo gmail.com> writes:
So, if your boss wants the URL of the request that was made
when the standard library threw you a FileNotFoundException,
you can do:


	try {
	      ...
        } catch (Exception ex) {
                //Rethrow the exception with the added detail:
		ex.details["custom_url"] = getenv("URI");
                throw ex;
        }

(don't beat me if URI wasn't the envvar that apache sends for uri, I
don't remember it at the moment).

--jm



On 02/20/2012 04:27 PM, Juan Manuel Cabo wrote:
 And so variant is the way to go ?

 Clearly, this is a very strong arguement in favor of typed Exception, that
provide usefull information about what went
 wrong. This is a safe approach.

 Because this Variant stuff is going to require massive ducktyping of
Exceptions, with all possible errors involved. The
 keys in the Variant[string] will depend on the Exception the dev is facing.
This should be avoided and should warn us
 about the requirement of typed Exceptions.

Some of the things that characterize an exception, their traits, are transversal to any hierachy that you can imagine, now and in the future. You can choose to keep changing a hierarchy, or build in some mechanism to the Exception base class, that will allow you to get your traits without downcasting. Say that at your place of work the boss decides that all exception classes should have a COM error code, or that all exception classes should provide the URL of the request that generated it. You know what will happen? Your boss will require you to derive all your exception classes from COMException or from WebRequestException and then redefine FileNotFound as a subclass of them. So you will have your FileNotFoundException different than the standard library exception. Don't believe me? It happened to .NET... they've got a COMException that wraps any other kind of error during a COM call. --jm

Feb 20 2012
next sibling parent reply deadalnix <deadalnix gmail.com> writes:
Le 20/02/2012 20:32, Juan Manuel Cabo a écrit :
 So, if your boss wants the URL of the request that was made
 when the standard library threw you a FileNotFoundException,
 you can do:


 	try {
 	      ...
          } catch (Exception ex) {
                  //Rethrow the exception with the added detail:
 		ex.details["custom_url"] = getenv("URI");
                  throw ex;
          }

 (don't beat me if URI wasn't the envvar that apache sends for uri, I
 don't remember it at the moment).

 --jm



 On 02/20/2012 04:27 PM, Juan Manuel Cabo wrote:
 And so variant is the way to go ?

 Clearly, this is a very strong arguement in favor of typed Exception, that
provide usefull information about what went
 wrong. This is a safe approach.

 Because this Variant stuff is going to require massive ducktyping of
Exceptions, with all possible errors involved. The
 keys in the Variant[string] will depend on the Exception the dev is facing.
This should be avoided and should warn us
 about the requirement of typed Exceptions.

Some of the things that characterize an exception, their traits, are transversal to any hierachy that you can imagine, now and in the future. You can choose to keep changing a hierarchy, or build in some mechanism to the Exception base class, that will allow you to get your traits without downcasting. Say that at your place of work the boss decides that all exception classes should have a COM error code, or that all exception classes should provide the URL of the request that generated it. You know what will happen? Your boss will require you to derive all your exception classes from COMException or from WebRequestException and then redefine FileNotFound as a subclass of them. So you will have your FileNotFoundException different than the standard library exception. Don't believe me? It happened to .NET... they've got a COMException that wraps any other kind of error during a COM call. --jm


Plus, you are confusing inheritance with composition here. What you want is throw a COMException and link it to the original Exception. You have to consider Exception as a linkedlist, one being the cause of another.
Feb 20 2012
next sibling parent reply Juan Manuel Cabo <juanmanuel.cabo gmail.com> writes:
 That wouldn't work, because you'll erase the stacktrace.

 Plus, you are confusing inheritance with composition here. What you want is
throw a COMException and link it to the
 original Exception. You have to consider Exception as a linkedlist, one being
the cause of another.

You are correct. But it doesn't change the FILE and LINE attributes of the exception. The code below changes the msg of the exception and rethrows it. Please note that the stacktrace is changed as you say. But the: object.Exception t.d(17): another points to the site where it was produced originally: #!/usr/bin/rdmd import std.stdio; void main () { anotherFunc(); } void anotherFunc() { try { writeln("another func"); badfunc(); } catch (Exception ex) { ex.msg = "another"; throw ex; } } void badfunc() { writeln("bad func"); throw new Exception("badfunc"); } another func bad func object.Exception t.d(17): another ---------------- ./t(void t.anotherFunc()+0x2b) [0x42a1c7] ./t(_Dmain+0x9) [0x42a195] ./t(extern (C) int rt.dmain2.main(int, char**).void runMain()+0x17) [0x43c003] ./t(extern (C) int rt.dmain2.main(int, char**).void tryExec(scope void delegate())+0x2a) [0x43b97a] ./t(extern (C) int rt.dmain2.main(int, char**).void runAll()+0x42) [0x43c056] ./t(extern (C) int rt.dmain2.main(int, char**).void tryExec(scope void delegate())+0x2a) [0x43b97a] ./t(main+0xd3) [0x43b90b] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xff) [0x7fc83b628eff] ---------------- On 02/20/2012 04:44 PM, deadalnix wrote:
 Le 20/02/2012 20:32, Juan Manuel Cabo a écrit :
 So, if your boss wants the URL of the request that was made
 when the standard library threw you a FileNotFoundException,
 you can do:


     try {
           ...
          } catch (Exception ex) {
                  //Rethrow the exception with the added detail:
         ex.details["custom_url"] = getenv("URI");
                  throw ex;
          }

 (don't beat me if URI wasn't the envvar that apache sends for uri, I
 don't remember it at the moment).

 --jm



 On 02/20/2012 04:27 PM, Juan Manuel Cabo wrote:
 And so variant is the way to go ?

 Clearly, this is a very strong arguement in favor of typed Exception, that
provide usefull information about what went
 wrong. This is a safe approach.

 Because this Variant stuff is going to require massive ducktyping of
Exceptions, with all possible errors involved. The
 keys in the Variant[string] will depend on the Exception the dev is facing.
This should be avoided and should warn us
 about the requirement of typed Exceptions.

Some of the things that characterize an exception, their traits, are transversal to any hierachy that you can imagine, now and in the future. You can choose to keep changing a hierarchy, or build in some mechanism to the Exception base class, that will allow you to get your traits without downcasting. Say that at your place of work the boss decides that all exception classes should have a COM error code, or that all exception classes should provide the URL of the request that generated it. You know what will happen? Your boss will require you to derive all your exception classes from COMException or from WebRequestException and then redefine FileNotFound as a subclass of them. So you will have your FileNotFoundException different than the standard library exception. Don't believe me? It happened to .NET... they've got a COMException that wraps any other kind of error during a COM call. --jm



Feb 20 2012
parent reply Juan Manuel Cabo <juanmanuel.cabo gmail.com> writes:
About this part:

 What you want is throw a COMException and link it to the original
 Exception. You have to consider Exception as a linkedlist, one
 being the cause of another.


The Variant[string] is an idea to help avoid people creating new kinds of exception types that don't add nothing. I guess that you are proving my point. --jm On 02/20/2012 04:48 PM, Juan Manuel Cabo wrote:
 That wouldn't work, because you'll erase the stacktrace.

 Plus, you are confusing inheritance with composition here. What you want is
throw a COMException and link it to the
 original Exception. You have to consider Exception as a linkedlist, one being
the cause of another.

You are correct. But it doesn't change the FILE and LINE attributes of the exception. The code below changes the msg of the exception and rethrows it. Please note that the stacktrace is changed as you say. But the: object.Exception t.d(17): another points to the site where it was produced originally: #!/usr/bin/rdmd import std.stdio; void main () { anotherFunc(); } void anotherFunc() { try { writeln("another func"); badfunc(); } catch (Exception ex) { ex.msg = "another"; throw ex; } } void badfunc() { writeln("bad func"); throw new Exception("badfunc"); } another func bad func object.Exception t.d(17): another ---------------- ./t(void t.anotherFunc()+0x2b) [0x42a1c7] ./t(_Dmain+0x9) [0x42a195] ./t(extern (C) int rt.dmain2.main(int, char**).void runMain()+0x17) [0x43c003] ./t(extern (C) int rt.dmain2.main(int, char**).void tryExec(scope void delegate())+0x2a) [0x43b97a] ./t(extern (C) int rt.dmain2.main(int, char**).void runAll()+0x42) [0x43c056] ./t(extern (C) int rt.dmain2.main(int, char**).void tryExec(scope void delegate())+0x2a) [0x43b97a] ./t(main+0xd3) [0x43b90b] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xff) [0x7fc83b628eff] ---------------- On 02/20/2012 04:44 PM, deadalnix wrote:
 Le 20/02/2012 20:32, Juan Manuel Cabo a écrit :
 So, if your boss wants the URL of the request that was made
 when the standard library threw you a FileNotFoundException,
 you can do:


     try {
           ...
          } catch (Exception ex) {
                  //Rethrow the exception with the added detail:
         ex.details["custom_url"] = getenv("URI");
                  throw ex;
          }

 (don't beat me if URI wasn't the envvar that apache sends for uri, I
 don't remember it at the moment).

 --jm



 On 02/20/2012 04:27 PM, Juan Manuel Cabo wrote:
 And so variant is the way to go ?

 Clearly, this is a very strong arguement in favor of typed Exception, that
provide usefull information about what went
 wrong. This is a safe approach.

 Because this Variant stuff is going to require massive ducktyping of
Exceptions, with all possible errors involved. The
 keys in the Variant[string] will depend on the Exception the dev is facing.
This should be avoided and should warn us
 about the requirement of typed Exceptions.

Some of the things that characterize an exception, their traits, are transversal to any hierachy that you can imagine, now and in the future. You can choose to keep changing a hierarchy, or build in some mechanism to the Exception base class, that will allow you to get your traits without downcasting. Say that at your place of work the boss decides that all exception classes should have a COM error code, or that all exception classes should provide the URL of the request that generated it. You know what will happen? Your boss will require you to derive all your exception classes from COMException or from WebRequestException and then redefine FileNotFound as a subclass of them. So you will have your FileNotFoundException different than the standard library exception. Don't believe me? It happened to .NET... they've got a COMException that wraps any other kind of error during a COM call. --jm




Feb 20 2012
parent Juan Manuel Cabo <juanmanuel.cabo gmail.com> writes:
I agree to disagree.

But about your argument for efficiency, any check that you do when
examining an exception doesn't need to be lightning fast, after all,
if you are looking at an exception object, it means that the stack
got unwound, and any nano seconds that you spend doing this:

	catch (Exception ex) {
		if ("mycustomfield" in ex) {
			.. do something ..
 		}
	}

which is just an "in" check in an associative array, which might never
have more than 10 elements (so even linear search is appropiate),
is overshadowed by the time that the runtime took to unwind the stack
and serve the exception to your catch block.

--jm


On 02/20/2012 05:05 PM, Sean Kelly wrote:
 On Feb 20, 2012, at 11:54 AM, Juan Manuel Cabo wrote:
 
 About this part:

 What you want is throw a COMException and link it to the original
 Exception. You have to consider Exception as a linkedlist, one
 being the cause of another.


The Variant[string] is an idea to help avoid people creating new kinds of exception types that don't add nothing.

I don't think this makes sense. To effectively use whatever's in the table I pretty much have to know what error I'm handling, and this isn't possible if type information is lost. Unless this determination is moved to a run-time check of some field within the exception, and then I'm making my code that much messier and less efficient by putting in tests of this identifier against a list of constants. Personally, I don't see any use for this table beyond providing context, much like we already have with file, line, etc.

Feb 20 2012
prev sibling parent Juan Manuel Cabo <juanmanuel.cabo gmail.com> writes:
On 02/20/2012 04:44 PM, Sean Kelly wrote:
 On Feb 20, 2012, at 11:44 AM, deadalnix wrote:
 
 That wouldn't work, because you'll erase the stacktrace.

It wouldn't be difficult to not overwrite a stack trace if one already exists on throw.

That would be very nice!! Java doesn't have a stacktrace reset problem: I tried the following in java. You can see by the output that the stacktrace of the exception object is preserved (I didn't leave blank lines on purpouse so you can count the line number shown in the output unequivocally): public class bla { public static void main(String[] args) throws Exception { anotherfunc(); } public static void anotherfunc() throws Exception { try { System.out.println("another func"); badfunc(); } catch (Exception ex) { //rethrow the same exception: throw ex; } } public static void badfunc() throws Exception { System.out.println("bad func"); throw new Exception("badfunc"); } } another func bad func Exception in thread "main" java.lang.Exception: badfunc at bla.badfunc(bla.java:16) at bla.anotherfunc(bla.java:8) at bla.main(bla.java:3) --jm
Feb 20 2012
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/20/12 1:32 PM, Juan Manuel Cabo wrote:
 So, if your boss wants the URL of the request that was made
 when the standard library threw you a FileNotFoundException,
 you can do:


 	try {
 	      ...
          } catch (Exception ex) {
                  //Rethrow the exception with the added detail:
 		ex.details["custom_url"] = getenv("URI");
                  throw ex;
          }

That's a very interesting angle! Andrei
Feb 20 2012
next sibling parent Juan Manuel Cabo <juanmanuel.cabo gmail.com> writes:
 That's a very interesting angle!

 Andrei

Thanks!! The main point I was making is, that otherwise, a user would be forced to create a new exception kind and chain the original exception to it. Now... what about the catch blocks defined upstream in the call tree? So this solves that then. The code upstream can still make a catch (FileNotFound) that obviously distinguishes FileNotFound from other exception types. Otherwise, if all one does is ie: COMExceptions that chain other exceptions, the distinction is lost, and rethrows mess everything. --jm On 02/20/2012 05:49 PM, Andrei Alexandrescu wrote:
 On 2/20/12 1:32 PM, Juan Manuel Cabo wrote:
 So, if your boss wants the URL of the request that was made
 when the standard library threw you a FileNotFoundException,
 you can do:


     try {
           ...
          } catch (Exception ex) {
                  //Rethrow the exception with the added detail:
         ex.details["custom_url"] = getenv("URI");
                  throw ex;
          }

That's a very interesting angle! Andrei

Feb 20 2012
prev sibling next sibling parent deadalnix <deadalnix gmail.com> writes:
Le 20/02/2012 21:49, Andrei Alexandrescu a écrit :
 On 2/20/12 1:32 PM, Juan Manuel Cabo wrote:
 So, if your boss wants the URL of the request that was made
 when the standard library threw you a FileNotFoundException,
 you can do:


 try {
 ...
 } catch (Exception ex) {
 //Rethrow the exception with the added detail:
 ex.details["custom_url"] = getenv("URI");
 throw ex;
 }

That's a very interesting angle! Andrei

That is terrible. If ones begin to do this, then you cannot rely on what you'll find in the Exception infos. And if people start to do that before any code is writen, It is very bad sign.
Feb 20 2012
prev sibling parent dennis luehring <dl.soluz gmx.net> writes:
Am 20.02.2012 21:49, schrieb Andrei Alexandrescu:
 On 2/20/12 1:32 PM, Juan Manuel Cabo wrote:
  So, if your boss wants the URL of the request that was made
  when the standard library threw you a FileNotFoundException,
  you can do:


  	try {
  	...
           } catch (Exception ex) {
                   //Rethrow the exception with the added detail:
  		ex.details["custom_url"] = getenv("URI");
                   throw ex;
           }

That's a very interesting angle! Andrei

that is sooooo bad - fuck signatures just add information ... now im able to just catch always Exception (sweet) ... but my damn handle code needs to know the real interface ... crippled into details["custom_url"]... so in the end we will come to if( detail[x] is in there) if( detail[y] is in there ) if ( detail[z] is in threre ) shit i agree 100% - its easy for just giving out the information, but awfull for handling the exception
Feb 20 2012
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/20/12 1:13 PM, Sean Kelly wrote:
 On Feb 20, 2012, at 9:55 AM, deadalnix wrote:
 And so variant is the way to go ?

 Clearly, this is a very strong arguement in favor of typed
 Exception, that provide usefull information about what went wrong.
 This is a safe approach.

 Because this Variant stuff is going to require massive ducktyping
 of Exceptions, with all possible errors involved. The keys in the
 Variant[string] will depend on the Exception the dev is facing.
 This should be avoided and should warn us about the requirement of
 typed Exceptions.

I think its debatable whether a Variant[string] or string[string] is ideal here, but either way I think the point of the table is for localized error messages. I wouldn't expect any data relevant for filtering the exception within the table.

Exactly. The need for Variant instead of string is mainly to distinguish numeric info from string info when doing things like singular vs. plural or Arabic numeral rendering vs. textual ("one", "two" etc). Andrei
Feb 20 2012
parent reply "Nick Sabalausky" <a a.a> writes:
"Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message 
news:jhub5b$1rj5$2 digitalmars.com...
 On 2/20/12 1:13 PM, Sean Kelly wrote:
 but either way I think the point of the table is for
 localized error messages.  I wouldn't expect any data relevant for
 filtering the exception within the table.

Exactly. The need for Variant instead of string is mainly to distinguish numeric info from string info when doing things like singular vs. plural or Arabic numeral rendering vs. textual ("one", "two" etc).

So like Jacob mentioned and you denied, you *are* advocating putting at least some i18n into exceptions.
Feb 20 2012
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/20/12 5:51 PM, Nick Sabalausky wrote:
 "Andrei Alexandrescu"<SeeWebsiteForEmail erdani.org>  wrote in message
 news:jhub5b$1rj5$2 digitalmars.com...
 On 2/20/12 1:13 PM, Sean Kelly wrote:
 but either way I think the point of the table is for
 localized error messages.  I wouldn't expect any data relevant for
 filtering the exception within the table.

Exactly. The need for Variant instead of string is mainly to distinguish numeric info from string info when doing things like singular vs. plural or Arabic numeral rendering vs. textual ("one", "two" etc).

So like Jacob mentioned and you denied, you *are* advocating putting at least some i18n into exceptions.

No. I am simply enabling a variety of applications, with i18n as an obvious one. Encoding numbers as strings may force decoding later. It's just unnecessary. Andrei
Feb 20 2012
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/20/12 11:44 AM, foobar wrote:
 This extra processing is orthogonal to the exception. the same exception
 can be logged to a file, processed (per above example) and generate
 graphical notification to the user, etc. The exception contains the
 information pertaining only to what went wrong. the rest is not part of
 this discussion.

Exactly. I don't see how a disagreement follows from here. So isn't it reasonable to design the exception such that it can offer information pertaining to what went wrong, in a uniform manner?
 The exact same exception in the example would also be thrown on a
 mistyped URL in an application that tries to scrape some info from a
 website for further processing. The error is still the same - the url is
 incorrect but different use cases handle it differently. In the former
 example I might call to a i18n lib (someone already mentioned gettext)
 while in the latter I'll call a logging library with the the mistyped
 url (for statistics' sake).
 in the first I use the url to find a closest match, in the second I want
 to log said requested url. Both handled by *other* mechanisms.
 in both cases the exception needs a url field and in both cases I have
 no need for the Variant[string] map.

The Variant[string] map saves a lot of duplication whenever you want to format a human-readable string (which is a common activity with exceptions). It transforms this (I'm too lazy to write code anew by hand, so I'll paste Jonathan's): 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; } into this: try getopt(args, ...) catch(Exception e) { stderr.writeln(stringTemplate(typeid(e).toString(), e.info)); return -1; } The stringTemplate function loads the formatting template from a table indexed on typeid(e).toString() and formats it with the info. It's simple factorization. Andrei
Feb 20 2012
next sibling parent reply deadalnix <deadalnix gmail.com> writes:
Le 20/02/2012 19:42, H. S. Teoh a crit :
 On Mon, Feb 20, 2012 at 01:23:04PM -0500, Jonathan M Davis wrote:
 On Monday, February 20, 2012 11:57:07 Andrei Alexandrescu wrote:

 Exactly. I don't see how a disagreement follows from here. So isn't
 it reasonable to design the exception such that it can offer
 information pertaining to what went wrong, in a uniform manner?


 I don't see how you could possibly make that uniform. It's very
 non-uniform by its very nature. The handling _needs_ to be
 non-uniform.

I think there's a misunderstanding here. What Andrei is trying to achieve is something like this: class Exception : Error { Variant[string] data; string formatMsg(LocaleInfo li) { return formatLocale(li, msg, data); } } class GetOptException : Exception { this() { data["..."] = ...; ... } } I contend that using Variant here is not necessary. You can easily do this instead: class Exception : Error { string formatMsg(LocaleInfo li) { auto members = typeid(this).getMembers(null); string msg; foreach (member; members) { if (member.name.startsWith("info")) { ... //format this field } } return msg; } } class GetOptException : Exception { string infoOption; // this gets picked up by formatMsg int internalData; // this is ignored // No need to declare anything else except ctor to set // the above fields. } This allows for a much cleaner, type-checked access to infoOption, should the catching code know how to deal with GetOptException specifically. T

This is bad design IMO. Exception are here to provide information about what is wrong. It has nothing to do with internationalisation whatsoever.
Feb 20 2012
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/20/12 12:50 PM, deadalnix wrote:
 This is bad design IMO. Exception are here to provide information about
 what is wrong. It has nothing to do with internationalisation whatsoever.

100% agree. Andrei
Feb 20 2012
parent reply Jacob Carlborg <doob me.com> writes:
On 2012-02-20 19:47, Andrei Alexandrescu wrote:
 On 2/20/12 12:50 PM, deadalnix wrote:
 This is bad design IMO. Exception are here to provide information about
 what is wrong. It has nothing to do with internationalisation whatsoever.

100% agree. Andrei

It sure seems you want to but internationalization into exceptions. -- /Jacob Carlborg
Feb 20 2012
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/20/12 2:45 PM, Jacob Carlborg wrote:
 On 2012-02-20 19:47, Andrei Alexandrescu wrote:
 On 2/20/12 12:50 PM, deadalnix wrote:
 This is bad design IMO. Exception are here to provide information about
 what is wrong. It has nothing to do with internationalisation
 whatsoever.

100% agree. Andrei

It sure seems you want to but internationalization into exceptions.

How did you infer that? I don't want to put internationalization into exceptions /at all/. Andrei
Feb 20 2012
parent reply "Nick Sabalausky" <a a.a> writes:
"Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message 
news:jhucla$1tuf$7 digitalmars.com...
 On 2/20/12 2:45 PM, Jacob Carlborg wrote:
 On 2012-02-20 19:47, Andrei Alexandrescu wrote:
 On 2/20/12 12:50 PM, deadalnix wrote:
 This is bad design IMO. Exception are here to provide information about
 what is wrong. It has nothing to do with internationalisation
 whatsoever.

100% agree. Andrei

It sure seems you want to but internationalization into exceptions.

How did you infer that? I don't want to put internationalization into exceptions /at all/.

You've suggested adding "Variant[string] info" to Exception for the sake of i18n. I think that's what he's referring to. You *could* argue that's not technically i18n, but so far i18n seems to be the only real use-case for it (although even that much has been disputed in light of reflection).
Feb 20 2012
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/20/12 5:46 PM, Nick Sabalausky wrote:
 "Andrei Alexandrescu"<SeeWebsiteForEmail erdani.org>  wrote in message
 news:jhucla$1tuf$7 digitalmars.com...
 On 2/20/12 2:45 PM, Jacob Carlborg wrote:
 On 2012-02-20 19:47, Andrei Alexandrescu wrote:
 On 2/20/12 12:50 PM, deadalnix wrote:
 This is bad design IMO. Exception are here to provide information about
 what is wrong. It has nothing to do with internationalisation
 whatsoever.

100% agree. Andrei

It sure seems you want to but internationalization into exceptions.

How did you infer that? I don't want to put internationalization into exceptions /at all/.

You've suggested adding "Variant[string] info" to Exception for the sake of i18n. I think that's what he's referring to. You *could* argue that's not technically i18n, but so far i18n seems to be the only real use-case for it (although even that much has been disputed in light of reflection).

All formatting and rendering of the exception information is helped. And I think that's great because that's the most common activity one would do with exceptions. Andrei
Feb 20 2012
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/20/12 6:51 PM, H. S. Teoh wrote:
 On Mon, Feb 20, 2012 at 06:19:32PM -0600, Andrei Alexandrescu wrote:
 On 2/20/12 5:46 PM, Nick Sabalausky wrote:

 You've suggested adding "Variant[string] info" to Exception for the
 sake of i18n. I think that's what he's referring to. You *could*
 argue that's not technically i18n, but so far i18n seems to be the
 only real use-case for it (although even that much has been disputed
 in light of reflection).

All formatting and rendering of the exception information is helped. And I think that's great because that's the most common activity one would do with exceptions.

Then, obviously, different development environments are leading to vastly different conclusions, because I can't for the life of me imagine that the most common activity you would do with an exception is to print it.

Different strokes for different folks I'd say. I think I could claim a little authority on diversity. I've worked on an extremely diverse range of applications; in fact I've made a point to acquire broad specialization. In virtually all systems I've worked on, rendering meaningful error messages out of exception information has been a major concern, and in most of them the problem has been poorly addressed. It is very exciting to have an opportunity to improve on the state of affairs. Andrei
Feb 20 2012
parent reply Jacob Carlborg <doob me.com> writes:
On 2012-02-21 03:34, Andrei Alexandrescu wrote:
 On 2/20/12 6:51 PM, H. S. Teoh wrote:
 On Mon, Feb 20, 2012 at 06:19:32PM -0600, Andrei Alexandrescu wrote:
 On 2/20/12 5:46 PM, Nick Sabalausky wrote:

 You've suggested adding "Variant[string] info" to Exception for the
 sake of i18n. I think that's what he's referring to. You *could*
 argue that's not technically i18n, but so far i18n seems to be the
 only real use-case for it (although even that much has been disputed
 in light of reflection).

All formatting and rendering of the exception information is helped. And I think that's great because that's the most common activity one would do with exceptions.

Then, obviously, different development environments are leading to vastly different conclusions, because I can't for the life of me imagine that the most common activity you would do with an exception is to print it.

Different strokes for different folks I'd say. I think I could claim a little authority on diversity. I've worked on an extremely diverse range of applications; in fact I've made a point to acquire broad specialization. In virtually all systems I've worked on, rendering meaningful error messages out of exception information has been a major concern, and in most of them the problem has been poorly addressed. It is very exciting to have an opportunity to improve on the state of affairs. Andrei

I think the correct way of handling this is provide enough information in the exception so a message can be built where the exception is caught. It might happen the you want to catch the same exception in different parts of the code and build different messages. -- /Jacob Carlborg
Feb 21 2012
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/21/12 5:11 AM, Jacob Carlborg wrote:
 On 2012-02-21 03:34, Andrei Alexandrescu wrote:
 I think the correct way of handling this is provide enough information
 in the exception so a message can be built where the exception is
 caught.

Quite so. I'd add "using a unified interface so reusable code can work with heterogeneous concrete exceptions".
 It might happen the you want to catch the same exception in
 different parts of the code and build different messages.

Yah, that's different string tables. Andrei
Feb 21 2012
parent Jacob Carlborg <doob me.com> writes:
On 2012-02-21 15:16, Andrei Alexandrescu wrote:
 On 2/21/12 5:11 AM, Jacob Carlborg wrote:
 On 2012-02-21 03:34, Andrei Alexandrescu wrote:
 I think the correct way of handling this is provide enough information
 in the exception so a message can be built where the exception is
 caught.

Quite so. I'd add "using a unified interface so reusable code can work with heterogeneous concrete exceptions".
 It might happen the you want to catch the same exception in
 different parts of the code and build different messages.

Yah, that's different string tables.

So you would set the string table where the exception is caught? -- /Jacob Carlborg
Feb 21 2012
prev sibling parent Jacob Carlborg <doob me.com> writes:
On 2012-02-21 01:51, H. S. Teoh wrote:
 On Mon, Feb 20, 2012 at 06:19:32PM -0600, Andrei Alexandrescu wrote:
 On 2/20/12 5:46 PM, Nick Sabalausky wrote:

 You've suggested adding "Variant[string] info" to Exception for the
 sake of i18n. I think that's what he's referring to. You *could*
 argue that's not technically i18n, but so far i18n seems to be the
 only real use-case for it (although even that much has been disputed
 in light of reflection).

All formatting and rendering of the exception information is helped. And I think that's great because that's the most common activity one would do with exceptions.

Then, obviously, different development environments are leading to vastly different conclusions, because I can't for the life of me imagine that the most common activity you would do with an exception is to print it. That may be true for a certain class of applications, say command-line tools like compilers and stuff. But that's absolutely not the case at my work project. There, exceptions are usually handled by code and almost never output anywhere, and even when they are, for the most part they just print a simple string literal right from the source code into a debug log channel. Only a tiny percentage of exceptions actually make it all the way to being formatted into a nice localized string displayed to the user. For the more common case of printing to the debug log, the current Exception facilities (string msg) are more than good enough (since the intended audience is the developers themselves). The i18n stuff is handled by catching the relevant exception by type and looking up the localized string at the catcher level. Any additional info inside the exception object itself isn't used for formatting the localized string anyway, since the user has no idea what it all means. T

I completely agree. -- /Jacob Carlborg
Feb 21 2012
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/20/12 12:23 PM, Jonathan M Davis wrote:
 On Monday, February 20, 2012 11:57:07 Andrei Alexandrescu wrote:
 On 2/20/12 11:44 AM, foobar wrote:
 This extra processing is orthogonal to the exception. the same exception
 can be logged to a file, processed (per above example) and generate
 graphical notification to the user, etc. The exception contains the
 information pertaining only to what went wrong. the rest is not part of
 this discussion.

Exactly. I don't see how a disagreement follows from here. So isn't it reasonable to design the exception such that it can offer information pertaining to what went wrong, in a uniform manner?

In most cases, that's not even vaguely possible.

Please hear me out.
 An exception for a socket
 would probably include an IP and port in its additonal information. An
 exception for a file operation would include the file name that it tried to
 operate on. An exception for malformed unicode would include information on
 the string and the bad character. An exception from parsing XML would give you
 information about which part of the XML had the problem and what the problem
 was. All of these situations are completely different. Using a Variant is the
 equivalent of using a void* and casting to the actual information based no the
 type of the exception or on a type code in the exception. How is that better
 than simply having an actual exception hierarchy with each of the derived
 exceptions having fields specific to their circumstances?

It is better because you get to factor out all formatting in one place. You combine a string template that has references to symbolic names, with a context giving the symbolic names. Otherwise the code doing so would need to duplicate that code all over the place - exactly as you did in your example.
 I contend that it's
 far worse. It's throwing away type information simply in an effort to
 genericize. Genericity can be very useful, but it can also be over-applied.

No, it's not throwing away anything. If you want to handle the actual exception, sure, you get typed access to the state. All I want is push one interface method up.
 I don't see how you could possibly make that uniform. It's very non-uniform by
 its very nature. The handling _needs_ to be non-uniform.

No, it doesn't, and if you just hear me out for a second you'll see how. It's very simple. Formatting is all about pairing symbolic names with data. A format engine would load a string template that uses symbolic name such as "file", "line", "IP", "port", and a context which contains bindings of these names to values. That way formatting does not need to know what an IP means etc.
 The stringTemplate function loads the formatting template from a table
 indexed on typeid(e).toString() and formats it with the info. It's
 simple factorization.

That only works in this particular case, because it happens that the code is just figuring out how to translate the exception into an error message without trying to recover. If you want to do anything fancier (like suggest the flag that the user probably meant), you need those extra fields in the exceptions.

Sure. Again, this is not advocating replacement of exception hierarchies with tables!
 Also, much of the time, the application is going to want to print its own
 error messages, not what library that its calling used.

Sure. Then the application has its own string templates. That was the purpose of the whole thing. Exceptions give bindings, applications give formatting templates.
 And frequently, the
 application can give much better messages, because it knows what it's doing,
 not just the operation that it tried and failed. Exceptions are for
 _developers_, not users, and in general, exception messages should not be
 printed to users.

Yup. Yup.
 And moving any messages that exceptions have out of the exception itself is
 _bad_ for debugging.

Sure. Print typeid(e) if you so want, and handle explicit exception specifically as needed.
 The exception should contain its own information, not
 have it externalized for internationalization purposes.

And that information should be accessible in a uniform manner.
 If its in the
 exception itself, you can see it in the debugger. That doesn't work anywhere
 near as well when you externalize its stuff.

 I gave the getopt example, because we were talking about getopt, and it was
 easy to write.

Code like that is present in very many catch blocks.
 It shows one way that the fields in the exception could be
 useful.

To me it shows how code can be duplicated without necessity.
 But it's an abnormality in that it's whole purpose is to allow you to
 figure out how to best give an error message to the user. Many, many other
 exceptions need to be processed by the program without necessarily informing
 the user of anything. So, getopt was probably a bad example. It just was an
 easy one to give.

You may want to give another one. Andrei
Feb 20 2012
next sibling parent reply Juan Manuel Cabo <juanmanuel.cabo gmail.com> writes:
 ...
 Sure. Again, this is not advocating replacement of exception hierarchies with
tables!
 ... 
 
 Andrei
 

I think that the case of rethrowing an exception with added detail is the worst enemy of clean Exception hierarchies. The idea of Variant[string] remedies that case without creating a new exception class just for the added fields. If that case is solved, then the tipical need for creating new exception types that don't really aid selecting them for catching and recovery is solved too. --jm
Feb 20 2012
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/20/12 2:31 PM, Juan Manuel Cabo wrote:
 ...
 Sure. Again, this is not advocating replacement of exception hierarchies with
tables!
 ...

 Andrei

I think that the case of rethrowing an exception with added detail is the worst enemy of clean Exception hierarchies. The idea of Variant[string] remedies that case without creating a new exception class just for the added fields. If that case is solved, then the tipical need for creating new exception types that don't really aid selecting them for catching and recovery is solved too.

Good point. One thing somewhat particular to D, you can always throw a new exception linked to the current one. Andrei
Feb 20 2012
parent reply deadalnix <deadalnix gmail.com> writes:
Le 20/02/2012 22:05, Andrei Alexandrescu a écrit :
 On 2/20/12 2:31 PM, Juan Manuel Cabo wrote:
 ...
 Sure. Again, this is not advocating replacement of exception
 hierarchies with tables!
 ...

 Andrei

I think that the case of rethrowing an exception with added detail is the worst enemy of clean Exception hierarchies. The idea of Variant[string] remedies that case without creating a new exception class just for the added fields. If that case is solved, then the tipical need for creating new exception types that don't really aid selecting them for catching and recovery is solved too.

Good point. One thing somewhat particular to D, you can always throw a new exception linked to the current one.

And this is great design. We should continue in that direction.
 Andrei

Feb 20 2012
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/20/12 3:14 PM, deadalnix wrote:
 Le 20/02/2012 22:05, Andrei Alexandrescu a écrit :
 On 2/20/12 2:31 PM, Juan Manuel Cabo wrote:
 ...
 Sure. Again, this is not advocating replacement of exception
 hierarchies with tables!
 ...

 Andrei

I think that the case of rethrowing an exception with added detail is the worst enemy of clean Exception hierarchies. The idea of Variant[string] remedies that case without creating a new exception class just for the added fields. If that case is solved, then the tipical need for creating new exception types that don't really aid selecting them for catching and recovery is solved too.

Good point. One thing somewhat particular to D, you can always throw a new exception linked to the current one.

And this is great design.

I'm not so sure. It's more like an incompetent design by someone who obviously knew nothing about exceptions.
 We should continue in that direction.

That has its own disadvantages, particularly the proliferation of types simply to embody state, but it does work. Andrei
Feb 20 2012
prev sibling next sibling parent reply Juan Manuel Cabo <juanmanuel.cabo gmail.com> writes:
I like one golden rule that I have:

 "You should only create a new exception type, if it makes sense to write
  a  catch(MyNewShinyType ex){}  "

other reasons for creating a new exception class I don't consider them valid
(but that's just me!).
This is because, exception types (in my head) are only a way to distinguish
whether to select them or let them go when I write a catch(), and to
help the catch recover.  Now, if I can't distinguish the *what* of the error,
I cannot recover well. The *cause* of the error goes inside the exception
object, not encoded in the type. Other details of the error go inside of
the exception object, not encoded in the type name.

So all I care about is the *what* of the error, so that it will fall in
the correct catch statement. Other criteria obscures that.

The Variant[string] helps keep the hierarchy clean. The hierachy should
tell the *what* of the error so that I can pick one when writing a catch block.

--jm


On 02/20/2012 05:51 PM, Jonathan M Davis wrote:
 On Monday, February 20, 2012 17:31:28 Juan Manuel Cabo wrote:
 ...
 Sure. Again, this is not advocating replacement of exception hierarchies
 with tables! ...

 Andrei

I think that the case of rethrowing an exception with added detail is the worst enemy of clean Exception hierarchies. The idea of Variant[string] remedies that case without creating a new exception class just for the added fields. If that case is solved, then the tipical need for creating new exception types that don't really aid selecting them for catching and recovery is solved too.

Having derived exceptions with additional information is a _huge_ boon, and I contend that it's vasty better with variant, which would be highly error prone, because it's not properly statically checked. Changes to what's put in the variant could kill code at runtime - code which by its very definiton is not supposed to be the normal code path, so you're less likely to actually run into the problem before you ship your product. Whereas with the information in actual member variables, if they get changed, you get a compilation error, and you know that you have to fix your code. Rethrowing is a separate issue. And in many cases, the correct thing to do is to chain exceptions. You catch one, do something with it, and then you throw a new one which took the first one as an argument. Then you get both. That functionality is already built into Exception. - Jonathan M Davis

Feb 20 2012
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/20/12 3:06 PM, Juan Manuel Cabo wrote:
 I like one golden rule that I have:

   "You should only create a new exception type, if it makes sense to write
    a  catch(MyNewShinyType ex){}  "

The problem with this is that anyone may come with an example created on the spot where they would, actually, want to catch a specific exception. It's very hard to make non-existential proofs.
 other reasons for creating a new exception class I don't consider them valid
 (but that's just me!).
 This is because, exception types (in my head) are only a way to distinguish
 whether to select them or let them go when I write a catch(), and to
 help the catch recover.  Now, if I can't distinguish the *what* of the error,
 I cannot recover well. The *cause* of the error goes inside the exception
 object, not encoded in the type. Other details of the error go inside of
 the exception object, not encoded in the type name.

 So all I care about is the *what* of the error, so that it will fall in
 the correct catch statement. Other criteria obscures that.

 The Variant[string] helps keep the hierarchy clean. The hierachy should
 tell the *what* of the error so that I can pick one when writing a catch block.

Don't forget that you always have the option of creating a type on the spot. void fun() { if (oops) { class MyException : Exception { string reasonForOops; ... } throw new MyException; // RTTI will find reasonForOops } } But after seeing a variety of pros and cons, I tend to agree that the Variant[string] is more attractive. Andrei
Feb 20 2012
prev sibling next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/20/12 2:51 PM, Jonathan M Davis wrote:
 On Monday, February 20, 2012 17:31:28 Juan Manuel Cabo wrote:
 ...
 Sure. Again, this is not advocating replacement of exception hierarchies
 with tables! ...

 Andrei

I think that the case of rethrowing an exception with added detail is the worst enemy of clean Exception hierarchies. The idea of Variant[string] remedies that case without creating a new exception class just for the added fields. If that case is solved, then the tipical need for creating new exception types that don't really aid selecting them for catching and recovery is solved too.

Having derived exceptions with additional information is a _huge_ boon, and I contend that it's vasty better with variant, which would be highly error prone, because it's not properly statically checked. Changes to what's put in the variant could kill code at runtime - code which by its very definiton is not supposed to be the normal code path, so you're less likely to actually run into the problem before you ship your product. Whereas with the information in actual member variables, if they get changed, you get a compilation error, and you know that you have to fix your code. Rethrowing is a separate issue. And in many cases, the correct thing to do is to chain exceptions. You catch one, do something with it, and then you throw a new one which took the first one as an argument. Then you get both. That functionality is already built into Exception.

I think we'd be quick to dismiss Juan's point. There are many execution contexts for any given exception, and summarily saying that we'll handle them with distinct types doesn't sound exactly scalable. I've long wanted to be able to plant context information in a scope, that was automatically added to any exception possibly leaving that scope. Andrei
Feb 20 2012
prev sibling next sibling parent reply Juan Manuel Cabo <juanmanuel.cabo gmail.com> writes:
Yeah.. that is a problem! :-) Thanks for liking the idea, now we can
talk about the fine details!!

One way is to not let the user direct access to the associative array,
but wrap the e.info["MyDetail"] call in a nothrow function, such as
e.info("MyDetail"), and an e.hasInfo("MyDetail"), and of course:
e.addInfo("MyDetail", value) and e.allInfoNames() or something.

The nothrow function would return an empty value if not found (I fear
that it might not be of the same Variant subtype as the Variant value
was intended when present).

--jm


On 02/20/2012 05:53 PM, H. S. Teoh wrote:
 On Mon, Feb 20, 2012 at 05:31:28PM -0300, Juan Manuel Cabo wrote:
 ...
 Sure. Again, this is not advocating replacement of exception hierarchies with
tables!
 ... 

 Andrei

I think that the case of rethrowing an exception with added detail is the worst enemy of clean Exception hierarchies.

Hmm. This is a valid point. Sometimes you want to add contextual details to an exception in order to provide the final catching code with more useful information. Otherwise you may end up with a chain of mostly redundant exception classes: class UTFError : Exception {...} class LexUTFError : LexUTFError { int line, col; ... } class ConfigFileParseError : LexUTFError { string cfgfile_name; } auto decodeUTF(...) { ... throw new UTFError; } auto configLexer(...) { try { ... decodeUTF(...); } catch(UTFError e) { throw new LexUTFError(...); } } auto configParser(...) { try { ... configLexer(...); } catch(LexUTFError e) { throw new ConfigFileParseError(...); } }
 The idea of Variant[string] remedies that case without creating a new
 exception class just for the added fields. If that case is solved,
 then the tipical need for creating new exception types that don't
 really aid selecting them for catching and recovery is solved too.

However, I still hesitate about using Variant[string]. How would you address the following problem: // Module A class MyException : Exception { this() { info["mydetail"] = ...; } } // Module B auto func() { try { ... } catch(MyException e) { if (e.info["mydetail"] == ...) { ... } } } If module A's maintainer renames "mydetail" to "detail", then module B will still compile with no problem, but now e.info["mydetail"] doesn't exist and will cause a runtime error at worst. At best, the catch block won't be able to recover from the error as it did before, because now it can't find the info it was looking for. If "mydetail" had been a field stored in MyException, then module B would get a compile-time error, and the problem can be fixed immediately, instead of going unnoticed until it blows up at the customer's production server. T

Feb 20 2012
next sibling parent Juan Manuel Cabo <juanmanuel.cabo gmail.com> writes:
I forgot to add, that you could define standard details names
as string constants, and even document them in the string constant
definition.

--jm


On 02/20/2012 06:11 PM, Juan Manuel Cabo wrote:
 Yeah.. that is a problem! :-) Thanks for liking the idea, now we can
 talk about the fine details!!
 
 One way is to not let the user direct access to the associative array,
 but wrap the e.info["MyDetail"] call in a nothrow function, such as
 e.info("MyDetail"), and an e.hasInfo("MyDetail"), and of course:
 e.addInfo("MyDetail", value) and e.allInfoNames() or something.
 
 The nothrow function would return an empty value if not found (I fear
 that it might not be of the same Variant subtype as the Variant value
 was intended when present).
 
 --jm
 
 
 On 02/20/2012 05:53 PM, H. S. Teoh wrote:
 On Mon, Feb 20, 2012 at 05:31:28PM -0300, Juan Manuel Cabo wrote:
 ...
 Sure. Again, this is not advocating replacement of exception hierarchies with
tables!
 ... 

 Andrei

I think that the case of rethrowing an exception with added detail is the worst enemy of clean Exception hierarchies.

Hmm. This is a valid point. Sometimes you want to add contextual details to an exception in order to provide the final catching code with more useful information. Otherwise you may end up with a chain of mostly redundant exception classes: class UTFError : Exception {...} class LexUTFError : LexUTFError { int line, col; ... } class ConfigFileParseError : LexUTFError { string cfgfile_name; } auto decodeUTF(...) { ... throw new UTFError; } auto configLexer(...) { try { ... decodeUTF(...); } catch(UTFError e) { throw new LexUTFError(...); } } auto configParser(...) { try { ... configLexer(...); } catch(LexUTFError e) { throw new ConfigFileParseError(...); } }
 The idea of Variant[string] remedies that case without creating a new
 exception class just for the added fields. If that case is solved,
 then the tipical need for creating new exception types that don't
 really aid selecting them for catching and recovery is solved too.

However, I still hesitate about using Variant[string]. How would you address the following problem: // Module A class MyException : Exception { this() { info["mydetail"] = ...; } } // Module B auto func() { try { ... } catch(MyException e) { if (e.info["mydetail"] == ...) { ... } } } If module A's maintainer renames "mydetail" to "detail", then module B will still compile with no problem, but now e.info["mydetail"] doesn't exist and will cause a runtime error at worst. At best, the catch block won't be able to recover from the error as it did before, because now it can't find the info it was looking for. If "mydetail" had been a field stored in MyException, then module B would get a compile-time error, and the problem can be fixed immediately, instead of going unnoticed until it blows up at the customer's production server. T


Feb 20 2012
prev sibling parent dennis luehring <dl.soluz gmx.net> writes:
Am 20.02.2012 22:11, schrieb Juan Manuel Cabo:
 Yeah.. that is a problem! :-) Thanks for liking the idea, now we can
 talk about the fine details!!

 One way is to not let the user direct access to the associative array,
 but wrap the e.info["MyDetail"] call in a nothrow function, such as
 e.info("MyDetail"), and an e.hasInfo("MyDetail"), and of course:
 e.addInfo("MyDetail", value) and e.allInfoNames() or something.

if the key is missing i can't recover/control the error - now all my data is gone - a silly wraper which catches the key-does-not-exists-lets-just-return-null will not help here
Feb 21 2012
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/20/12 2:53 PM, H. S. Teoh wrote:
 On Mon, Feb 20, 2012 at 05:31:28PM -0300, Juan Manuel Cabo wrote:
 ...
 Sure. Again, this is not advocating replacement of exception hierarchies with
tables!
 ...

 Andrei

I think that the case of rethrowing an exception with added detail is the worst enemy of clean Exception hierarchies.

Hmm. This is a valid point. Sometimes you want to add contextual details to an exception in order to provide the final catching code with more useful information. Otherwise you may end up with a chain of mostly redundant exception classes:

Yah, it does look we need to better investigate the Variant[string] info() primitive. Andrei
Feb 20 2012
next sibling parent reply Juan Manuel Cabo <juanmanuel.cabo gmail.com> writes:
 I still don't like the idea of using Variant[string], though.
 
 (1) It doesn't allow compile-time type checking. This is a big minus, in
 my book.

When you need compile-time type checking, define a variable in your class. Just make sure that you are creating that new exception class for a good reason. When a user needs to add a variable to the exception, he can add it without putting your exception class chained in a new type of exception, that will hide your class from being selected by upstream catch blocks in the call tree.
 
 (2) It's overly flexible. Anyone along the call stack can insert
 (hopefully NOT delete!!) additional data into the Exception object, as
 the stack is unwound. 

As is currently the case. Did you know that anyone can overwrite any field of the exception and rethrow it? Such as msg field and so on?
 By the time it gets to the final catch() block,
 you cannot guarantee a particular field you depend on will be defined.

If you want to guarantee it, then use a plain old variable for that piece of data. I just would like a way to add data to an exception without creating a new type. If I create a new exception type for the wrong reasons, I'm polluting the exception hierarchy. If I pollute the exception hierarchy, then catching by exception type becomes convolluted. It becomes difficult for an exception to fall in the right catch. And I think that is a worst problem than not being sure if a piece of data is in the extra info. Data is data. Types are types. Exceptions should be typed the best way possible that will allow me to select them and fall in the right catch block. And that is the *what* of the error, not the data of the error.
 Say if your call graph looks something like this:
 
 	main()
 	  +--func1()
 	      +--func2()
 	      |   +--helperFunc()
 	      |   +--func3()
 	      |       +--helperFunc()
 	      +--func4()
 	          +--helperFunc()
 
 Suppose helperFunc() throws HelperException, which func1's catch block
 specifically wants to handle. Suppose func2() adds an attribute called
 "lineNumber" to its catch block, which then rethrows the exception, and
 func3() adds an attribute called "colNumber".
 
 Now how should you write func1()'s catch block? You will get all
 HelperException's thrown, but you've no idea from which part of the call
 graph it originates. If it comes from func3(), then you have both
 "lineNumber" and "colNumber". If it comes before you reach func3(), then
 only "lineNumber" is defined. If it comes from func4(), then neither is
 present.
 
 So your catch block degenerates into a morass of if-then-else
 conditions. And then what do you do if you're depending on a particular
 field to be set, but it's not? Rethrow the exception? Then you have the
 stack trace reset problem.
 
 Whereas if HelperException always has the the same fields, the catch
 block is very straightforward: just catch HelperException, and you are
 guaranteed you have all the info you need.
 
 Then if func3() wants to add more info, create a new exception derived
 from HelperException, and add the field there. Then in func1(), add a
 new catch block that catches the new exception, and makes use of the new
 field.
 
 This does introduce a lot of little exception classes, which you could
 argue is class bloat, but I don't see how the Variant[string] method is
 necessarily superior. It comes with its own set of (IMHO quite nasty)
 problems.
 
 
 T
 

HAhaha, it sometimes feel as though people are afraid that the Variant[string] idea is to never use plain old variables and never use exception subclasses. :-) On the contrary, the idea is so that plain old variables and exception subclasses can be created for the right reasons, and to remove cases where they need to be created for the wrong reasons. --jm
Feb 20 2012
next sibling parent Juan Manuel Cabo <juanmanuel.cabo gmail.com> writes:
 Do you have actual use
 cases that requires adding data to exceptions? Without concrete examples
 we're just arguing about hypotheticals.

I posted a few hypothetical cases during in the thread, but this is one long megathread! I'm not facing an urgent need right now for the Variant[string] capability. I just realized that the majority of twisted exception hierarchies are grown trying to encode transversal traits of the exceptions in their types names. The exception types should encode only one thing: the error *what*. Now, for real use cases for the Variant[string], you just have to look around and they are everywhere. For instance: a C library wrapper, which gets the library errors encoded as some error code and throws them as exceptions. Shouldn't the library throw a FileNotFoundException when that's the error, instead of throwing a LibraryException that has the error code in a field? So the correct thing to do is: after a library call, the wrapper checks the last error code number with a switch statement, and deciding which standard exception type to throw (defaulting to whatever you like if the error code doesn't map to a standard D exception). Then you add the error code to the Variant[string]. That way, exception types can be standard. So, to keep D exception types standard reusable and respected by future code, you must follow the Open-Closed design principle (nicest principle of OO design ever). --jm On 02/20/2012 07:57 PM, H. S. Teoh wrote:
 On Mon, Feb 20, 2012 at 07:44:30PM -0300, Juan Manuel Cabo wrote:
 [...]
 (2) It's overly flexible. Anyone along the call stack can insert
 (hopefully NOT delete!!) additional data into the Exception object, as
 the stack is unwound. 

As is currently the case. Did you know that anyone can overwrite any field of the exception and rethrow it? Such as msg field and so on?

This is an implementation bug. Exceptions should always be const in the catch block. I believe this issue has been filed, and will eventually be fixed.
 By the time it gets to the final catch() block, you cannot guarantee
 a particular field you depend on will be defined.

If you want to guarantee it, then use a plain old variable for that piece of data. I just would like a way to add data to an exception without creating a new type. If I create a new exception type for the wrong reasons, I'm polluting the exception hierarchy.

Point taken. So I think what we should have is *both* data stored in fields in Exception subclasses, and some kind of way to attach auxilliary data to the exception. Say with Variant[string], or whatever way you prefer. But Variant[string] should not be used for *everything*. That only leads to problems. But then, it limits the usefulness of Variant[string], because then you can't just pass it to the i18n formatter, since now some fields are static but they may need to be part of the formatted message. So we haven't really solved anything, we just added a new feature to Exception which I'm not sure how useful it is. Do you have actual use cases that requires adding data to exceptions? Without concrete examples we're just arguing about hypotheticals. T

Feb 20 2012
prev sibling next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/20/12 4:57 PM, H. S. Teoh wrote:
 So I think what we should have is *both* data stored in fields in
 Exception subclasses, and some kind of way to attach auxilliary data to
 the exception. Say with Variant[string], or whatever way you prefer.

 But Variant[string] should not be used for *everything*. That only leads
 to problems. But then, it limits the usefulness of Variant[string],
 because then you can't just pass it to the i18n formatter, since now
 some fields are static but they may need to be part of the formatted
 message.

Great. I'll plant the interface and submit it for destruction.
 So we haven't really solved anything, we just added a new feature to
 Exception which I'm not sure how useful it is. Do you have actual use
 cases that requires adding data to exceptions? Without concrete examples
 we're just arguing about hypotheticals.

I'm seeing plenty of that in our code base at work. They use exception wrapping in conjunction with a fair amount of contortions because the Variant[string] approach has not been designed. Andrei
Feb 20 2012
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/20/12 4:44 PM, Juan Manuel Cabo wrote:
 HAhaha, it sometimes feel as though people are afraid that the Variant[string]
 idea is to never use plain old variables and never use exception subclasses.
:-)

 On the contrary, the idea is so that plain old variables and exception
subclasses
 can be created for the right reasons, and to remove cases where they need
 to be created for the wrong reasons.

Yah, I think there's a lot of confusion and consequent apprehension regarding this. Thanks for attempting to clarify things. Andrei
Feb 20 2012
parent reply deadalnix <deadalnix gmail.com> writes:
Le 21/02/2012 00:23, Andrei Alexandrescu a crit :
 On 2/20/12 4:44 PM, Juan Manuel Cabo wrote:
 HAhaha, it sometimes feel as though people are afraid that the
 Variant[string]
 idea is to never use plain old variables and never use exception
 subclasses. :-)

 On the contrary, the idea is so that plain old variables and exception
 subclasses
 can be created for the right reasons, and to remove cases where they need
 to be created for the wrong reasons.

Yah, I think there's a lot of confusion and consequent apprehension regarding this. Thanks for attempting to clarify things. Andrei

So it doesn't help. Dulb subclasses of Exceptions are done mostly to be able to catch them. To avoid useless subclasses, we need a more precise way to catch Exception than the type only. This Variant[string] doesn't help.
Feb 21 2012
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/21/12 2:47 PM, H. S. Teoh wrote:
 On Tue, Feb 21, 2012 at 09:32:35PM +0100, deadalnix wrote:
 [...]
 So it doesn't help. Dulb subclasses of Exceptions are done mostly to
 be able to catch them. To avoid useless subclasses, we need a more
 precise way to catch Exception than the type only.

This is a good point. Has anybody even considered, *why* does catch match by type? Is there a good reason for that? Or is it just inherited from the rah rah days of OOP where "everything is an object", so since exception is part of everything, exception is an object, therefore we can just catch by object type?

I think a reason is that exceptions should be able to transport an arbitrary amount of information. That means heterogeneous types at least. Then, as you discussed in the beginning of the thread, placing types in a hierarchy allows client code to catch several exceptions sharing a common super type, theory that was well understood. A more debatable aspect of exceptions is the first-match rule in catch blocks. All of OOP goes with best match, except here. But then all code is together so the effect is small.
 From all the heated debate in this thread, it's clear that exceptions

contention and what amounts to workarounds and hacks. So I'm going to throw (har har) this very crazy and wild idea out there and let's see if it's viable: What if instead of catching by class, we catch by attribute matching? So instead of writing: try { ... } catch(SomeExceptionType e) { ... } catch(SomeOtherExceptionType e) { ... } catch(YetAnotherSillyException e) { ... } we write: try { ... } catch(e: exists!e.filename&& e.failedOp is File.open) { // something } catch(e: e.is_transient&& e.num_retries< 5) { // something else } // And why should we even need an exception object in the first // place? catch(time()>= oldtime+5000) { // This thing's been running for way too long, time to // do something drastic } Flamesuit on! ;-)

The only problem I see here is ascribing e a type. Andrei
Feb 21 2012
parent deadalnix <deadalnix gmail.com> writes:
Le 21/02/2012 22:15, Andrei Alexandrescu a crit :
 What if instead of catching by class, we catch by attribute matching?
 So instead of writing:

 try { ... }
 catch(SomeExceptionType e) { ... }
 catch(SomeOtherExceptionType e) { ... }
 catch(YetAnotherSillyException e) { ... }

 we write:

 try { ... }
 catch(e: exists!e.filename&& e.failedOp is File.open) {
 // something
 }
 catch(e: e.is_transient&& e.num_retries< 5) {
 // something else
 }
 // And why should we even need an exception object in the first
 // place?
 catch(time()>= oldtime+5000) {
 // This thing's been running for way too long, time to
 // do something drastic
 }

 Flamesuit on! ;-)

The only problem I see here is ascribing e a type.

That is why I proposed here : http://forum.dlang.org/thread/jhos0l$102t$1 digitalmars.com?page=25#post-jhtus4:2416tp:2 1:40digitalmars.com some alternative syntax. To make it short : try { // Stuff . . . } catch(Exception e) if(e.transient == false) { // Handling . . . } For the longer explanation, see the link.
Feb 21 2012
prev sibling next sibling parent "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Monday, February 20, 2012 17:23:42 Andrei Alexandrescu wrote:
 On 2/20/12 4:44 PM, Juan Manuel Cabo wrote:
 HAhaha, it sometimes feel as though people are afraid that the
 Variant[string] idea is to never use plain old variables and never use
 exception subclasses. :-)
 
 On the contrary, the idea is so that plain old variables and exception
 subclasses can be created for the right reasons, and to remove cases
 where they need to be created for the wrong reasons.

Yah, I think there's a lot of confusion and consequent apprehension regarding this. Thanks for attempting to clarify things.

I think that as long as it makes sense to catch an exception and handle it based on a new type, any information associated with that exception should be member variables in that exception. But if you have extra information which could be useful but where you wouldn't really want to catch a new type, then it makes some sense to use the hashtable. However, I don't recall ever having personally run into a situation where that sort of need arose. - Jonathan M Davis
Feb 20 2012
prev sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2012-02-20 23:44, Juan Manuel Cabo wrote:
 I still don't like the idea of using Variant[string], though.

 (1) It doesn't allow compile-time type checking. This is a big minus, in
 my book.

When you need compile-time type checking, define a variable in your class. Just make sure that you are creating that new exception class for a good reason. When a user needs to add a variable to the exception, he can add it without putting your exception class chained in a new type of exception, that will hide your class from being selected by upstream catch blocks in the call tree.
 (2) It's overly flexible. Anyone along the call stack can insert
 (hopefully NOT delete!!) additional data into the Exception object, as
 the stack is unwound.

As is currently the case. Did you know that anyone can overwrite any field of the exception and rethrow it? Such as msg field and so on?

No one says the fields need to be public instance variables. You could take the arguments in the constructor and only have getters. -- /Jacob Carlborg
Feb 21 2012
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/21/12 6:36 AM, Jacob Carlborg wrote:
 On 2012-02-20 23:44, Juan Manuel Cabo wrote:
 I still don't like the idea of using Variant[string], though.

 (1) It doesn't allow compile-time type checking. This is a big minus, in
 my book.

When you need compile-time type checking, define a variable in your class. Just make sure that you are creating that new exception class for a good reason. When a user needs to add a variable to the exception, he can add it without putting your exception class chained in a new type of exception, that will hide your class from being selected by upstream catch blocks in the call tree.
 (2) It's overly flexible. Anyone along the call stack can insert
 (hopefully NOT delete!!) additional data into the Exception object, as
 the stack is unwound.

As is currently the case. Did you know that anyone can overwrite any field of the exception and rethrow it? Such as msg field and so on?

No one says the fields need to be public instance variables. You could take the arguments in the constructor and only have getters.

I think he meant to say things have been like that for a while and there's no blood in the streets. Andrei
Feb 21 2012
parent reply Juan Manuel Cabo <juanmanuel.cabo gmail.com> writes:
 I think he meant to say things have been like that for a while and there's no
blood in the streets.

That's exactly what I meant. And even if one makes those fields private, anyone can take a pointer to the class or void[] or whatever and do a mess. (Java isn't exepmpt, you can do a mess with reflection there). So there is a minimum of trust that we put on APIs and code that we call downstream. The same trust that one puts, to begin with, when one expects that an exception will be thrown when an error happens. Ruby and PHP are based on a lot of trust for instance! Having the advantages of staticlly typed language doesn't mean that one must turn the advantages into disadvantages and start hammering screws because we love hammers. --jm On 02/21/2012 11:20 AM, Andrei Alexandrescu wrote:
 On 2/21/12 6:36 AM, Jacob Carlborg wrote:
 On 2012-02-20 23:44, Juan Manuel Cabo wrote:
 I still don't like the idea of using Variant[string], though.

 (1) It doesn't allow compile-time type checking. This is a big minus, in
 my book.

When you need compile-time type checking, define a variable in your class. Just make sure that you are creating that new exception class for a good reason. When a user needs to add a variable to the exception, he can add it without putting your exception class chained in a new type of exception, that will hide your class from being selected by upstream catch blocks in the call tree.
 (2) It's overly flexible. Anyone along the call stack can insert
 (hopefully NOT delete!!) additional data into the Exception object, as
 the stack is unwound.

As is currently the case. Did you know that anyone can overwrite any field of the exception and rethrow it? Such as msg field and so on?

No one says the fields need to be public instance variables. You could take the arguments in the constructor and only have getters.

I think he meant to say things have been like that for a while and there's no blood in the streets. Andrei

Feb 21 2012
parent Juan Manuel Cabo <juanmanuel.cabo gmail.com> writes:
Never mind modifying fields of the exception at some intermediate catch place.
Someone could even catch the exception and not rethrow it.
So: do some trusting. Life gets easier :-)

--jm


On 02/21/2012 12:46 PM, Juan Manuel Cabo wrote:
 I think he meant to say things have been like that for a while and there's no
blood in the streets.

That's exactly what I meant.

Feb 21 2012
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/20/12 4:04 PM, H. S. Teoh wrote:
 On Mon, Feb 20, 2012 at 03:12:08PM -0600, Andrei Alexandrescu wrote:
 I still don't like the idea of using Variant[string], though.

I don't like it, either. I mean not "like" like. It's an approach suggested by necessity.
 (1) It doesn't allow compile-time type checking. This is a big minus, in
 my book.

In mine, too. Literally. We're on the same boat.
 (2) It's overly flexible. Anyone along the call stack can insert
 (hopefully NOT delete!!) additional data into the Exception object, as
 the stack is unwound.

But that's a plus. It means the approach scales up to any number of control flows, of which there are combinatorially many. Defining one type for each... well you wouldn't "like" that, either.
 By the time it gets to the final catch() block,
 you cannot guarantee a particular field you depend on will be defined.

Indeed. If you depend on anything you'd want to catch the specific type.
 Say if your call graph looks something like this:

 	main()
 	  +--func1()
 	      +--func2()
 	      |   +--helperFunc()
 	      |   +--func3()
 	      |       +--helperFunc()
 	      +--func4()
 	          +--helperFunc()

 Suppose helperFunc() throws HelperException, which func1's catch block
 specifically wants to handle. Suppose func2() adds an attribute called
 "lineNumber" to its catch block, which then rethrows the exception, and
 func3() adds an attribute called "colNumber".

 Now how should you write func1()'s catch block? You will get all
 HelperException's thrown, but you've no idea from which part of the call
 graph it originates.
 If it comes from func3(), then you have both
 "lineNumber" and "colNumber". If it comes before you reach func3(), then
 only "lineNumber" is defined. If it comes from func4(), then neither is
 present.

Exactly. So you suggest adding one type for each possible control flow? Are you sure this scales beyond a toy example?
 So your catch block degenerates into a morass of if-then-else
 conditions.

No, precisely on the contrary. You catch blockS degenerate into a morass of catch (This) { ... } catch (That) { ... } catch (TheOther) { ... }. That is fine if the code in different "..." does very different things, but it's a terrible approach if all do the same thing, such as formatting. That shouldn't make anyone feel better than using a morass of if/else. The code with Variant[string] does not need combinatorial testing if it wants to do a uniform action (such as formatting). It handles formatting uniformly, and if it wants to look for one particular field it inserts a test.
 And then what do you do if you're depending on a particular
 field to be set, but it's not? Rethrow the exception? Then you have the
 stack trace reset problem.

Don't forget that Variant[string] does not preclude distinct exception types. It's not one or the other.
 Whereas if HelperException always has the the same fields, the catch
 block is very straightforward: just catch HelperException, and you are
 guaranteed you have all the info you need.

HelperException can definitely be there. It can only help if there's additional information associated with it.
 Then if func3() wants to add more info, create a new exception derived
 from HelperException, and add the field there. Then in func1(), add a
 new catch block that catches the new exception, and makes use of the new
 field.

They call that non-scalable code bloat.
 This does introduce a lot of little exception classes, which you could
 argue is class bloat, but I don't see how the Variant[string] method is
 necessarily superior. It comes with its own set of (IMHO quite nasty)
 problems.

Variant[string] is not superior because it doesn't compete against anything. It's a simple addition to the primitives available to the base Exception class. Andrei
Feb 20 2012
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/20/12 6:25 PM, H. S. Teoh wrote:
 On Mon, Feb 20, 2012 at 05:15:17PM -0600, Andrei Alexandrescu wrote:
 Formatting should use class reflection. We already discussed that, and
 we already agreed that was the superior approach.

Jose's argument convinced me otherwise. I retract my agreement.
 When you're catching a specific exception, you're catching it with the
 view that it will contain precisely information X, Y, Z that you need to
 recover from the problem. If you don't need to catch something, then
 don't put the catch block there.

That's extremely rare in my experience, and only present in toy examples that contain a ton of "..." magic.
 The problem with using Variant[string] is that everything gets lumped
 into one Exception object, and there's no way to only catch the
 Exception that happens to have variables "p", "q", and "r" set in the
 Variant[string].

No. You are still free to define as many exception classes as you deem necessary. Please let's not pit the hash against the hierarchy again. This is confusing the role of the two. Consider the hash an interface function you want to occasionally implement.
 You have to catch an exception type that includes all
 sorts of combinations of data in Variant[string], then manually do tests
 to single out the exception you want, and rethrow the rest. That's where
 the ugliness comes from.

Yah, that would suck, but it's not at all what I say.
 [...]
 The code with Variant[string] does not need combinatorial testing if
 it wants to do a uniform action (such as formatting). It handles
 formatting uniformly, and if it wants to look for one particular field
 it inserts a test.

Again, we've already agreed class reflection is the proper solution to this one.

Agreement rescinded. Sorry! Jose's point was just too good, and reminded me of a pain point I so long had with exception, I'd got used to it as a fact of life.
 And then what do you do if you're depending on a particular field to
 be set, but it's not? Rethrow the exception? Then you have the stack
 trace reset problem.

Don't forget that Variant[string] does not preclude distinct exception types. It's not one or the other.

Agreed. But it shouldn't be the be-all and end-all of data passed in exceptions. If anything, it should only be rarely used, with most exception classes using static fields to convey relevant information.

And to perfectly help code duplication everywhere.
 I can see the usefulness of using Variant[string] as a way of
 "decorating" exceptions with "extra attributes", but it shouldn't be the
 primary way of conveying information from the throw site to the catch
 site.

 As for iterating over the information in the most derived class, for
 formatting, etc., class reflection is the way to go.

Agreement rescinded as far as exceptions go. That doesn't make reflection any less necessary btw. It just reflects the dynamism of exception paths.
 We shouldn't be
 using Variant[string] for this, because there's another problem
 associated with it: suppose MyException sometimes gets "extra_info1" and
 "extra_info2" tacked onto it, on its way up the call stack, and
 sometimes not. Now what should the catcher do?

Use a central loop to render the information.
 How do you format this
 exception?

With a string template as has been discussed.
 Should the format string include extra_info1 and extra_info2,
 or not? If it doesn't, what's the use of this extra info? If it does,
 what happens if these fields are missing?

Decision belongs to the string template engine.
 This is what I mean by not being able to depend on whether some data is
 there. Ultimately, to do anything useful with the info in the object,
 you need to know what's there.

No. You do realize you are positioning yourself straight against every OO principle there is? The entire OO world is predicated on the assumption you _can_ do useful stuff with an object WITHOUT knowing what's there.
 Preferably, the object's type will tell
 you exactly what's there, then you do a simple map from type to list of
 available attributes (e.g., map exception type to format string with
 known, static list of attributes). But if the type doesn't guarantee
 what data will be present, then your code becomes vastly more complex,
 you have to deal with potentially all possible combinations of what's
 there and what isn't.

I think you are misunderstanding the mechanisms involved. There is no combinatorial code, just simple iteration and such. Dealing with distinct exceptions with distinct code IS combinatorial, repetitive, and non-scalable.
 Instead of a single format string for a single
 exception type, you now have a combinatorial explosion of format strings
 for every possible combination of missing/present fields in the
 exception object.

No, I'm afraid there's a sizeable misunderstanding here. Andrei
Feb 20 2012
next sibling parent reply Juan Manuel Cabo <juanmanuel.cabo gmail.com> writes:
 Jose's argument convinced me otherwise. I retract my agreement.

 No, I'm afraid there's a sizeable misunderstanding here.


 Andrei

Hahah, yeah, I think there is a sizeable misunderstanding: unless you are referring to another guy with a spanish name in this thread, (which I haven't found). My name is Juan Manuel (people call me: Juanma, JM or Juan, but Jos is a first! I wouldn't mind John which is the 'translation' of my name). Back to the nasty argument. I think that the example that everyone wants is this one. If anyone solves this one without Variant[string] then it's a better solution than Variant[string]. (I repaste it from an above reply I gave): [..] For instance: a C library wrapper, which gets the library errors encoded as some error code and throws them as exceptions. Shouldn't the library throw a FileNotFoundException when that's the error, instead of throwing a LibraryException that has the error code in a field? So the correct thing to do is: after a library call, the wrapper checks the last error code number with a switch statement, and deciding which standard exception type to throw (defaulting to whatever you like if the error code doesn't map to a standard D exception). Then you add the error code to the Variant[string], and any other extra info. That way, exception types can be standard. So, to keep D exception types standard reusable and respected by future code, you must follow the Open-Closed design principle (nicest principle of OO design ever). [..] Adding the Variant[string] is considered applying the great Open-Closed Design Principle: -Open for reuse. -Closed for modification. http://www.objectmentor.com/resources/articles/ocp.pdf --jm On 02/20/2012 09:38 PM, Andrei Alexandrescu wrote:
 On 2/20/12 6:25 PM, H. S. Teoh wrote:
 On Mon, Feb 20, 2012 at 05:15:17PM -0600, Andrei Alexandrescu wrote:
 Formatting should use class reflection. We already discussed that, and
 we already agreed that was the superior approach.

Jose's argument convinced me otherwise. I retract my agreement.
 When you're catching a specific exception, you're catching it with the
 view that it will contain precisely information X, Y, Z that you need to
 recover from the problem. If you don't need to catch something, then
 don't put the catch block there.

That's extremely rare in my experience, and only present in toy examples that contain a ton of "..." magic.
 The problem with using Variant[string] is that everything gets lumped
 into one Exception object, and there's no way to only catch the
 Exception that happens to have variables "p", "q", and "r" set in the
 Variant[string].

No. You are still free to define as many exception classes as you deem necessary. Please let's not pit the hash against the hierarchy again. This is confusing the role of the two. Consider the hash an interface function you want to occasionally implement.
 You have to catch an exception type that includes all
 sorts of combinations of data in Variant[string], then manually do tests
 to single out the exception you want, and rethrow the rest. That's where
 the ugliness comes from.

Yah, that would suck, but it's not at all what I say.
 [...]
 The code with Variant[string] does not need combinatorial testing if
 it wants to do a uniform action (such as formatting). It handles
 formatting uniformly, and if it wants to look for one particular field
 it inserts a test.

Again, we've already agreed class reflection is the proper solution to this one.

Agreement rescinded. Sorry! Jose's point was just too good, and reminded me of a pain point I so long had with exception, I'd got used to it as a fact of life.
 And then what do you do if you're depending on a particular field to
 be set, but it's not? Rethrow the exception? Then you have the stack
 trace reset problem.

Don't forget that Variant[string] does not preclude distinct exception types. It's not one or the other.

Agreed. But it shouldn't be the be-all and end-all of data passed in exceptions. If anything, it should only be rarely used, with most exception classes using static fields to convey relevant information.

And to perfectly help code duplication everywhere.
 I can see the usefulness of using Variant[string] as a way of
 "decorating" exceptions with "extra attributes", but it shouldn't be the
 primary way of conveying information from the throw site to the catch
 site.

 As for iterating over the information in the most derived class, for
 formatting, etc., class reflection is the way to go.

Agreement rescinded as far as exceptions go. That doesn't make reflection any less necessary btw. It just reflects the dynamism of exception paths.
 We shouldn't be
 using Variant[string] for this, because there's another problem
 associated with it: suppose MyException sometimes gets "extra_info1" and
 "extra_info2" tacked onto it, on its way up the call stack, and
 sometimes not. Now what should the catcher do?

Use a central loop to render the information.
 How do you format this
 exception?

With a string template as has been discussed.
 Should the format string include extra_info1 and extra_info2,
 or not? If it doesn't, what's the use of this extra info? If it does,
 what happens if these fields are missing?

Decision belongs to the string template engine.
 This is what I mean by not being able to depend on whether some data is
 there. Ultimately, to do anything useful with the info in the object,
 you need to know what's there.

No. You do realize you are positioning yourself straight against every OO principle there is? The entire OO world is predicated on the assumption you _can_ do useful stuff with an object WITHOUT knowing what's there.
 Preferably, the object's type will tell
 you exactly what's there, then you do a simple map from type to list of
 available attributes (e.g., map exception type to format string with
 known, static list of attributes). But if the type doesn't guarantee
 what data will be present, then your code becomes vastly more complex,
 you have to deal with potentially all possible combinations of what's
 there and what isn't.

I think you are misunderstanding the mechanisms involved. There is no combinatorial code, just simple iteration and such. Dealing with distinct exceptions with distinct code IS combinatorial, repetitive, and non-scalable.
 Instead of a single format string for a single
 exception type, you now have a combinatorial explosion of format strings
 for every possible combination of missing/present fields in the
 exception object.

No, I'm afraid there's a sizeable misunderstanding here. Andrei

Feb 20 2012
next sibling parent reply Juan Manuel Cabo <juanmanuel.cabo gmail.com> writes:
oops, sorry!! I just saw a post by someone named Jose. My thousand apollogies!!

On 02/20/2012 10:01 PM, Juan Manuel Cabo wrote:
 Jose's argument convinced me otherwise. I retract my agreement.

 No, I'm afraid there's a sizeable misunderstanding here.


 Andrei

Hahah, yeah, I think there is a sizeable misunderstanding: unless you are referring to another guy with a spanish name in this thread, (which I haven't found). My name is Juan Manuel (people call me: Juanma, JM or Juan, but Jos is a first! I wouldn't mind John which is the 'translation' of my name). Back to the nasty argument. I think that the example that everyone wants is this one. If anyone solves this one without Variant[string] then it's a better solution than Variant[string]. (I repaste it from an above reply I gave): [..] For instance: a C library wrapper, which gets the library errors encoded as some error code and throws them as exceptions. Shouldn't the library throw a FileNotFoundException when that's the error, instead of throwing a LibraryException that has the error code in a field? So the correct thing to do is: after a library call, the wrapper checks the last error code number with a switch statement, and deciding which standard exception type to throw (defaulting to whatever you like if the error code doesn't map to a standard D exception). Then you add the error code to the Variant[string], and any other extra info. That way, exception types can be standard. So, to keep D exception types standard reusable and respected by future code, you must follow the Open-Closed design principle (nicest principle of OO design ever). [..] Adding the Variant[string] is considered applying the great Open-Closed Design Principle: -Open for reuse. -Closed for modification. http://www.objectmentor.com/resources/articles/ocp.pdf --jm On 02/20/2012 09:38 PM, Andrei Alexandrescu wrote:
 On 2/20/12 6:25 PM, H. S. Teoh wrote:
 On Mon, Feb 20, 2012 at 05:15:17PM -0600, Andrei Alexandrescu wrote:
 Formatting should use class reflection. We already discussed that, and
 we already agreed that was the superior approach.

Jose's argument convinced me otherwise. I retract my agreement.
 When you're catching a specific exception, you're catching it with the
 view that it will contain precisely information X, Y, Z that you need to
 recover from the problem. If you don't need to catch something, then
 don't put the catch block there.

That's extremely rare in my experience, and only present in toy examples that contain a ton of "..." magic.
 The problem with using Variant[string] is that everything gets lumped
 into one Exception object, and there's no way to only catch the
 Exception that happens to have variables "p", "q", and "r" set in the
 Variant[string].

No. You are still free to define as many exception classes as you deem necessary. Please let's not pit the hash against the hierarchy again. This is confusing the role of the two. Consider the hash an interface function you want to occasionally implement.
 You have to catch an exception type that includes all
 sorts of combinations of data in Variant[string], then manually do tests
 to single out the exception you want, and rethrow the rest. That's where
 the ugliness comes from.

Yah, that would suck, but it's not at all what I say.
 [...]
 The code with Variant[string] does not need combinatorial testing if
 it wants to do a uniform action (such as formatting). It handles
 formatting uniformly, and if it wants to look for one particular field
 it inserts a test.

Again, we've already agreed class reflection is the proper solution to this one.

Agreement rescinded. Sorry! Jose's point was just too good, and reminded me of a pain point I so long had with exception, I'd got used to it as a fact of life.
 And then what do you do if you're depending on a particular field to
 be set, but it's not? Rethrow the exception? Then you have the stack
 trace reset problem.

Don't forget that Variant[string] does not preclude distinct exception types. It's not one or the other.

Agreed. But it shouldn't be the be-all and end-all of data passed in exceptions. If anything, it should only be rarely used, with most exception classes using static fields to convey relevant information.

And to perfectly help code duplication everywhere.
 I can see the usefulness of using Variant[string] as a way of
 "decorating" exceptions with "extra attributes", but it shouldn't be the
 primary way of conveying information from the throw site to the catch
 site.

 As for iterating over the information in the most derived class, for
 formatting, etc., class reflection is the way to go.

Agreement rescinded as far as exceptions go. That doesn't make reflection any less necessary btw. It just reflects the dynamism of exception paths.
 We shouldn't be
 using Variant[string] for this, because there's another problem
 associated with it: suppose MyException sometimes gets "extra_info1" and
 "extra_info2" tacked onto it, on its way up the call stack, and
 sometimes not. Now what should the catcher do?

Use a central loop to render the information.
 How do you format this
 exception?

With a string template as has been discussed.
 Should the format string include extra_info1 and extra_info2,
 or not? If it doesn't, what's the use of this extra info? If it does,
 what happens if these fields are missing?

Decision belongs to the string template engine.
 This is what I mean by not being able to depend on whether some data is
 there. Ultimately, to do anything useful with the info in the object,
 you need to know what's there.

No. You do realize you are positioning yourself straight against every OO principle there is? The entire OO world is predicated on the assumption you _can_ do useful stuff with an object WITHOUT knowing what's there.
 Preferably, the object's type will tell
 you exactly what's there, then you do a simple map from type to list of
 available attributes (e.g., map exception type to format string with
 known, static list of attributes). But if the type doesn't guarantee
 what data will be present, then your code becomes vastly more complex,
 you have to deal with potentially all possible combinations of what's
 there and what isn't.

I think you are misunderstanding the mechanisms involved. There is no combinatorial code, just simple iteration and such. Dealing with distinct exceptions with distinct code IS combinatorial, repetitive, and non-scalable.
 Instead of a single format string for a single
 exception type, you now have a combinatorial explosion of format strings
 for every possible combination of missing/present fields in the
 exception object.

No, I'm afraid there's a sizeable misunderstanding here. Andrei


Feb 20 2012
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/20/12 7:02 PM, Juan Manuel Cabo wrote:
 oops, sorry!! I just saw a post by someone named Jose. My thousand apollogies!!

I got confused. It was your argument I meant to refer to - adding info to the exception in flight. Andrei
Feb 20 2012
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/21/12 4:48 AM, foobar wrote:
 On Tuesday, 21 February 2012 at 02:23:58 UTC, Andrei Alexandrescu wrote:
 On 2/20/12 7:02 PM, Juan Manuel Cabo wrote:
 oops, sorry!! I just saw a post by someone named Jose. My thousand
 apollogies!!

I got confused. It was your argument I meant to refer to - adding info to the exception in flight. Andrei

I'd implement this along these lines: class WithErrorCode(E) : E { int errorCode; this(Args)(int err, Args args) { this.errorCode = err; super(args); } } and add this wrapper where relevant. e.g. replace: throw new FileNotFoundException(...); with something like: throw new WithErrorCode!FileNotFoundException(-1, ...); This is a localized change that doesn't affect all uses of exceptions and it remains type-safe. surely this is a better solution than the hash table?

The two approaches don't compete as one is static and the other is dynamic. For example, how does one add contextual information "While opening table in {database}" to the current exception of type WithErrorCode!FileNotFoundException? Andrei
Feb 21 2012
next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/21/12 8:38 AM, foobar wrote:
 On Tuesday, 21 February 2012 at 14:13:55 UTC, Andrei Alexandrescu wrote:
 On 2/21/12 4:48 AM, foobar wrote:
 On Tuesday, 21 February 2012 at 02:23:58 UTC, Andrei Alexandrescu wrote:
 On 2/20/12 7:02 PM, Juan Manuel Cabo wrote:
 oops, sorry!! I just saw a post by someone named Jose. My thousand
 apollogies!!

I got confused. It was your argument I meant to refer to - adding info to the exception in flight. Andrei

I'd implement this along these lines: class WithErrorCode(E) : E { int errorCode; this(Args)(int err, Args args) { this.errorCode = err; super(args); } } and add this wrapper where relevant. e.g. replace: throw new FileNotFoundException(...); with something like: throw new WithErrorCode!FileNotFoundException(-1, ...); This is a localized change that doesn't affect all uses of exceptions and it remains type-safe. surely this is a better solution than the hash table?

The two approaches don't compete as one is static and the other is dynamic. For example, how does one add contextual information "While opening table in {database}" to the current exception of type WithErrorCode!FileNotFoundException? Andrei

This works: // note: the int parameter above isn't static dbConn.query("select age from people where id='foobar'"); throw new WithErrorCode!FileNotFoundException( db.rs.getValue(1), "file not found");

I don't understand this example.
 This approach fails if you don't know ahead of time what *fields* you
 want to add to your exception but I'd argue that this is unrealistic. An
 exception is thrown as a response to a specific erroneous condition
 which means you already know *what* the problem is and what kind of data
 is needed to describe it.

 Can you offer a real world use-case where the above isn't sufficient?

This has been discussed. A function would want to add contextual information to an exception and rethrow it. Requiring a new type for each such flow does not scale. Andrei
Feb 21 2012
prev sibling parent reply Juan Manuel Cabo <juanmanuel.cabo gmail.com> writes:
 This works:
 // note: the int parameter above isn't static
 dbConn.query("select age from people where id='foobar'");
 throw new WithErrorCode!FileNotFoundException(
           db.rs.getValue(1), "file not found");

 Can you offer a real world use-case where the above isn't sufficient?

What happened is that a file wasn't found. What one wants to catch is a FileNotFoundException. Do you suggest that I have to: try { ... } catch (FileNotFoundException ex) { ... } catch (WithErrorCode!FileNotFoundException ex) { ... } catch (WithRainbows!FileNotFoundException ex) { ... } and so on? --jm
Feb 21 2012
parent reply Juan Manuel Cabo <juanmanuel.cabo gmail.com> writes:
 FileNotFoundException is the super class of the others so the first catch
clause is enough. in fact, the others will
 never be called if listed in the above order.

Nice! I missed that. But what if you want to add ErrorCode and Rainbows? And with your approach, one has to test for type and downcast, or otherwise have multiple catch blocks (I don't want to miss plain FileNotFoundExceptions). So it's square one. With Variant[string] (or something equivalent, nothing better comes to mind) one does: try { ... } catch (FileNotFoundException ex) { if (ex.hasInfo(MyNameConstant)) { ... use that ... } ... common handling ... } --jm
Feb 21 2012
next sibling parent Juan Manuel Cabo <juanmanuel.cabo gmail.com> writes:
Also, you would lose the stacktrace by rethrowing with a different exception
object.
(Currently, the stacktrace is lost by rethrowing the same object, but the
Exception.file
and Exception.line are not lost, and it seems that it is very easy to not lose
the
stacktrace when rethrowing, and it is the correct thing (for instance, java
doesn't
lose the stacktrace when rethrowing, and C++ with its throw; statement for
rethrowing
doesn't either).

--jm

On 02/21/2012 01:15 PM, Juan Manuel Cabo wrote:
 FileNotFoundException is the super class of the others so the first catch
clause is enough. in fact, the others will
 never be called if listed in the above order.

Nice! I missed that. But what if you want to add ErrorCode and Rainbows? And with your approach, one has to test for type and downcast, or otherwise have multiple catch blocks (I don't want to miss plain FileNotFoundExceptions). So it's square one. With Variant[string] (or something equivalent, nothing better comes to mind) one does: try { ... } catch (FileNotFoundException ex) { if (ex.hasInfo(MyNameConstant)) { ... use that ... } ... common handling ... } --jm

Feb 21 2012
prev sibling next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/21/12 10:39 AM, foobar wrote:
 Regarding the downcast - you still perform a check in the code above!
  You gained nothing by replacing a type check with a check on a
 hash.

You do gain because capability checks don't force a tree structure, whereas downcasting does.
 Regarding composition of several traits - even that simple snippet is
  enough: throw new
 WithRainbows!withErrorCode!withFoobar!FileNotFoundException(...);

 That's without further design which could probably improve this
 further.

To quote a classic:
 It's clear that you are trying to generify exceptions. This
 contradicts the very notion of what exceptions are. You also seem to
 try to optimize the amount of exception classes. Making user code
 convoluted for the sake of some premature optimization which most
 likely has negligible affect is completely unacceptable. I get that
 you are a templates master, that does *NOT* mean everything must be
 made generic. You seem to prove the old saying that when all you have
 is a hammer everything looks like a nail.

It's gotta be one or the other. Andrei
Feb 21 2012
prev sibling parent reply Juan Manuel Cabo <juanmanuel.cabo gmail.com> writes:
 throw new WithRainbows!withErrorCode!withFoobar!FileNotFoundException(...);

So: catch (WithRainbows!withErrorCode!withFoobar!FileNotFoundException ex) { .... } catch (WithRainbows!withErrorCode!withFoobar!FileNotFoundException ex) { .... } catch (WithErrorCode!withRainbows!withFoobar!FileNotFoundException ex) { .... } catch (WithRainbows!withFoobar!withErrorCode!FileNotFoundException ex) { and so on (in this case will be, its 3! == 6). and you would have to write them all. You cannot catch only WithRainbows!* because you miss the FileNotFoundException at the end. Please, refer to my previous posts. I don't want to start to repaste my posts. In one of them, I said that what you care about for the catch selection is the *what* of the error. Not the *cause* of the error, not the *where* of the error (no one catches by *where*). And that it seems wrong to encode anything other than the *what* of the error in the type name. Other things such as the cause or the date should be encoded inside the exception object instead of in the exception class type name. I thought that an alternative to Variant[string] would be to have some virtual functions overrideable (getExceptionData(string dataName) or something). but they would all have to return Object or Variant, so it's the same thing. --jm On 02/21/2012 01:39 PM, foobar wrote:
 On Tuesday, 21 February 2012 at 16:15:17 UTC, Juan Manuel Cabo wrote:
 FileNotFoundException is the super class of the others so the first catch
clause is enough. in fact, the others will
 never be called if listed in the above order.

Nice! I missed that. But what if you want to add ErrorCode and Rainbows? And with your approach, one has to test for type and downcast, or otherwise have multiple catch blocks (I don't want to miss plain FileNotFoundExceptions). So it's square one. With Variant[string] (or something equivalent, nothing better comes to mind) one does: try { ... } catch (FileNotFoundException ex) { if (ex.hasInfo(MyNameConstant)) { ... use that ... } ... common handling ... } --jm

Regarding the downcast - you still perform a check in the code above! You gained nothing by replacing a type check with a check on a hash. Regarding composition of several traits - even that simple snippet is enough: throw new WithRainbows!withErrorCode!withFoobar!FileNotFoundException(...); That's without further design which could probably improve this further.

Feb 21 2012
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/21/12 10:50 AM, Juan Manuel Cabo wrote:
 I thought that an alternative to Variant[string] would be to have some virtual
 functions overrideable (getExceptionData(string dataName) or something).
 but they would all have to return Object or Variant, so it's the same thing.

Exactly. By and large, I think in the fire of the debate too many people in this thread have forgotten to apply a simple OO design principle: push policy up and implementation down. Any good primitive pushed up the exception hierarchy is a huge win, and any design that advocates reliance on concrete types is admitting defeat. Andrei
Feb 21 2012
next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2012-02-21 17:57, Andrei Alexandrescu wrote:
 On 2/21/12 10:50 AM, Juan Manuel Cabo wrote:
 I thought that an alternative to Variant[string] would be to have some
 virtual
 functions overrideable (getExceptionData(string dataName) or something).
 but they would all have to return Object or Variant, so it's the same
 thing.

Exactly. By and large, I think in the fire of the debate too many people in this thread have forgotten to apply a simple OO design principle: push policy up and implementation down. Any good primitive pushed up the exception hierarchy is a huge win, and any design that advocates reliance on concrete types is admitting defeat. Andrei

That because you can't (shouldn't) push up implementations specific to a given subclass. Why don't we only have one class, Object, and add a Variant[string] there. Do you see how stupid that is. -- /Jacob Carlborg
Feb 21 2012
next sibling parent reply Juan Manuel Cabo <juanmanuel.cabo gmail.com> writes:
 That because you can't (shouldn't) push up implementations specific to a given
subclass. Why don't we only have one
 class, Object, and add a Variant[string] there.

 Do you see how stupid that is.

As stupid as any database API which returns result items as Variant[string] or string[string], but it works. (the sad part is that one has to rely a bit on convention, but convention can be standardized (string constants) and measures taken when deviated so that it is done gracefuly). Do you have an alternative solution that allows to extend an exception object with extra information, while keeping it the same class? So if one removes the bad reasons to create new Exception types, then the ones that DO get created are solid, standard, reusable, and can withstand the test of time. Because they would be open for extension but closed for source code modification. --jm On 02/21/2012 03:03 PM, Jacob Carlborg wrote:
 On 2012-02-21 17:57, Andrei Alexandrescu wrote:
 On 2/21/12 10:50 AM, Juan Manuel Cabo wrote:
 I thought that an alternative to Variant[string] would be to have some
 virtual
 functions overrideable (getExceptionData(string dataName) or something).
 but they would all have to return Object or Variant, so it's the same
 thing.

Exactly. By and large, I think in the fire of the debate too many people in this thread have forgotten to apply a simple OO design principle: push policy up and implementation down. Any good primitive pushed up the exception hierarchy is a huge win, and any design that advocates reliance on concrete types is admitting defeat. Andrei

That because you can't (shouldn't) push up implementations specific to a given subclass. Why don't we only have one class, Object, and add a Variant[string] there. Do you see how stupid that is.

Feb 21 2012
parent Jacob Carlborg <doob me.com> writes:
On 2012-02-21 19:33, Juan Manuel Cabo wrote:
 That because you can't (shouldn't) push up implementations specific to a given
subclass. Why don't we only have one
 class, Object, and add a Variant[string] there.

 Do you see how stupid that is.

As stupid as any database API which returns result items as Variant[string] or string[string], but it works. (the sad part is that one has to rely a bit on convention, but convention can be standardized (string constants) and measures taken when deviated so that it is done gracefuly).

That's because we are limited by the database API. If you created a new database from scratch, completely written in D, perhaps even object oriented, you could return the correct object form the beginning.
 Do you have an alternative solution that allows to extend an exception
 object with extra information, while keeping it the same class?

No, but that's what subclasses are used for.
 So if one removes the bad reasons to create new Exception types, then the
 ones that DO get created are solid, standard, reusable, and can withstand
 the test of time. Because they would be open for extension but closed for
 source code modification.

-- /Jacob Carlborg
Feb 21 2012
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/21/12 12:03 PM, Jacob Carlborg wrote:
 On 2012-02-21 17:57, Andrei Alexandrescu wrote:
 On 2/21/12 10:50 AM, Juan Manuel Cabo wrote:
 I thought that an alternative to Variant[string] would be to have some
 virtual
 functions overrideable (getExceptionData(string dataName) or something).
 but they would all have to return Object or Variant, so it's the same
 thing.

Exactly. By and large, I think in the fire of the debate too many people in this thread have forgotten to apply a simple OO design principle: push policy up and implementation down. Any good primitive pushed up the exception hierarchy is a huge win, and any design that advocates reliance on concrete types is admitting defeat. Andrei

That because you can't (shouldn't) push up implementations specific to a given subclass. Why don't we only have one class, Object, and add a Variant[string] there. Do you see how stupid that is.

I think I do. It's also fair to ask you if you are sure you understood my point. Andrei
Feb 21 2012
parent reply Jacob Carlborg <doob me.com> writes:
On 2012-02-21 21:06, Andrei Alexandrescu wrote:
 On 2/21/12 12:03 PM, Jacob Carlborg wrote:
 On 2012-02-21 17:57, Andrei Alexandrescu wrote:
 On 2/21/12 10:50 AM, Juan Manuel Cabo wrote:
 I thought that an alternative to Variant[string] would be to have some
 virtual
 functions overrideable (getExceptionData(string dataName) or
 something).
 but they would all have to return Object or Variant, so it's the same
 thing.

Exactly. By and large, I think in the fire of the debate too many people in this thread have forgotten to apply a simple OO design principle: push policy up and implementation down. Any good primitive pushed up the exception hierarchy is a huge win, and any design that advocates reliance on concrete types is admitting defeat. Andrei

That because you can't (shouldn't) push up implementations specific to a given subclass. Why don't we only have one class, Object, and add a Variant[string] there. Do you see how stupid that is.

I think I do. It's also fair to ask you if you are sure you understood my point. Andrei

As I said, it seems you want to push up implementation details specific to a given subclass to the base class even though it shouldn't be pushed up. -- /Jacob Carlborg
Feb 21 2012
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/21/12 2:26 PM, Jacob Carlborg wrote:
 As I said, it seems you want to push up implementation details specific
 to a given subclass to the base class even though it shouldn't be pushed
 up.

I explained that doing so allows for proper formatting of error messages. So it should pushed up. Andrei
Feb 21 2012
parent reply Jacob Carlborg <doob me.com> writes:
On 2012-02-21 21:27, Andrei Alexandrescu wrote:
 On 2/21/12 2:26 PM, Jacob Carlborg wrote:
 As I said, it seems you want to push up implementation details specific
 to a given subclass to the base class even though it shouldn't be pushed
 up.

I explained that doing so allows for proper formatting of error messages. So it should pushed up. Andrei

Well, I don't think that is the right approach. As many others have explained, error messages are only a small part of exception handling. If you do want to have a generic way of getting an error message out of an exception, what's wrong with toString? Or a new method that formats the error messages. No need to push up the instance variables to the base class. -- /Jacob Carlborg
Feb 21 2012
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/21/12 2:42 PM, Jacob Carlborg wrote:
 On 2012-02-21 21:27, Andrei Alexandrescu wrote:
 On 2/21/12 2:26 PM, Jacob Carlborg wrote:
 As I said, it seems you want to push up implementation details specific
 to a given subclass to the base class even though it shouldn't be pushed
 up.

I explained that doing so allows for proper formatting of error messages. So it should pushed up. Andrei

Well, I don't think that is the right approach. As many others have explained, error messages are only a small part of exception handling.

I agree. Also, one interface function is only a small part of a class hierarchy.
 If you do want to have a generic way of getting an error message out of
 an exception, what's wrong with toString? Or a new method that formats
 the error messages. No need to push up the instance variables to the
 base class.

This has been answered in the long thread. In brief, toString loses too much information and putting formatting inside exceptions is not the right place. Andrei
Feb 21 2012
next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2012-02-21 22:08, Andrei Alexandrescu wrote:
 On 2/21/12 2:42 PM, Jacob Carlborg wrote:
 On 2012-02-21 21:27, Andrei Alexandrescu wrote:
 On 2/21/12 2:26 PM, Jacob Carlborg wrote:
 As I said, it seems you want to push up implementation details specific
 to a given subclass to the base class even though it shouldn't be
 pushed
 up.

I explained that doing so allows for proper formatting of error messages. So it should pushed up. Andrei

Well, I don't think that is the right approach. As many others have explained, error messages are only a small part of exception handling.

I agree. Also, one interface function is only a small part of a class hierarchy.
 If you do want to have a generic way of getting an error message out of
 an exception, what's wrong with toString? Or a new method that formats
 the error messages. No need to push up the instance variables to the
 base class.

This has been answered in the long thread. In brief, toString loses too much information and putting formatting inside exceptions is not the right place. Andrei

Now I'm completely lost. According to what I've read this is thread this is exactly what you want to do, put the formatting inside the exceptions. -- /Jacob Carlborg
Feb 21 2012
next sibling parent Jacob Carlborg <doob me.com> writes:
On 2012-02-22 08:33, Jonathan M Davis wrote:
 On Wednesday, February 22, 2012 08:22:21 Jacob Carlborg wrote:
 Now I'm completely lost. According to what I've read this is thread this
 is exactly what you want to do, put the formatting inside the exceptions.

No. He wants to provide a way for an external function to generically generate strings according to the format that you want when you generate the string. So, some function would take a formatting string of some kind and then read the corresponding values form the Variant[string] in Exception and generate a string according to that format string. How exactly that works, I don't understand (something about a string template language), but that's the idea. So, while you could still use toString, there would be a way to generate strings formatted the way that _you_ want rather than how toString would do it - and to do it in a generic manner. As long as this doesn't mean using Variant[string] as the way to inject all of the extra data into exceptions and we still use an exception hierarchy with the appropriate data members in derived exceptions, then I don't really see that as a problem. The problem is if we then also get rid of the hierarchy and/or try and put all of the data is the Variant[string] and only in the Variant[string].

I agree.
 From the sounds of it, we have _some_ agreement to have an exception hierarchy

Variant[string] bit to Exception to enable the passing of other data that you might want but is not in the exception type normally as well as enable the fancy string formatting stuff that Andrei wants. But this thread is so long and complicated that I think that many of us are just confused. - Jonathan M Davis

Ok, I see. -- /Jacob Carlborg
Feb 21 2012
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/22/12 1:22 AM, Jacob Carlborg wrote:
 Now I'm completely lost. According to what I've read this is thread this
 is exactly what you want to do, put the formatting inside the exceptions.

No, just have exceptions inform an external formatter. Andrei
Feb 22 2012
parent Jacob Carlborg <doob me.com> writes:
On 2012-02-22 15:01, Andrei Alexandrescu wrote:
 On 2/22/12 1:22 AM, Jacob Carlborg wrote:
 Now I'm completely lost. According to what I've read this is thread this
 is exactly what you want to do, put the formatting inside the exceptions.

No, just have exceptions inform an external formatter. Andrei

Ok, I see. -- /Jacob Carlborg
Feb 22 2012
prev sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Wednesday, February 22, 2012 08:22:21 Jacob Carlborg wrote:
 Now I'm completely lost. According to what I've read this is thread this
 is exactly what you want to do, put the formatting inside the exceptions.

No. He wants to provide a way for an external function to generically generate strings according to the format that you want when you generate the string. So, some function would take a formatting string of some kind and then read the corresponding values form the Variant[string] in Exception and generate a string according to that format string. How exactly that works, I don't understand (something about a string template language), but that's the idea. So, while you could still use toString, there would be a way to generate strings formatted the way that _you_ want rather than how toString would do it - and to do it in a generic manner. As long as this doesn't mean using Variant[string] as the way to inject all of the extra data into exceptions and we still use an exception hierarchy with the appropriate data members in derived exceptions, then I don't really see that as a problem. The problem is if we then also get rid of the hierarchy and/or try and put all of the data is the Variant[string] and only in the Variant[string].
From the sounds of it, we have _some_ agreement to have an exception hierarchy 

Variant[string] bit to Exception to enable the passing of other data that you might want but is not in the exception type normally as well as enable the fancy string formatting stuff that Andrei wants. But this thread is so long and complicated that I think that many of us are just confused. - Jonathan M Davis
Feb 21 2012
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/21/12 1:17 PM, Jonathan M Davis wrote:
 On Tuesday, February 21, 2012 10:57:20 Andrei Alexandrescu wrote:
 On 2/21/12 10:50 AM, Juan Manuel Cabo wrote:
 I thought that an alternative to Variant[string] would be to have some
 virtual functions overrideable (getExceptionData(string dataName) or
 something). but they would all have to return Object or Variant, so it's
 the same thing.

in this thread have forgotten to apply a simple OO design principle: push policy up and implementation down. Any good primitive pushed up the exception hierarchy is a huge win, and any design that advocates reliance on concrete types is admitting defeat.

Exceptions do _not_ lend themselves to polymorphism. Having them in a type hierarchy is useful. It allows you to deal with them at varying levels of abstractions. But ultimately, you deal with the concrete types, _not_ an abstract interface. In that sense, they're not OO _at all_.

Well this is just a series of assertions that conveys no information.
 Adding a Variant[string] property to allow adding on additional information if
 a particular application finds it useful may be a good thing to do. But it
 should be an _add on_, not the core design.

Again, just an assertion.
 Aside from printing strings,
 trying to deal with exceptions generically just does not make sense.

Assertion.
 At best,
 you might care about a common exception rather than a more specific one in
 particular case (e.g. catching IOException rather than FileException). But if
 you're trying to actually handle the exception in any real way rather than
 just print out a message, you need the concrete type, not an abstract
 interface.

Assertion.
 I think that you're pushing the OO angle too hard onto exceptions.

I thought I was pushing the generics angle, and OO people explained it to me that that was wrong.
 They're not
 completely separated from it, but they really aren't classic OO and shouldn't
 be treated as such. If anything, they're inverted, because you frequently try
 and deal with as concrete a type as possible rather than as abstract a type as
 possible. The hierarchy aspect is really the only truly OO aspect of
 exceptions IMHO. For the most part, polymorphism just doesn't enter into it.
 And Exception really already declares the few functions where it does.

I'm sorry, I was unable to derive information from this post. It's a string of assertion without any backing. Andrei
Feb 21 2012
parent reply deadalnix <deadalnix gmail.com> writes:
Le 22/02/2012 06:47, H. S. Teoh a crit :
 On Tue, Feb 21, 2012 at 07:43:32PM -0500, Jonathan M Davis wrote:
 On Tuesday, February 21, 2012 14:15:03 Andrei Alexandrescu wrote:
 I thought I was pushing the generics angle, and OO people explained
 it to me that that was wrong.


I've changed my mind. Now I'm trying to see if the generics angle has some possibilities. Maybe, maybe not, but we'll never know without experimenting with it. I think your is_transient idea can be expanded upon. The way exceptions are currently implemented, they only carry information, not behaviour, as Jonathan said rightly. The try/catch mechanism essentially reduces to "I've hit a problem I don't know how to solve, here's a description of it". There's no behaviour in there. The throwing code has already given up. It's up to the catcher to interpret the description of the problem and figure out how to recover. To recover well, the catcher must know the intimate details of the problem well. So you have the situation of a specific catcher catching a specific Exception subclass. This is not an ideal situation, because now high-level code needs to know the specifics of low-level errors. With your is_transient idea, though, this begins to change. Now we're no longer just describing the problem. When is_transient=1, it means the thrower is suggesting that perhaps retrying would help. Of course, it's up to the catcher whether or not to follow through with this suggestion, but it's one step up from "here's a description of the problem, figure out the solution yourself". But now the catcher doesn't necessarily have to know the specifics of the low-level problem. It knows at least one strategy that might fix the problem, regardless of what the problem is: retry the operation. This is good, because the low-level code, which knows the problem best, can offer a useful suggestion (retry). The high-level code can just take the suggestion or not; it no longer needs to know low-level details. But why stop there? Since the low-level code knows all the dirty details about the problem, it's in the best position to offer meaningful recovery suggestions. It just has to communicate these possible recovery strategies to the high-level code, and let the high-level code decide what to do. The high-level code doesn't need to know how to implement these strategies -- it's not in the best position to know that anyway. It just knows, here's a list of recovery strategies, I can go ahead with one of them, or just call it quits and unwind the stack. The low-level code is what implements each strategy. Of course, in order for the high-level code to meaningfully choose between alternative strategies, the strategies themselves must be generic concepts; otherwise we're still tying high-level code to low-level details. So we need to identify generic categories of exceptions for which this kind of generic recovery is meaningful -- which is what I've done in another post. I won't repeat the details here, but I just want to say that I think this angle merits some investigation. It allows us to factor out exceptions which can be resolved by commonly used recovery strategies so that we don't have to keep writing tons and tons of exception-specific recovery code everywhere. Some specific code is still needed, no doubt, there's always special cases that need specific handling. But if enough exceptions can be adequately dealt with generically, then we don't need to write specific code for them. We can simply reuse generic recovery solutions. T

100% Agree. Additionnaly, I would mention that the transient isn't a caracteristic of the Exception, but of the recovery strategy.
Feb 22 2012
parent deadalnix <deadalnix gmail.com> writes:
Le 22/02/2012 18:50, H. S. Teoh a crit :
 On Wed, Feb 22, 2012 at 11:53:39AM +0100, deadalnix wrote:
 [...]
 Additionnaly, I would mention that the transient isn't a caracteristic
 of the Exception, but of the recovery strategy.

Technically correct. Though I'm playing with the idea of making recovery strategies a property of an exception - since a recovery strategy is meaningless without an associated exception (or problem). I need to think this through a bit more, though, as to how to correctly implement this.

I did though about this. This isn't the right way. Recovery strategy doesn't have any meaning at the catch point, so we shouldn't make it a property of Exception. And sometime you don't care about the Exception. If you try to connect something that is know to fail for exemple, you really don't want to know what went wrong. You just want to try again with some backoff. I do think you made a point with the handler getting an Exception and a recovery stratgy as parameter, but it is still unclear where all this goes to me.
Feb 22 2012
prev sibling parent "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Tuesday, February 21, 2012 10:57:20 Andrei Alexandrescu wrote:
 On 2/21/12 10:50 AM, Juan Manuel Cabo wrote:
 I thought that an alternative to Variant[string] would be to have some
 virtual functions overrideable (getExceptionData(string dataName) or
 something). but they would all have to return Object or Variant, so it's
 the same thing.

in this thread have forgotten to apply a simple OO design principle: push policy up and implementation down. Any good primitive pushed up the exception hierarchy is a huge win, and any design that advocates reliance on concrete types is admitting defeat.

Exceptions do _not_ lend themselves to polymorphism. Having them in a type hierarchy is useful. It allows you to deal with them at varying levels of abstractions. But ultimately, you deal with the concrete types, _not_ an abstract interface. In that sense, they're not OO _at all_. Adding a Variant[string] property to allow adding on additional information if a particular application finds it useful may be a good thing to do. But it should be an _add on_, not the core design. Aside from printing strings, trying to deal with exceptions generically just does not make sense. At best, you might care about a common exception rather than a more specific one in particular case (e.g. catching IOException rather than FileException). But if you're trying to actually handle the exception in any real way rather than just print out a message, you need the concrete type, not an abstract interface. I think that you're pushing the OO angle too hard onto exceptions. They're not completely separated from it, but they really aren't classic OO and shouldn't be treated as such. If anything, they're inverted, because you frequently try and deal with as concrete a type as possible rather than as abstract a type as possible. The hierarchy aspect is really the only truly OO aspect of exceptions IMHO. For the most part, polymorphism just doesn't enter into it. And Exception really already declares the few functions where it does. - Jonathan M Davis
Feb 21 2012
prev sibling next sibling parent reply Juan Manuel Cabo <juanmanuel.cabo gmail.com> writes:
Well... then why did this mistakes exist?:

In dot NET:

	ComException - Exception encapsulating COM HRESULT information
        SEHException	Exception encapsulating Win32 structured exception
handling information.z

	http://msdn.microsoft.com/en-us/library/z4c5tckx%28v=VS.71%29.aspx

And why do you think that a thing like standardizing DatabaseException
never survives users, and that each database manager library defines its
own top *DatabaseException base class?

This is a universal problem with transversal traits of exceptions.

--jm



On 02/20/2012 10:22 PM, H. S. Teoh wrote:
 On Mon, Feb 20, 2012 at 10:01:03PM -0300, Juan Manuel Cabo wrote:
 [...]
 Back to the nasty argument. I think that the example that everyone
 wants is this one. If anyone solves this one without Variant[string]
 then it's a better solution than Variant[string]. (I repaste it
 from an above reply I gave):

   [..]
   For instance: a C library wrapper, which gets the library errors encoded
   as some error code and throws them as exceptions. Shouldn't the library
   throw a FileNotFoundException when that's the error, instead of throwing
   a LibraryException that has the error code in a field?

   So the correct thing to do is: after a library call, the wrapper
   checks the last error code number with a switch statement, and deciding
   which standard exception type to throw (defaulting to whatever you like
   if the error code doesn't map to a standard D exception). Then you
   add the error code to the Variant[string], and any other extra info.

But why bother with the error code at all? If you get a FileNotFoundException, you already know all there is to know about the problem, adding errno to it is redundant and only encourages code that's bound to a specific implementation. Instead, Phobos should present a self-consistent API that's independent of what it uses to implement it, be it C stdio (errno) or C++ iostreams or Oracle driver (Oracle-specific error codes) or Postgresql driver (Postgresql-specific error codes), or what have you. For error codes that *don't* have a direct mapping to standard exceptions, you can just encapsulate the errno (or whatever) inside a specific catch-all exception type dedicated to catch these sorts of unmapped cases, so that code that *does* know what errno can just catch this exception and interpret what happened. General, platform-independent code need not know what this exception is at all, they can just treat it as a general problem and react accordingly. We don't (and shouldn't) expect every app out there to know or care about the errno of a failed operation, especially if it doesn't map to one of the standard exception types.
   That way, exception types can be standard.

   So, to keep D exception types standard reusable and respected by
   future code, you must follow the Open-Closed design principle
   (nicest principle of OO design ever).
   [..]

 Adding the Variant[string] is considered applying the great
 Open-Closed Design Principle:
 	-Open for reuse.
 	-Closed for modification.
         http://www.objectmentor.com/resources/articles/ocp.pdf

Please bear in mind, I'm not saying that Variant[string] is *completely* useless. I'm just saying that most of the time it's not necessary. Sure there are some cases where it's useful, I've no problem with it being used in those cases. But we shouldn't be using it for all kinds of stuff that can be handled in better ways, e.g., static fields in a derived exception class. T

Feb 20 2012
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/20/12 7:32 PM, Juan Manuel Cabo wrote:
 Well... then why did this mistakes exist?:

 In dot NET:

 	ComException - Exception encapsulating COM HRESULT information
          SEHException	Exception encapsulating Win32 structured exception
handling information.z

 	http://msdn.microsoft.com/en-us/library/z4c5tckx%28v=VS.71%29.aspx

 And why do you think that a thing like standardizing DatabaseException
 never survives users, and that each database manager library defines its
 own top *DatabaseException base class?

 This is a universal problem with transversal traits of exceptions.

Yes, exactly. Exceptions are poorly equipped to address cross-cutting concerns, which is odd because they are themselves a cross-cutting matter :o). The solution is, I think, to find improved abstractions. Andrei
Feb 20 2012
prev sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/20/12 7:22 PM, H. S. Teoh wrote:
 Please bear in mind, I'm not saying that Variant[string] is *completely*
 useless. I'm just saying that most of the time it's not necessary. Sure
 there are some cases where it's useful, I've no problem with it being
 used in those cases. But we shouldn't be using it for all kinds of stuff
 that can be handled in better ways, e.g., static fields in a derived
 exception class.

On the contrary, I predict that once Variant[string] info() is present in Exception, people will start using it with a collective sigh of relief. Andrei
Feb 20 2012
prev sibling next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/20/12 7:08 PM, H. S. Teoh wrote:
 On Mon, Feb 20, 2012 at 06:38:08PM -0600, Andrei Alexandrescu wrote:
 [...]
 We shouldn't be using Variant[string] for this, because there's
 another problem associated with it: suppose MyException sometimes
 gets "extra_info1" and "extra_info2" tacked onto it, on its way up
 the call stack, and sometimes not. Now what should the catcher do?

Use a central loop to render the information.

Then it's useless for i18n, unless you can magically translate a sequence of unknown values into a different language in a consistent way. It sounds like all this is good for is to print a list of "name=value" pairs. Which is useful, I'll admit, for development/debugging purposes. I don't see how such a thing is useful in i18n.

As I said, string templates in conjunction with name/value bindings is all that's needed for i18n.
 [...]
 This is what I mean by not being able to depend on whether some data
 is there. Ultimately, to do anything useful with the info in the
 object, you need to know what's there.

No. You do realize you are positioning yourself straight against every OO principle there is? The entire OO world is predicated on the assumption you _can_ do useful stuff with an object WITHOUT knowing what's there.

And the way OO does this is by having the *derived classes* do useful stuff behind a polymorphic interface.

Exactly, and with this you just destroyed your own design. For it does not build any polymorphic interface, no abstraction. It forever forces code to toil in the concrete (_fileName, _ipAddress, _userName) and fails to elevate any common interface that would foster reusable code.
 What the Variant[string] does is,
 as you have said previously, to replace the need for many derived
 classes save a few, thereby throwing away the polymorphism and
 presenting what amounts to an array of void* to the catch block.

On the contrary. This builds abstraction: class Exception { bool transient(); Variant[string] info(); ... } because it allows client code to treat different types, even types that haven't even been defined yet, uniformly. This does not build anything: class Exception {} class UserNameException : Exception { string _userName; } class NetException : Exception { string _ipAddress; } ...
 For you to now accuse me of going against OO principles is a really
 strange argument, I must say. I was in fact *advocating* the use of OO
 by putting the useful information in the derived classes, where they
 belong.

I'm not accusing of anything, merely noting that your design does not stand the scrutiny of your own principles (which are correct). Both can't be sustained simultaneously. Try a fresh run of your principles over your design. It'll get totaled.
 I think you are misunderstanding the mechanisms involved. There is no
 combinatorial code, just simple iteration and such. Dealing with
 distinct exceptions with distinct code IS combinatorial, repetitive,
 and non-scalable.

Then pray tell how "simple iteration" will achieve the translation of the data in Variant[string] into a localized string, when there is no guarantee any field will be in the hash at all. Format strings obviously won't work, since you can't have a format string unless you already know what arguments are present.

There's no guarantee. That'll be a runtime check in the formatting engine. Andrei
Feb 20 2012
prev sibling next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/20/12 7:07 PM, Jonathan M Davis wrote:
 On Monday, February 20, 2012 18:38:08 Andrei Alexandrescu wrote:
 On 2/20/12 6:25 PM, H. S. Teoh wrote:
 This is what I mean by not being able to depend on whether some data is
 there. Ultimately, to do anything useful with the info in the object,
 you need to know what's there.

No. You do realize you are positioning yourself straight against every OO principle there is? The entire OO world is predicated on the assumption you _can_ do useful stuff with an object WITHOUT knowing what's there.

Exceptions aren't very OO really. Aside from getting a string out of them, you generally don't have any polymorphism involved, and even with the string, there's a good chance that you don't. For instance, the message field which toString uses to generate its string is passed as an argument to Exception's constructor.

(Cloning and comparison are also essential polymorphic methods to implement.) But then again the view "exceptions are wildebeests" is coming straight from inside the box. We want to improve on that.
 With exceptions, it's the types that matter. It's not a case of changing
 behavior based on implementation. With OO, you try _not_ to care about what
 the type is. With exceptions, on the other hand, you care a great deal. It's
 inverted from OO. Exceptions hold data but not much behavior. The behavior
 goes in the catch blocks.

 If we had a construct which allowed inheritance but not polymorphism (which we
 don't, since for better or wors, inheritance is conflated with polymorphism in
 D), then that would make a lot of sense for exceptions.

I don't think inheritance without subtyping would help any. C++ has it (non-public inheritance) but that didn't improve exceptions one bit.
 It doesn't really hurt us that exceptions have polymorphism, but it doesn't
 generally help them do their job at all. If you're going to do anything beyond
 simply print an error message, you need to know what the exception type is and
 what data it has. And that's not OO at all.

Or you need to implement a key-value interface that allows printing of an elaborate error message. Andrei
Feb 20 2012
prev sibling parent deadalnix <deadalnix gmail.com> writes:
Le 21/02/2012 01:38, Andrei Alexandrescu a crit :
 On 2/20/12 6:25 PM, H. S. Teoh wrote:
 On Mon, Feb 20, 2012 at 05:15:17PM -0600, Andrei Alexandrescu wrote:
 Formatting should use class reflection. We already discussed that, and
 we already agreed that was the superior approach.

Jose's argument convinced me otherwise. I retract my agreement.
 When you're catching a specific exception, you're catching it with the
 view that it will contain precisely information X, Y, Z that you need to
 recover from the problem. If you don't need to catch something, then
 don't put the catch block there.

That's extremely rare in my experience, and only present in toy examples that contain a ton of "..." magic.

I think you experience here is biased by C++ .
Feb 21 2012
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/20/12 4:25 PM, Jonathan M Davis wrote:
 In my experience, the type is very much tied to the context.

I thought you said the type is tied to "what happened, not where it happened", which is the polar opposite of the above.
 When a particular
 type of error occurs, there's a particular set of information that goes with
 that, and that doesn't generally change. So, you don't end up with a bunch of
 types which are solely there to add additional information.

But on the way up there's additional contextual information. "Total amount must be a positive number" is more descriptive "Conversion error", although it originated as the latter. Insisting that all that must be encoded as types seems overly rigid, not to mention non-scalable.
 Using variant means moving to dynamic typing and problems that you don't see
 until runtime, whereas having the data as direct member variables is
 statically checked.

That's a given. However I think the most frequent use of exception interfaces is to extract data for formatting purposes, and the exception interface would do good to help with that.
 Having the ability to add extra information via Variant[string] may not be a
 problem, but doing that for everything _would_ be. It's far more bug prone,
 and the fact that it would be error handling code which would have the bugs
 would make them _far_ harder to catch and fix.

 I definitely think that we should favor putting data in member variables, not
 in a hashtable of Variants.

Whenever you put data in member variables you set yourself up for code bloat. Do you agree? Andrei
Feb 20 2012
parent reply Piotr Szturmaj <bncrbme jadamspam.pl> writes:
Andrei Alexandrescu wrote:
 On 2/20/12 4:25 PM, Jonathan M Davis wrote:
 I definitely think that we should favor putting data in member
 variables, not
 in a hashtable of Variants.


+1
 Whenever you put data in member variables you set yourself up for code
 bloat. Do you agree?

It's a problem of classes as a whole, not just exceptions. I'm afraid such 'pressure' will limit the use of classes in Phobos.
Feb 20 2012
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/20/12 7:09 PM, Piotr Szturmaj wrote:
 Andrei Alexandrescu wrote:
 On 2/20/12 4:25 PM, Jonathan M Davis wrote:
 I definitely think that we should favor putting data in member
 variables, not
 in a hashtable of Variants.


+1
 Whenever you put data in member variables you set yourself up for code
 bloat. Do you agree?

It's a problem of classes as a whole, not just exceptions. I'm afraid such 'pressure' will limit the use of classes in Phobos.

The problem is solved by pushing interface up, i.e. defining good methods in the base classes. Andrei
Feb 20 2012
prev sibling next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Mon, Feb 20, 2012 at 10:32:48PM -0300, Juan Manuel Cabo wrote:
 Well... then why did this mistakes exist?:
 
 In dot NET:
 
 	ComException - Exception encapsulating COM HRESULT information
         SEHException	Exception encapsulating Win32 structured exception
handling information.z
 
 	http://msdn.microsoft.com/en-us/library/z4c5tckx%28v=VS.71%29.aspx

I couldn't tell you, I didn't design this.
 And why do you think that a thing like standardizing DatabaseException
 never survives users, and that each database manager library defines
 its own top *DatabaseException base class?

Perhaps because when users chose to use the Oracle engine, they want to deal with Oracle-specific features including Oracle error codes directly? So a generalized SQL exception designed under some idealized database framework would only get in their way?
 This is a universal problem with transversal traits of exceptions.

True, so now we're trying to fix this by having *both* an exception class hierarchy *and* what amounts to a generic varbind that stores transversal information? If Oracle error codes are what the user wants, then giving them a pretty exception class hierarchy is sorta pointless. They just want an OracleException containing the Oracle error code. They couldn't care less if there was a SQLParseErrorException or a SQLCannotAcquireLockException. It would be a waste of effort to build a pretty hierarchy for them if they're just going to ignore it and use Oracle error codes anyway. If we're really trying to solve transversal problems, then I have the feeling that this will require something more than just slapping a Variant[string] onto the Exception class. We need to re-examine the entire model of what exceptions are and what's the best way to deal with them. In the case of Phobos, we're not really dealing with the same kind of situation (not yet, anyway). Phobos modules, in general, aren't merely thin wrappers around a self-contained external module the same way an Oracle API is just a thin wrapper over an independent Oracle driver built by an independent party. So we get to define our own game, in a sense. There's no Oracle error code to map, and we can enforce the proper use of an exception class hierarchy. If someone wants direct access to errno, say, they could just call the C API directly -- D was designed not to require wrappers in that case. But still, I concede that class hierarchies don't solve everything. Sometimes we do need something more. But slapping on a Variant[string] onto the existing system seems like merely a workaround, not a real solution. T -- Study gravitation, it's a field with a lot of potential.
Feb 20 2012
prev sibling next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Mon, Feb 20, 2012 at 08:32:08PM -0600, Andrei Alexandrescu wrote:
 On 2/20/12 7:08 PM, H. S. Teoh wrote:
On Mon, Feb 20, 2012 at 06:38:08PM -0600, Andrei Alexandrescu wrote:
[...]
We shouldn't be using Variant[string] for this, because there's
another problem associated with it: suppose MyException sometimes
gets "extra_info1" and "extra_info2" tacked onto it, on its way up
the call stack, and sometimes not. Now what should the catcher do?

Use a central loop to render the information.

Then it's useless for i18n, unless you can magically translate a sequence of unknown values into a different language in a consistent way. It sounds like all this is good for is to print a list of "name=value" pairs. Which is useful, I'll admit, for development/debugging purposes. I don't see how such a thing is useful in i18n.

As I said, string templates in conjunction with name/value bindings is all that's needed for i18n.

And how would such templates be designed, if what is available in the bindings changes depending on the code path that led to the exception? I agree with you that this allows the catch block to do stuff like pass the entire hash to the i18n formatter without needing to know what's in it. And conceivably, the formatter doesn't need to know either, it just gives the bindings to the string templates. But the string templates themselves have to know what's in the bindings. In fact, they *assume* that certain bindings are in there. And when the expected bindings are not there, then the template can't be applied. And you couldn't possibly know this until runtime. Whereas if the fields were fixed at compile-time, then the compiler could verify that what the code thinks is there, is actually there. [...]
 Exactly, and with this you just destroyed your own design. For it does
 not build any polymorphic interface, no abstraction. It forever forces
 code to toil in the concrete (_fileName, _ipAddress, _userName) and
 fails to elevate any common interface that would foster reusable code.

No. With RTTI, there's no need to toil in the concrete at all. Loop over whatever fields happen to be in the object, hand them off to the formatter or whatever it is wants to use them, job finished. The only time you explicitly refer to _fileName, _ipAddress, etc., is when you *specifically want to deal with them*. This is where you *want* the compiler to statically verify that _fileName actually exists in that object. Rather than wait till runtime and then the catch block realizes oops, the field doesn't exist. I'm not negating the fact that the hash is useful for *some* things. I'm just saying that there are occasions where it *shouldn't* be used. There are times when you need compile-type type-checking. [...]
 On the contrary. This builds abstraction:
 
 class Exception
 {
     bool transient();
     Variant[string] info();
     ...
 }
 
 because it allows client code to treat different types, even types
 that haven't even been defined yet, uniformly.

I see your point. But doesn't RTTI already fill the need of genericity? Why not take advantage of compiler-time type checking where it's possible? You can still have the hash for runtime-added stuff, like adding properties to exceptions in transit. I can see a use for that. But why must *everything* be stuffed into the hash? [...]
Then pray tell how "simple iteration" will achieve the translation of
the data in Variant[string] into a localized string, when there is no
guarantee any field will be in the hash at all. Format strings
obviously won't work, since you can't have a format string unless you
already know what arguments are present.

There's no guarantee. That'll be a runtime check in the formatting engine.

So in order for the format string to *not* fail at runtime, the exception must always have the same fields in the bindings, right? Isn't that the same thing as defining the fields statically and using RTTI to iterate over them? That way, the compiler can verify at compile time that the fields are actually there. T -- Real Programmers use "cat > a.out".
Feb 20 2012
prev sibling next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Tue, Feb 21, 2012 at 03:15:19PM -0600, Andrei Alexandrescu wrote:
[...]
 A more debatable aspect of exceptions is the first-match rule in
 catch blocks. All of OOP goes with best match, except here. But then
 all code is together so the effect is small.

Does it make sense to make it best-match? Or is that too risky since everyone expects it to be first-match? [...]
So I'm going to throw (har har) this very crazy and wild idea out
there and let's see if it's viable:

What if instead of catching by class, we catch by attribute matching?
So instead of writing:

	try { ... }
	catch(SomeExceptionType e) { ... }
	catch(SomeOtherExceptionType e) { ... }
	catch(YetAnotherSillyException e) { ... }

we write:

	try { ... }
	catch(e: exists!e.filename&&  e.failedOp is File.open) {
		// something
	}
	catch(e: e.is_transient&&  e.num_retries<  5) {
		// something else
	}
	// And why should we even need an exception object in the first
	// place?
	catch(time()>= oldtime+5000) {
		// This thing's been running for way too long, time to
		// do something drastic
	}

Flamesuit on! ;-)

The only problem I see here is ascribing e a type.

True, for this to work in its full generality would require duck-typing (the catch block can use any property it tests for, regardless of type). Which D doesn't have. So it looks like we're back to catch conditions, that has come up a few times in this thread: try { ... } catch(Exception e: e.is_transient && ... || ...) { // or whatever the latest proposed syntax is, the idea // is the same. } catch(Exception e: !e.is_transient && ...) { // the previous block doesn't catch if conditions fail, // so we can still get Exception here. } This does start to look like it might make sense to switch to best-match instead of first-match for catch clauses. T -- Your inconsistency is the only consistent thing about you! -- KD
Feb 21 2012
prev sibling parent "Martin Nowak" <dawg dawgfoto.de> writes:
On Tue, 21 Feb 2012 23:29:32 +0100, H. S. Teoh <hsteoh quickfur.ath.cx>  
wrote:

 On Tue, Feb 21, 2012 at 03:15:19PM -0600, Andrei Alexandrescu wrote:
 [...]
 A more debatable aspect of exceptions is the first-match rule in
 catch blocks. All of OOP goes with best match, except here. But then
 all code is together so the effect is small.

Does it make sense to make it best-match? Or is that too risky since everyone expects it to be first-match?

error, g++ does that as a warning too.
Feb 21 2012
prev sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Monday, February 20, 2012 17:31:28 Juan Manuel Cabo wrote:
 ...
 Sure. Again, this is not advocating replacement of exception hierarchies
 with tables! ...
 
 Andrei

I think that the case of rethrowing an exception with added detail is the worst enemy of clean Exception hierarchies. The idea of Variant[string] remedies that case without creating a new exception class just for the added fields. If that case is solved, then the tipical need for creating new exception types that don't really aid selecting them for catching and recovery is solved too.

Having derived exceptions with additional information is a _huge_ boon, and I contend that it's vasty better with variant, which would be highly error prone, because it's not properly statically checked. Changes to what's put in the variant could kill code at runtime - code which by its very definiton is not supposed to be the normal code path, so you're less likely to actually run into the problem before you ship your product. Whereas with the information in actual member variables, if they get changed, you get a compilation error, and you know that you have to fix your code. Rethrowing is a separate issue. And in many cases, the correct thing to do is to chain exceptions. You catch one, do something with it, and then you throw a new one which took the first one as an argument. Then you get both. That functionality is already built into Exception. - Jonathan M Davis
Feb 20 2012
prev sibling next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/20/12 12:42 PM, H. S. Teoh wrote:
 On Mon, Feb 20, 2012 at 01:23:04PM -0500, Jonathan M Davis wrote:
 On Monday, February 20, 2012 11:57:07 Andrei Alexandrescu wrote:

 Exactly. I don't see how a disagreement follows from here. So isn't
 it reasonable to design the exception such that it can offer
 information pertaining to what went wrong, in a uniform manner?


 I don't see how you could possibly make that uniform. It's very
 non-uniform by its very nature. The handling _needs_ to be
 non-uniform.

I think there's a misunderstanding here. What Andrei is trying to achieve is something like this: class Exception : Error { Variant[string] data; string formatMsg(LocaleInfo li) { return formatLocale(li, msg, data); } } class GetOptException : Exception { this() { data["..."] = ...; ... } } I contend that using Variant here is not necessary. You can easily do this instead: class Exception : Error { string formatMsg(LocaleInfo li) { auto members = typeid(this).getMembers(null); string msg; foreach (member; members) { if (member.name.startsWith("info")) { ... //format this field } } return msg; } } class GetOptException : Exception { string infoOption; // this gets picked up by formatMsg int internalData; // this is ignored // No need to declare anything else except ctor to set // the above fields. } This allows for a much cleaner, type-checked access to infoOption, should the catching code know how to deal with GetOptException specifically.

But this moves i18n code straight inside the exception, which Jonathan argues against. Separated concerns call for separated modules. Sorry, I don't know what to write beyond the one line above! Andrei
Feb 20 2012
prev sibling next sibling parent Juan Manuel Cabo <juanmanuel.cabo gmail.com> writes:
On 02/20/2012 02:57 PM, Andrei Alexandrescu wrote:
 On 2/20/12 11:44 AM, foobar wrote:
 This extra processing is orthogonal to the exception. the same exception
 can be logged to a file, processed (per above example) and generate
 graphical notification to the user, etc. The exception contains the
 information pertaining only to what went wrong. the rest is not part of
 this discussion.

Exactly. I don't see how a disagreement follows from here. So isn't it reasonable to design the exception such that it can offer information pertaining to what went wrong, in a uniform manner?
 The exact same exception in the example would also be thrown on a
 mistyped URL in an application that tries to scrape some info from a
 website for further processing. The error is still the same - the url is
 incorrect but different use cases handle it differently. In the former
 example I might call to a i18n lib (someone already mentioned gettext)
 while in the latter I'll call a logging library with the the mistyped
 url (for statistics' sake).
 in the first I use the url to find a closest match, in the second I want
 to log said requested url. Both handled by *other* mechanisms.
 in both cases the exception needs a url field and in both cases I have
 no need for the Variant[string] map.

The Variant[string] map saves a lot of duplication whenever you want to format a human-readable string (which is a common activity with exceptions). It transforms this (I'm too lazy to write code anew by hand, so I'll paste Jonathan's): 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; } into this: try getopt(args, ...) catch(Exception e) { stderr.writeln(stringTemplate(typeid(e).toString(), e.info)); return -1; } The stringTemplate function loads the formatting template from a table indexed on typeid(e).toString() and formats it with the info. It's simple factorization. Andrei

Feb 20 2012
prev sibling next sibling parent reply Juan Manuel Cabo <juanmanuel.cabo gmail.com> writes:
I like the idea!

Remember please for anyone reading: Use positional arguments in
format strings. Otherwise:

	"The '%s' file's size is %d which is wrong"

        translated to

	"El tamaño %d es incorrecto para el archivo %s"

will be trouble. Instead please do:

        "The '%1$s' file's size is %2$d which is wrong"

specially for standard library messages. This would be very helpful!

--jm



 into this:

 try
     getopt(args, ...)
 catch(Exception e)
 {
     stderr.writeln(stringTemplate(typeid(e).toString(), e.info));
     return -1;
 }

 The stringTemplate function loads the formatting template from a table indexed
on typeid(e).toString() and formats it
 with the info. It's simple factorization.


 Andrei

Feb 20 2012
next sibling parent deadalnix <deadalnix gmail.com> writes:
Why don't we use .tupleof for such a thing ?

Le 20/02/2012 20:17, Juan Manuel Cabo a écrit :
 I like the idea!

 Remember please for anyone reading: Use positional arguments in
 format strings. Otherwise:

 	"The '%s' file's size is %d which is wrong"

          translated to

 	"El tamaño %d es incorrecto para el archivo %s"

 will be trouble. Instead please do:

          "The '%1$s' file's size is %2$d which is wrong"

 specially for standard library messages. This would be very helpful!

 --jm



 into this:

 try
      getopt(args, ...)
 catch(Exception e)
 {
      stderr.writeln(stringTemplate(typeid(e).toString(), e.info));
      return -1;
 }

 The stringTemplate function loads the formatting template from a table indexed
on typeid(e).toString() and formats it
 with the info. It's simple factorization.


 Andrei


Feb 20 2012
prev sibling next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/20/12 1:17 PM, Juan Manuel Cabo wrote:
 I like the idea!

 Remember please for anyone reading: Use positional arguments in
 format strings.

Not positional, but instead Symbolic is the way. Andrei
Feb 20 2012
prev sibling parent Jacob Carlborg <doob me.com> writes:
On 2012-02-20 20:49, H. S. Teoh wrote:
 On Mon, Feb 20, 2012 at 08:36:56PM +0100, Andrej Mitrovic wrote:
 On 2/20/12, Juan Manuel Cabo<juanmanuel.cabo gmail.com>  wrote:
 will be trouble. Instead please do:

          "The '%1$s' file's size is %2$d which is wrong"

That is the shittiest formatting specifier ever invented. The unreadability of it is why I never, ever, use it. Python solved this nicely with its {0} {1} syntax:
 print '{0} and {1}'.format('foo', 'bar')




Actually, even that isn't ideal. How is the translator to know what on earth {0} and {1} are? Sometimes you need to know in order to make a good translation. This would be even better: "The ${file}'s size is ${size}, which is wrong" The usefulness of named arguments is even more apparent in complex message like this one: "${file}:${line}: Expecting ${expectedtoken}, got ${inputtoken}" Without named parameters, you'd have: "{0}:{1}: expecting {2}, got {3}" which is almost impossible to translate. What are {0} and {1}? What are {2} and {3}? Does it mean "12:30pm: expecting program to succeed, got general protection fault"? Using named parameters makes it clear this is a parser error, not something else. This difference may mean using a completely different grammatical structure to translate the message. T

Ruby on Rails uses name parameters: http://guides.rubyonrails.org/i18n.html -- /Jacob Carlborg
Feb 20 2012
prev sibling next sibling parent Juan Manuel Cabo <juanmanuel.cabo gmail.com> writes:
On 02/20/2012 03:23 PM, Jonathan M Davis wrote:
 I don't see how you could possibly make that uniform. It's very non-uniform by 
 its very nature. The handling _needs_ to be non-uniform.
 

The handling might need to be non-uniform, but the exception hierarchy doesn't. Not talking about i18n formatting now. Sometimes I'd like to add a 'trait' to an exception, but find myself needing to create a new exception type just for that, which will sit oddly in the hierarchy. Consider the case of rethrowing an exception with added detail. --jm
Feb 20 2012
prev sibling next sibling parent Jacob Carlborg <doob me.com> writes:
On 2012-02-20 19:42, H. S. Teoh wrote:
 On Mon, Feb 20, 2012 at 01:23:04PM -0500, Jonathan M Davis wrote:
 On Monday, February 20, 2012 11:57:07 Andrei Alexandrescu wrote:

 Exactly. I don't see how a disagreement follows from here. So isn't
 it reasonable to design the exception such that it can offer
 information pertaining to what went wrong, in a uniform manner?


 I don't see how you could possibly make that uniform. It's very
 non-uniform by its very nature. The handling _needs_ to be
 non-uniform.

I think there's a misunderstanding here. What Andrei is trying to achieve is something like this: class Exception : Error { Variant[string] data; string formatMsg(LocaleInfo li) { return formatLocale(li, msg, data); } } class GetOptException : Exception { this() { data["..."] = ...; ... } } I contend that using Variant here is not necessary. You can easily do this instead: class Exception : Error { string formatMsg(LocaleInfo li) { auto members = typeid(this).getMembers(null); string msg; foreach (member; members) { if (member.name.startsWith("info")) { ... //format this field } } return msg; } } class GetOptException : Exception { string infoOption; // this gets picked up by formatMsg int internalData; // this is ignored // No need to declare anything else except ctor to set // the above fields. } This allows for a much cleaner, type-checked access to infoOption, should the catching code know how to deal with GetOptException specifically. T

Currently getMembers doesn't work and if I recall correctly it's not possible to get the value out of what getMembers returns. But that might just be considered implementation issues. -- /Jacob Carlborg
Feb 20 2012
prev sibling next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Mon, Feb 20, 2012 at 06:38:08PM -0600, Andrei Alexandrescu wrote:
[...]
We shouldn't be using Variant[string] for this, because there's
another problem associated with it: suppose MyException sometimes
gets "extra_info1" and "extra_info2" tacked onto it, on its way up
the call stack, and sometimes not. Now what should the catcher do?

Use a central loop to render the information.

Then it's useless for i18n, unless you can magically translate a sequence of unknown values into a different language in a consistent way. It sounds like all this is good for is to print a list of "name=value" pairs. Which is useful, I'll admit, for development/debugging purposes. I don't see how such a thing is useful in i18n. [...]
This is what I mean by not being able to depend on whether some data
is there. Ultimately, to do anything useful with the info in the
object, you need to know what's there.

No. You do realize you are positioning yourself straight against every OO principle there is? The entire OO world is predicated on the assumption you _can_ do useful stuff with an object WITHOUT knowing what's there.

And the way OO does this is by having the *derived classes* do useful stuff behind a polymorphic interface. What the Variant[string] does is, as you have said previously, to replace the need for many derived classes save a few, thereby throwing away the polymorphism and presenting what amounts to an array of void* to the catch block. For you to now accuse me of going against OO principles is a really strange argument, I must say. I was in fact *advocating* the use of OO by putting the useful information in the derived classes, where they belong.
Preferably, the object's type will tell you exactly what's there,
then you do a simple map from type to list of available attributes
(e.g., map exception type to format string with known, static list of
attributes). But if the type doesn't guarantee what data will be
present, then your code becomes vastly more complex, you have to deal
with potentially all possible combinations of what's there and what
isn't.

I think you are misunderstanding the mechanisms involved. There is no combinatorial code, just simple iteration and such. Dealing with distinct exceptions with distinct code IS combinatorial, repetitive, and non-scalable.

Then pray tell how "simple iteration" will achieve the translation of the data in Variant[string] into a localized string, when there is no guarantee any field will be in the hash at all. Format strings obviously won't work, since you can't have a format string unless you already know what arguments are present. Or are you just going to individually print out "name=value" pairs? In which case, we've been talking at cross purposes. There is no i18n here at all. T -- Those who've learned LaTeX swear by it. Those who are learning LaTeX swear at it. -- Pete Bleackley
Feb 20 2012
prev sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Tue, Feb 21, 2012 at 09:32:35PM +0100, deadalnix wrote:
[...]
 So it doesn't help. Dulb subclasses of Exceptions are done mostly to
 be able to catch them. To avoid useless subclasses, we need a more
 precise way to catch Exception than the type only.

This is a good point. Has anybody even considered, *why* does catch match by type? Is there a good reason for that? Or is it just inherited from the rah rah days of OOP where "everything is an object", so since exception is part of everything, exception is an object, therefore we can just catch by object type?
From all the heated debate in this thread, it's clear that exceptions

contention and what amounts to workarounds and hacks. So I'm going to throw (har har) this very crazy and wild idea out there and let's see if it's viable: What if instead of catching by class, we catch by attribute matching? So instead of writing: try { ... } catch(SomeExceptionType e) { ... } catch(SomeOtherExceptionType e) { ... } catch(YetAnotherSillyException e) { ... } we write: try { ... } catch(e: exists!e.filename && e.failedOp is File.open) { // something } catch(e: e.is_transient && e.num_retries < 5) { // something else } // And why should we even need an exception object in the first // place? catch(time() >= oldtime+5000) { // This thing's been running for way too long, time to // do something drastic } Flamesuit on! ;-) T -- Famous last words: I *think* this will work...
Feb 21 2012
prev sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/20/12 11:56 AM, H. S. Teoh wrote:
 On Mon, Feb 20, 2012 at 11:11:08AM -0600, Andrei Alexandrescu wrote:
 On 2/20/12 11:05 AM, foobar wrote:

 Separation of concerns - exceptions are meant to notify the
 *developer* of errors. User facing error messages is a separate
 concern that exceptions should not be responsible for. it's not just
 outsourcing the translation strings, it's the developer's job to
 determine what if at all should be done with the exception.

At the end of the day, a human-readable error message must be properly formatted given some exception that signaled an error. So I disagree that exceptions are meant for the developer. They are mechanism, a means to an end.

No, exceptions *are* meant for the developer, because it's the developer who decides whether and how to to display it to the user.

And when she does decide so, isn't it desirable that the exception hierarchy provides good means to do so?
 Trivial example: My mom enters a misspelled URL (e.g. goggle.com) in
 her browser, she does not know or care what 404 means. instead she
 gets a formated page suggesting her to check her spelling and
 probably a suggestion to try google.com instead.

Sure, and the question is how the message gets created.

By encoding *useful* information in the exception, not just some generic stuff lacking in semantic meaning, so that the code that catches the exception knows what the problem is, and can make a sensible decision as to how to display it (or not display it, but react in some other way).

Sure. But to the extent the exception hierarchy can serve a helpful interface, that would only help.
 Again, this brings us back to class hierarchies. In order to react
 sensibly to an exception, the code that catches it *has* to know what it
 is.

Often code has a generic response to all exceptions or categories thereof.
 There's simply no way around this. Just saying "a problem happened"
 is unhelpful.

Of course. That's why it's worth looking into enhancing the functionality of the exception hierarchy. It's a classic of OO design: if you want reuse, push policy up.
 Code cannot divine the right course of action just by
 being told that a problem happened. It *needs* to know *what* happened.
 And the details of what happened depends entirely on the context in
 which it happened, so the most sensible solution is to use a class
 hierarchy where you add information to the bottom levels -- that's
 because that's where the information is!!

I don't think the details depend entirely on the context. Some aspects depend on the origin of the error, and some others depend on the context in which the error occurred. Both are needed.
 At the end of the day, using a Variant is no better than using a deep
 class hierarchy.

It is better because it pushes policy up and replaces formatting code duplicated across catch sites, with reusable table-based code.
 You're just encoding the exact same structure of
 information in a different way.

Yes. It's a way that is uniform, which makes it possible to push in the base class.
 You *still* have to know what kind of
 information is available in the Variant, just like you need to know
 which exception subclass to catch so that you can access the member
 variable that tells you what went wrong.

Yes. That's where the formatting template comes together with the concrete information.
 For user-facing code, you *need* the catching code to understand what
 kinds of exceptions can happen so that it can decide whether/how to
 display it to the user.

That doesn't contradict, prevent, or preclude designing class hierarchies for uniform interfaces.
 In this light, it doesn't make sense to have a fully generic,
 full-fledged i18n system encoded into Exception (i.e., into every single
 error the system might encounter).

Of course not. I'm just discussing adding interface to enable that possibility (and others).
 Only a tiny subset of exceptions even
 *need* to see the light of day, and require i18n.

Agreed.
 You're just reinventing class hierarchies using variants. To what end?

I don't think so. What happens here is simple OO design - pushing interface up so as to allow manipulation of bases instead of duplicating code dealing with concrete classes. Maybe this is all a misunderstanding. Allow me to explain what the intent is. I suggest we add a method (better than a member, upon further thinking): class Exception : Error { Variant[string] info() { return null; // no extra info here } ... } Classes that inherit Exception may override info: class GetOptException : Exception { Variant[string] info() { return [ "flag" : _flag ]; } ... string _flag; } Now code that wants to handle GetOptException specifically can definitely do so. The advantage of defining info() is that now we get to write code that does not need duplication across all concrete types, but instead gets to use Exception.info in conjunction with various formatting and rendering engines. Again, it's simple OO design. Andrei
Feb 20 2012
prev sibling next sibling parent deadalnix <deadalnix gmail.com> writes:
Le 20/02/2012 18:28, Jose Armando Garcia a crit :
 On Mon, Feb 20, 2012 at 2:37 PM, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org <mailto:SeeWebsiteForEmail erdani.org>>
 wrote:

     On 2/20/12 10:16 AM, Nick Sabalausky wrote:

         "Andrei Alexandrescu"<SeeWebsiteForEma__il erdani.org
         <mailto:SeeWebsiteForEmail erdani.org>>  wrote in message
         news:jhtq31$u8q$1 digitalmars.__com...


             Again, I think this thread clarified we need the
             "Variant[string] info;"
             member however we define the hierarchy.


         I disagree. I don't see a need for that.


     How would we address custom formatting of e.g. error messages?


 This may not be D. Gettext says to solve it as follow:

 throw new Exception(gettext("Cool English message at
 %s.").format(DateTime.now()))

 The gettext "compiler" goes through the code an generates all the
 strings that need to be localized. The translation teams modifies those
 string and you store them in file/map for that language. At runtime the
 i18n library turns gettext(...) into a query into that map and returns
 the actual localized string. There is a map for the entire process.

 Localization can also be disable at compile time by making gettext a
 template and generating a "noop" for that operation.

 I have been thinking of making a module for i18n model after gettext but
 taking advantage of D's language features. Is there some interest in this?

This is definitively something that should be integrated into phobos IMO.
 Thanks,
 -Jose

Feb 20 2012
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/20/12 11:28 AM, Jose Armando Garcia wrote:
 On Mon, Feb 20, 2012 at 2:37 PM, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org <mailto:SeeWebsiteForEmail erdani.org>>
 wrote:

     On 2/20/12 10:16 AM, Nick Sabalausky wrote:

         "Andrei Alexandrescu"<SeeWebsiteForEma__il erdani.org
         <mailto:SeeWebsiteForEmail erdani.org>>  wrote in message
         news:jhtq31$u8q$1 digitalmars.__com...


             Again, I think this thread clarified we need the
             "Variant[string] info;"
             member however we define the hierarchy.


         I disagree. I don't see a need for that.


     How would we address custom formatting of e.g. error messages?


 This may not be D. Gettext says to solve it as follow:

 throw new Exception(gettext("Cool English message at
 %s.").format(DateTime.now()))

 The gettext "compiler" goes through the code an generates all the
 strings that need to be localized. The translation teams modifies those
 string and you store them in file/map for that language. At runtime the
 i18n library turns gettext(...) into a query into that map and returns
 the actual localized string. There is a map for the entire process.

 Localization can also be disable at compile time by making gettext a
 template and generating a "noop" for that operation.

So this moves formatting from the catch point to the throw point. That's fine, but it forces the formatting effort upfront even when the message is not actually used and leads to duplication because each call site would need to provide the message entirely. I think it's worth investigating formatting at the catch site.
 I have been thinking of making a module for i18n model after gettext but
 taking advantage of D's language features. Is there some interest in this?

Yah, this would be of interest. Andrei
Feb 20 2012
parent deadalnix <deadalnix gmail.com> writes:
Le 20/02/2012 19:02, Andrei Alexandrescu a crit :
 On 2/20/12 11:28 AM, Jose Armando Garcia wrote:
 On Mon, Feb 20, 2012 at 2:37 PM, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org <mailto:SeeWebsiteForEmail erdani.org>>
 wrote:

 On 2/20/12 10:16 AM, Nick Sabalausky wrote:

 "Andrei Alexandrescu"<SeeWebsiteForEma__il erdani.org
 <mailto:SeeWebsiteForEmail erdani.org>> wrote in message
 news:jhtq31$u8q$1 digitalmars.__com...


 Again, I think this thread clarified we need the
 "Variant[string] info;"
 member however we define the hierarchy.


 I disagree. I don't see a need for that.


 How would we address custom formatting of e.g. error messages?


 This may not be D. Gettext says to solve it as follow:

 throw new Exception(gettext("Cool English message at
 %s.").format(DateTime.now()))

 The gettext "compiler" goes through the code an generates all the
 strings that need to be localized. The translation teams modifies those
 string and you store them in file/map for that language. At runtime the
 i18n library turns gettext(...) into a query into that map and returns
 the actual localized string. There is a map for the entire process.

 Localization can also be disable at compile time by making gettext a
 template and generating a "noop" for that operation.

So this moves formatting from the catch point to the throw point. That's fine, but it forces the formatting effort upfront even when the message is not actually used and leads to duplication because each call site would need to provide the message entirely. I think it's worth investigating formatting at the catch site.

This is not the way we want to go. You have a piece of code to describe what the problem is, another to descide what to do with that problem and again another to do something that may go wrong. Formatting is an error handling problem, and so formatting at the throw point is a bad separation of concerns. Plus, the way you want to format stuff is likely to depend on the application.
Feb 20 2012
prev sibling next sibling parent reply "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Monday, February 20, 2012 18:05:38 foobar wrote:
 Separation of concerns - exceptions are meant to notify the
 *developer* of errors. User facing error messages is a separate
 concern that exceptions should not be responsible for. it's not
 just outsourcing the translation strings, it's the developer's
 job to determine what if at all should be done with the exception.

Agreed. Users shouldn't be seeing exception messages. They are intended for the developer. Users don't know or care about them. You don't display error codes to users when something goes wrong do you? The point of exceptions is to allow the _program_ to react appropriately. In some cases, that means printing out a message to the user or popping up a dialogue. In many, it doesn't. We need exceptions to give the program the information that it needs to programmatically react to what went wrong, not print a nice, internationalized error message. And adding an internationalization mechanism would actually make things _worse_, because it complicates exceptions for something that they don't generally need, and it makes debugging harder, because you can't see the message as easily in the debugger. The fields and functions that exceptions have should be geared towards facilitating the program processing and recovering from the exception, not printing out error messages. And a big part of that is giving them fields which can be processed programmatically, not ways to print internationalized strings. - Jonathan M Davis
Feb 20 2012
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/20/12 1:15 PM, H. S. Teoh wrote:
 I think what Andrei wants to do is more along these lines:

 	catch(Exception e) {
 		writeln(formatLocaleString(e.fmt, e.data));
 	}

Appproximately, except the formatting string is not inside the exception.
 I think there's some merit to this idea. However, I'm still unsure about
 storing stuff in a Variant.

 For one thing, you either need some sort of format string in the
 exception object (as I have above), which is bad, as somebody else
 pointed out, because now you're mixing i18n code into exception code, or
 you need some way of figuring out what format to use for which
 exception. So ultimately, you'll still end up with with a huge switch
 statement,

no
 or a global table of all exceptions (not good for
 maintenance, now every time someone changes an exception he has to
 remember to update the table).

Tables (including database-store ones etc.) are a way of life in international applications.
 One solution, perhaps, is to have an i18n file containing mappings of
 exception types to message formats. So that you can have:

 	class Exception : Error {
 		Variant[string] info;
 		...
 	}

 	string[string] exceptionFormats = loadLocaleData();

 	string formatExceptionMsg(LocaleInfo li, Exception e) {
 		if (typeid(e).name in exceptionFormats) {
 			return format(exceptionFormats[typeid(e)],
 				e.info);
 		}
 		return e.msg;
 	}

That's quite sensible.
 This may be acceptable, if the catching code knows which formats are
 actually defined in the locale data, so it will only call
 formatExceptionMsg() if there's actually a format defined for it. This
 way, you don't need to have *every* exception translated, just those
 that your catching code will actually use.

Yah.
 The one objection I have with this, though, is that if the catching code
 wants to specifically catch, say, FileNotFoundException, and extract the
 offending filename from the exception object, it would have to do a
 string lookup in Exception.info, rather than just accessing
 FileNotFoundException.filename directly.

Speed of rendering exception messages is secondary, but point taken.
 Then if the Phobos maintainer renames the field, the code still compiles
 since the compiler has no way to know that Exception.info["filename"] is
 no longer set by the ctor of FileNotFoundException, so the problem will
 go undetected until the exception actually occurs, at which time the
 catching code gets a runtime error (very bad).

 Having the field directly in FileNotFoundException is better, because
 you get a compile-time error when the field is renamed, so the developer
 can fix it before it ships, rather than have the customer run into the
 runtime error.

Yah, it also means there's a lot of duplicated code which may contain other bugs.
 That's why I proposed to use runtime reflection to scan the exception
 object for applicable fields. Then you get the best of both worlds: the
 message formatter doesn't need to know what the fields are, and you get
 full compile-time type checking for catching code that directly accesses
 the fields.

Runtime reflection is indeed a more general realization of the Variant[string] thingie. A naming convention (e.g. prefix all members in Exception relevant to rendering with "info" would complete a nice scheme. Andrei
Feb 20 2012
prev sibling next sibling parent deadalnix <deadalnix gmail.com> writes:
Le 20/02/2012 20:04, Johannes Pfau a crit :
 Am Mon, 20 Feb 2012 15:28:33 -0200
 schrieb Jose Armando Garcia<jsancio gmail.com>:

 I have been thinking of making a module for i18n model after gettext
 but taking advantage of D's language features. Is there some interest
 in this?

Yep we need this. I also thought about developing an i18n module maybe we can work together on one. I won't have time for it the next 3 weeks though. Anyway, I think we should base our i18n module on boost.locale http://www.boost.org/doc/libs/1_48_0/libs/locale/doc/html/index.html but boost.locale is pretty big, we should probably start with the translation part only: http://www.boost.org/doc/libs/1_48_0/libs/locale/doc/html/index.html The API is quite good imho. It'll need some changes to fit D better, but I think this could become a nice D style API. Boost.locale uses the gettext format internally which is important. This way we can use all the gettext tools& websites and the translator community (https://www.transifex.net/start/). BTW: I already have code to read the gettext format, so that could be used.
 Thanks,
 -Jose


Great initiative, but can you please start this in a new thread ? This one is already quite hard to follow :D
Feb 20 2012
prev sibling next sibling parent reply "Nick Sabalausky" <a a.a> writes:
"Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message 
news:jhtss8$12mq$3 digitalmars.com...
 On 2/20/12 10:16 AM, Nick Sabalausky wrote:
 "Andrei Alexandrescu"<SeeWebsiteForEmail erdani.org>  wrote in message
 news:jhtq31$u8q$1 digitalmars.com...
 Again, I think this thread clarified we need the "Variant[string] info;"
 member however we define the hierarchy.

I disagree. I don't see a need for that.

How would we address custom formatting of e.g. error messages?

Maybe I've misunderstood your intent for this "Variant[string] info;" My understanding is that, for example, you want to replace this: ---------------------------------------------------- class Exception {} class FooException { string fooName; int fooID; bool didBarOccur; this(string fooName, int fooID, bool didBarOccur) { this.fooName = fooName; this.fooID= fooID; this.didBarOccur= didBarOccur; } } ---------------------------------------------------- With this: ---------------------------------------------------- class Exception { Variant[string] info; } class FooException { string fooName; int fooID; bool didBarOccur; this(string fooName, int fooID, bool didBarOccur) { this.fooName = fooName; this.fooID= fooID; this.didBarOccur= didBarOccur; info["fooName"] = fooName; info["fooID"] = fooID; info["didBarOccur"] = didBarOccur; } } ---------------------------------------------------- If so, then I don't see any usefulness of "Variant[string] info" other than to start treating exceptions like JS's abomination of an "object" (Or at least AS2's objects anyway - not 100% certain how much of AS2 is taken from JS). If not, then could you clarify what you meant? In either case, I am interested to hear in more detail how you see "Variant[string] info" being used to address i18n. I haven't really dealt with a lot of i18n myself.
Feb 20 2012
next sibling parent reply deadalnix <deadalnix gmail.com> writes:
Le 20/02/2012 20:12, Nick Sabalausky a écrit :
 "Andrei Alexandrescu"<SeeWebsiteForEmail erdani.org>  wrote in message
 news:jhtss8$12mq$3 digitalmars.com...
 On 2/20/12 10:16 AM, Nick Sabalausky wrote:
 "Andrei Alexandrescu"<SeeWebsiteForEmail erdani.org>   wrote in message
 news:jhtq31$u8q$1 digitalmars.com...
 Again, I think this thread clarified we need the "Variant[string] info;"
 member however we define the hierarchy.

I disagree. I don't see a need for that.

How would we address custom formatting of e.g. error messages?

Maybe I've misunderstood your intent for this "Variant[string] info;" My understanding is that, for example, you want to replace this: ---------------------------------------------------- class Exception {} class FooException { string fooName; int fooID; bool didBarOccur; this(string fooName, int fooID, bool didBarOccur) { this.fooName = fooName; this.fooID= fooID; this.didBarOccur= didBarOccur; } } ---------------------------------------------------- With this: ---------------------------------------------------- class Exception { Variant[string] info; } class FooException { string fooName; int fooID; bool didBarOccur; this(string fooName, int fooID, bool didBarOccur) { this.fooName = fooName; this.fooID= fooID; this.didBarOccur= didBarOccur; info["fooName"] = fooName; info["fooID"] = fooID; info["didBarOccur"] = didBarOccur; } } ---------------------------------------------------- If so, then I don't see any usefulness of "Variant[string] info" other than to start treating exceptions like JS's abomination of an "object" (Or at least AS2's objects anyway - not 100% certain how much of AS2 is taken from JS). If not, then could you clarify what you meant? In either case, I am interested to hear in more detail how you see "Variant[string] info" being used to address i18n. I haven't really dealt with a lot of i18n myself.

Why not use tupleof on the Exception instead of Variant[string] ?
Feb 20 2012
next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/20/12 1:23 PM, deadalnix wrote:
 Why not use tupleof on the Exception instead of Variant[string] ?

The one-liner police shall be alerted. You can't use tupleof because that's compile-time information. Andrei
Feb 20 2012
prev sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/20/12 1:38 PM, H. S. Teoh wrote:
 On Mon, Feb 20, 2012 at 08:23:05PM +0100, deadalnix wrote:
 [...]
 Why not use tupleof on the Exception instead of Variant[string] ?

+1. Compile-time type-checking and runtime generic access to fields. Best of both worlds. Excellent idea!

Doesn't scale. You need to literally write one catch handler for each exception type caught. Andrei
Feb 20 2012
prev sibling next sibling parent reply "Nick Sabalausky" <a a.a> writes:
"Nick Sabalausky" <a a.a> wrote in message 
news:jhu61p$1jpm$1 digitalmars.com...
 "Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message 
 news:jhtss8$12mq$3 digitalmars.com...
 On 2/20/12 10:16 AM, Nick Sabalausky wrote:
 "Andrei Alexandrescu"<SeeWebsiteForEmail erdani.org>  wrote in message
 news:jhtq31$u8q$1 digitalmars.com...
 Again, I think this thread clarified we need the "Variant[string] 
 info;"
 member however we define the hierarchy.

I disagree. I don't see a need for that.

How would we address custom formatting of e.g. error messages?

Maybe I've misunderstood your intent for this "Variant[string] info;" My understanding is that, for example, you want to replace this: ---------------------------------------------------- class Exception {} class FooException { string fooName; int fooID; bool didBarOccur; this(string fooName, int fooID, bool didBarOccur) { this.fooName = fooName; this.fooID= fooID; this.didBarOccur= didBarOccur; } } ---------------------------------------------------- With this: ---------------------------------------------------- class Exception { Variant[string] info; } class FooException { string fooName; int fooID; bool didBarOccur; this(string fooName, int fooID, bool didBarOccur) { this.fooName = fooName; this.fooID= fooID; this.didBarOccur= didBarOccur; info["fooName"] = fooName; info["fooID"] = fooID; info["didBarOccur"] = didBarOccur; } } ---------------------------------------------------- If so, then I don't see any usefulness of "Variant[string] info" other than to start treating exceptions like JS's abomination of an "object" (Or at least AS2's objects anyway - not 100% certain how much of AS2 is taken from JS). If not, then could you clarify what you meant? In either case, I am interested to hear in more detail how you see "Variant[string] info" being used to address i18n. I haven't really dealt with a lot of i18n myself.

Ok, I just saw your explanation here: http://forum.dlang.org/post/jhu1hk$1cb8$1 digitalmars.com And, IIUC, I assume that inside "stringTemplate()", you'd then access some table that roughly amounts to (yea, I know we don't have 2D AAs): string[string, Locale] i18nTable; i18nTable["FileNotFoundExcepton", English] = "File ${filename} not found"; Then stringTemplate() would look that up, find the string "File ${filename} not found" and essentially do: return "File "~e.info("filename")~" not found"; Although obviously not hardcoded like that. Is that right? In that case, I like the general idea, *but* why not just use reflection to access the members instead of essentially creating a JS-style "class" with AAs? That way we don't have to either A. throw away the benefits that class members have over AAs or B. violate DRY by duplicating field members in an AA.
Feb 20 2012
next sibling parent "Nick Sabalausky" <a a.a> writes:
"Nick Sabalausky" <a a.a> wrote in message 
news:jhu7kj$1mae$1 digitalmars.com...
 "Nick Sabalausky" <a a.a> wrote in message 
 news:jhu61p$1jpm$1 digitalmars.com...
 "Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message 
 news:jhtss8$12mq$3 digitalmars.com...
 On 2/20/12 10:16 AM, Nick Sabalausky wrote:
 "Andrei Alexandrescu"<SeeWebsiteForEmail erdani.org>  wrote in message
 news:jhtq31$u8q$1 digitalmars.com...
 Again, I think this thread clarified we need the "Variant[string] 
 info;"
 member however we define the hierarchy.

I disagree. I don't see a need for that.

How would we address custom formatting of e.g. error messages?

Maybe I've misunderstood your intent for this "Variant[string] info;" My understanding is that, for example, you want to replace this: ---------------------------------------------------- class Exception {} class FooException { string fooName; int fooID; bool didBarOccur; this(string fooName, int fooID, bool didBarOccur) { this.fooName = fooName; this.fooID= fooID; this.didBarOccur= didBarOccur; } } ---------------------------------------------------- With this: ---------------------------------------------------- class Exception { Variant[string] info; } class FooException { string fooName; int fooID; bool didBarOccur; this(string fooName, int fooID, bool didBarOccur) { this.fooName = fooName; this.fooID= fooID; this.didBarOccur= didBarOccur; info["fooName"] = fooName; info["fooID"] = fooID; info["didBarOccur"] = didBarOccur; } } ---------------------------------------------------- If so, then I don't see any usefulness of "Variant[string] info" other than to start treating exceptions like JS's abomination of an "object" (Or at least AS2's objects anyway - not 100% certain how much of AS2 is taken from JS). If not, then could you clarify what you meant? In either case, I am interested to hear in more detail how you see "Variant[string] info" being used to address i18n. I haven't really dealt with a lot of i18n myself.

Ok, I just saw your explanation here: http://forum.dlang.org/post/jhu1hk$1cb8$1 digitalmars.com And, IIUC, I assume that inside "stringTemplate()", you'd then access some table that roughly amounts to (yea, I know we don't have 2D AAs): string[string, Locale] i18nTable; i18nTable["FileNotFoundExcepton", English] = "File ${filename} not found"; Then stringTemplate() would look that up, find the string "File ${filename} not found" and essentially do: return "File "~e.info("filename")~" not found"; Although obviously not hardcoded like that. Is that right? In that case, I like the general idea, *but* why not just use reflection to access the members instead of essentially creating a JS-style "class" with AAs? That way we don't have to either A. throw away the benefits that class members have over AAs or B. violate DRY by duplicating field members in an AA.

Using reflection instead of "Variant[string] info" has another benefit: It allows the i18n table to *automatically* support any type, not just Exceptions. By contrast, with "Variant[string] info", a member has to be explicity (or via inheritance) added to every type that the i18n table wants to handle. So you're actally *creating* unnecessary cupling with i18n by using "Variant[string] info". If you replace "Variant[string] info" with reflection, then you've *completely* factored out i18n. Admittedly, if the i18n engine populates its tables at runtime, then the reflection would have to be runtime reflection, and I don't know offhand if we currently have that particular form of runtime reflection yet or not. But if not, then this is a good use-case for why it should get implemented sooner or later.
Feb 20 2012
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/20/12 2:01 PM, foobar wrote:
 Seconded. Reflection seems a much better solution for this.

Absolutely.
 The i18n table would map (exception type) -> (language, format string)
 and the i18n formatter would use reflection to map field values to the
 format string.
 a format string would be e.g. "File {filename} not found" and the
 formatter would replace {filename} with e.class.getField("filename").

 This does not require any modifications to the exception mechanism.

Yah, but the reflection engine would have to present RTTI in a uniform manner... such as, Variant[string]. Bane of my life: I'm always right :o). Andrei
Feb 20 2012
next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/20/12 4:46 PM, foobar wrote:
 On Monday, 20 February 2012 at 21:02:21 UTC, Andrei Alexandrescu wrote:
 On 2/20/12 2:01 PM, foobar wrote:
 Seconded. Reflection seems a much better solution for this.

Absolutely.
 The i18n table would map (exception type) -> (language, format string)
 and the i18n formatter would use reflection to map field values to the
 format string.
 a format string would be e.g. "File {filename} not found" and the
 formatter would replace {filename} with e.class.getField("filename").

 This does not require any modifications to the exception mechanism.

Yah, but the reflection engine would have to present RTTI in a uniform manner... such as, Variant[string]. Bane of my life: I'm always right :o). Andrei

Unfortunately you aren't :) The Variant[string] approach causes coupling between two separate concerns: i18n and error handling.

Not at all. It simply exposes information for any rendering purposes. In fact I imagine a simple renderer would just display to the developer the message formatted as a simple table with <name, value> for each property.
 Reflection does *not* cause such
 coupling. More over, Reflection is a general language mechanism with
 many benefits such as allowing the user to use the i18n library with
 other non exception types.

Sure. The question is whether reflection is enough. Juan made the case that dynamic paths would want to enhance an exception with additional information. Using reflection for that would be forced.
 Lastly, good RTTI is hardly a simple Variant[string]. Java for instance
 has pretty good RTTI API whereas the c++ one is horrible (and incomplete).

 Conclusion - *not* the same thing!

Yah, full-blown RTTI would be a fair amount more. Andrei
Feb 20 2012
prev sibling next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/20/12 4:46 PM, foobar wrote:
 On Monday, 20 February 2012 at 21:02:21 UTC, Andrei Alexandrescu wrote:
 On 2/20/12 2:01 PM, foobar wrote:
 Seconded. Reflection seems a much better solution for this.

Absolutely.
 The i18n table would map (exception type) -> (language, format string)
 and the i18n formatter would use reflection to map field values to the
 format string.
 a format string would be e.g. "File {filename} not found" and the
 formatter would replace {filename} with e.class.getField("filename").

 This does not require any modifications to the exception mechanism.

Yah, but the reflection engine would have to present RTTI in a uniform manner... such as, Variant[string]. Bane of my life: I'm always right :o). Andrei

Unfortunately you aren't :)

Esprit d'escalier! "Bane of my life: nobody admits I'm always right." Damn I wish I'd come up with that a minute ago before submitting. Andrei
Feb 20 2012
prev sibling parent "Nick Sabalausky" <a a.a> writes:
"Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message 
news:jhuccs$1tuf$4 digitalmars.com...
 On 2/20/12 2:01 PM, foobar wrote:
 Seconded. Reflection seems a much better solution for this.

Absolutely.
 The i18n table would map (exception type) -> (language, format string)
 and the i18n formatter would use reflection to map field values to the
 format string.
 a format string would be e.g. "File {filename} not found" and the
 formatter would replace {filename} with e.class.getField("filename").

 This does not require any modifications to the exception mechanism.

Yah, but the reflection engine would have to present RTTI in a uniform manner... such as, Variant[string]. Bane of my life: I'm always right :o).

It's easy to always be right when you twist your argument after-the-fact to match what was eventually agreed.
Feb 20 2012
prev sibling next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/20/12 1:41 PM, Sean Kelly wrote:
 Localized error messages are typically generated by a localization
 team and placed in some sort of a lookup table, indexed by language
 and error code.  Say the code is roughly like this:

 displayLocalizedError(Exception e) { auto t =
 loadTable(e.info["lang"]); // info["lang"] returns "ja" for japanese,
 etc. string m = t.findMessage(typeid(e).toString);
 writeln(buildLocalizedMessage(m, e.info)); }

I'd amend your example a bit as follows: displayLocalizedError(Exception e) { auto t = loadTable(g_lang); // g_lang contains "ja" for japanese, etc. string m = t.findMessage(typeid(e).toString); writeln(buildLocalizedMessage(m, e.info)); } I mean the exception has no notion of "lang"uage.
 Where m (in english) may be something like:

 "Saving to {LocationType} {!LocationName} failed because the
 destination is full."

 The message is parsed and when {LocalizationType} is encountered, the
 parser knows to replace it with another localized string indexed by
 "LocationType".  Then {!LocationName} is replaced by
 e.info["LocationName"], which is specific to the actual error that
 occurred.  In essence, each line that has to be localized has a
 monetary (and time) cost for doing so, so error lines are reused when
 possible.  Then specifics that may or may not themselves be localized
 are potentially pasted in to generate the final message.

Yup, yup, and yup. Andrei
Feb 20 2012
prev sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2012-02-20 20:12, Nick Sabalausky wrote:
 If so, then I don't see any usefulness of "Variant[string] info" other than
 to start treating exceptions like JS's abomination of an "object" (Or at
 least AS2's objects anyway - not 100% certain how much of AS2 is taken from
 JS). If not, then could you clarify what you meant?

 In either case, I am interested to hear in more detail how you see
 "Variant[string] info" being used to address i18n. I haven't really dealt
 with a lot of i18n myself.

Internationalization in Ruby on Rails works something like this: # set the locale I18n.locale = :se This will then, at an appropriate time load, a YAML file containing the translations, defaults too <rails_app>/config/<locale>.yml. The YAML file can look something like this: se: customer: address: first_name: Frnamn last_name: Efternamn Then to use the translation, it looks like this: I18n.translate("customer.address.first_name") Results in "Frnamn". It's also possible to pass variables to the translation: se: product: price: %{price} kr I18n.translate("product.price", :price => 300) ":price => 300" is a hash map-literal. -- /Jacob Carlborg
Feb 20 2012
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/20/12 3:08 PM, Jacob Carlborg wrote:
 On 2012-02-20 20:12, Nick Sabalausky wrote:
 If so, then I don't see any usefulness of "Variant[string] info" other
 than
 to start treating exceptions like JS's abomination of an "object" (Or at
 least AS2's objects anyway - not 100% certain how much of AS2 is taken
 from
 JS). If not, then could you clarify what you meant?

 In either case, I am interested to hear in more detail how you see
 "Variant[string] info" being used to address i18n. I haven't really dealt
 with a lot of i18n myself.

Internationalization in Ruby on Rails works something like this: # set the locale I18n.locale = :se This will then, at an appropriate time load, a YAML file containing the translations, defaults too <rails_app>/config/<locale>.yml. The YAML file can look something like this: se: customer: address: first_name: Frnamn last_name: Efternamn Then to use the translation, it looks like this: I18n.translate("customer.address.first_name") Results in "Frnamn". It's also possible to pass variables to the translation: se: product: price: %{price} kr I18n.translate("product.price", :price => 300) ":price => 300" is a hash map-literal.

This uses very nicely Ruby's built-in, elegant dynamic type information. The closest thing in D would use a hash Variant[string]. Andrei
Feb 20 2012
prev sibling next sibling parent Jacob Carlborg <doob me.com> writes:
On 2012-02-20 18:05, foobar wrote:
 On Monday, 20 February 2012 at 16:37:28 UTC, Andrei Alexandrescu wrote:
 On 2/20/12 10:16 AM, Nick Sabalausky wrote:
 "Andrei Alexandrescu"<SeeWebsiteForEmail erdani.org> wrote in message
 news:jhtq31$u8q$1 digitalmars.com...
 Again, I think this thread clarified we need the "Variant[string]
 info;"
 member however we define the hierarchy.

I disagree. I don't see a need for that.

How would we address custom formatting of e.g. error messages? Andrei

Separation of concerns - exceptions are meant to notify the *developer* of errors. User facing error messages is a separate concern that exceptions should not be responsible for. it's not just outsourcing the translation strings, it's the developer's job to determine what if at all should be done with the exception. Trivial example: My mom enters a misspelled URL (e.g. goggle.com) in her browser, she does not know or care what 404 means. instead she gets a formated page suggesting her to check her spelling and probably a suggestion to try google.com instead. the exception notifies the developer of the error, the developer does extra processing (e.g. to suggest similar valid websites) and the user get a friendly notification. clearly it doesn't make sense to put all this into the exception.

I completely agree, that's exactly how it should be done. I can't understand how difficult it seems to not see this. -- /Jacob Carlborg
Feb 20 2012
prev sibling parent Jacob Carlborg <doob me.com> writes:
On 2012-02-20 18:28, Jose Armando Garcia wrote:
 On Mon, Feb 20, 2012 at 2:37 PM, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org <mailto:SeeWebsiteForEmail erdani.org>>
 wrote:

     On 2/20/12 10:16 AM, Nick Sabalausky wrote:

         "Andrei Alexandrescu"<SeeWebsiteForEma__il erdani.org
         <mailto:SeeWebsiteForEmail erdani.org>>  wrote in message
         news:jhtq31$u8q$1 digitalmars.__com...


             Again, I think this thread clarified we need the
             "Variant[string] info;"
             member however we define the hierarchy.


         I disagree. I don't see a need for that.


     How would we address custom formatting of e.g. error messages?


 This may not be D. Gettext says to solve it as follow:

 throw new Exception(gettext("Cool English message at
 %s.").format(DateTime.now()))

 The gettext "compiler" goes through the code an generates all the
 strings that need to be localized. The translation teams modifies those
 string and you store them in file/map for that language. At runtime the
 i18n library turns gettext(...) into a query into that map and returns
 the actual localized string. There is a map for the entire process.

 Localization can also be disable at compile time by making gettext a
 template and generating a "noop" for that operation.

 I have been thinking of making a module for i18n model after gettext but
 taking advantage of D's language features. Is there some interest in this?

 Thanks,
 -Jose

Yes, but not for this purpose. -- /Jacob Carlborg
Feb 20 2012
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/20/12 10:31 AM, Sean Kelly wrote:
 On Feb 20, 2012, at 7:49 AM, Andrei
Alexandrescu<SeeWebsiteForEmail erdani.org>  wrote:
 Also, I think we can do better than defining the boilerplate constructor (see
e.g. https://github.com/D-Programming-Language/phobos/pull/439). It's just a
function. Consider:

 // this goes in the stdlib
 void raise(ConcreteException)(string message, Throwable t = null, string f =
__FILE__, size_t l = __LINE__)
 {
   auto r = new ConcreteException;
   r.message = message;
   r.file = f;
   r.line = l;
   r.next = t;
   throw r;
 }

 class AcmeException : Exception {}

 Now whenever you want to raise AcmeException, you say
raise!AcmeException("message"). Also, raise may accept additional data that
fills the Variant[string]. That makes exception definitions one-liners.

What is gained here over the current approach: throw new AcmeException("message"); Just eliminate the need for the ctor definition in the exception class?

Also possibly the definition of other constructors that set state. It's simple factorization. Andrei
Feb 20 2012
parent reply Mafi <mafi example.org> writes:
Am 20.02.2012 17:41, schrieb Andrei Alexandrescu:
 On 2/20/12 10:31 AM, Sean Kelly wrote:
 On Feb 20, 2012, at 7:49 AM, Andrei
 Alexandrescu<SeeWebsiteForEmail erdani.org> wrote:
 Also, I think we can do better than defining the boilerplate
 constructor (see e.g.
 https://github.com/D-Programming-Language/phobos/pull/439). It's just
 a function. Consider:

 // this goes in the stdlib
 void raise(ConcreteException)(string message, Throwable t = null,
 string f = __FILE__, size_t l = __LINE__)
 {
 auto r = new ConcreteException;
 r.message = message;
 r.file = f;
 r.line = l;
 r.next = t;
 throw r;
 }

 class AcmeException : Exception {}

 Now whenever you want to raise AcmeException, you say
 raise!AcmeException("message"). Also, raise may accept additional
 data that fills the Variant[string]. That makes exception definitions
 one-liners.

What is gained here over the current approach: throw new AcmeException("message"); Just eliminate the need for the ctor definition in the exception class?

Also possibly the definition of other constructors that set state. It's simple factorization. Andrei

If it's supposed to be simple factorization, then you should replace "throw r" with "return r". Then the name of that function doesn't make much sense anymore. But then you can better search for throw in user code and the stack traces aren't obfuscated anymore. throw createEx!AcmeException("...."); Mafi
Feb 20 2012
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/20/12 11:08 AM, Mafi wrote:
 If it's supposed to be simple factorization, then you should replace
 "throw r" with "return r". Then the name of that function doesn't make
 much sense anymore. But then you can better search for throw in user
 code and the stack traces aren't obfuscated anymore.

 throw createEx!AcmeException("....");

I think that's a great idea, thanks. Andrei
Feb 20 2012
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/20/12 11:32 AM, foobar wrote:
 On Monday, 20 February 2012 at 17:12:17 UTC, Andrei Alexandrescu wrote:
 On 2/20/12 11:08 AM, Mafi wrote:
 If it's supposed to be simple factorization, then you should replace
 "throw r" with "return r". Then the name of that function doesn't make
 much sense anymore. But then you can better search for throw in user
 code and the stack traces aren't obfuscated anymore.

 throw createEx!AcmeException("....");

I think that's a great idea, thanks. Andrei

I fail to see the point in this. Why is the above better than throw AcmeException("...."); If you want to avoid boilerplate code in the definition of AcmeException, this can be better accomplished with a mixin.

The advantage is that e.g. the compiler can see that flow ends at throw. Other languages have a "none" type that function may return to signal they never end. Andrei
Feb 20 2012
next sibling parent reply "Nick Sabalausky" <a a.a> writes:
"foobar" <foo bar.com> wrote in message 
news:uphvtoqkvtshnzlqoaus forum.dlang.org...
 I meant -
 what's the benefit of:
 throw createEx!AcmeException("....");
 vs.
 throw new AcmeException("....");

Fixed.
 As far as I can see, the former has no benefits over the simpler latter 
 option.

The benefit is that you don't have to create any boilerplate ctors in the definition of AcmeException. *However*, I think that: 1. That's an insufficient improvement to justify breaking the ultra-common "throw new NameOfException(args)" idiom. 2. The solution fails to cover the *entire* scope of the *real* problem: Classes that need to write boilerplate ctors which merely forward to the base class's ctors. This issue is *not* limited to Exceptions, but Andrei's proposes solution *only* covers the case with Exceptions. A better solution has already been proposed: class AcmeException : Exception { mixin inheritCtors!(); // Actual name open for bikeshedding } Now, yes, this *is* admittedly more boilerplate than: class AcmeException : Exception {} However, it's superior overall, because: 1. The solution is *contained* to the author of the exception's class. It's *not* a solution that leeks out and infects user code. 2. It solves the *whole* problem in the general case, not just for Exceptions. And you know what? Even if it's still deemed too much bolierplate, we can still just do this: mixin( createInheritedClass("AcmeException", "Exception") ); Or maybe this: // Second param is optional. // Not sure if this can be a template mixin or has to be string mixin. mixin createException!("AcmeException", "Exception");
Feb 20 2012
next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 02/20/2012 09:17 PM, Nick Sabalausky wrote:
      // Second param is optional.
      // Not sure if this can be a template mixin or has to be string mixin.
      mixin createException!("AcmeException", "Exception");

It can be a template mixin that mixes in a string mixin declaration.
Feb 20 2012
prev sibling next sibling parent deadalnix <deadalnix gmail.com> writes:
Le 20/02/2012 21:17, Nick Sabalausky a écrit :
 *However*, I think that:

 1. That's an insufficient improvement to justify breaking the ultra-common
 "throw new NameOfException(args)" idiom.

 2. The solution fails to cover the *entire* scope of the *real* problem:
 Classes that need to write boilerplate ctors which merely forward to the
 base class's ctors.  This issue is *not* limited to Exceptions, but Andrei's
 proposes solution *only* covers the case with Exceptions.

 A better solution has already been proposed:

      class AcmeException : Exception
      {
          mixin inheritCtors!();  // Actual name open for bikeshedding
      }

 Now, yes, this *is* admittedly more boilerplate than:

      class AcmeException : Exception {}

On IRC we discussed the following solution : class with no defined ctor get default ctor forwarding argument to parent's ctor. If one is defined, this default behaviour disapear. People were entusiasts about that. But I think we should discuss that in another thread.
Feb 20 2012
prev sibling parent "Nick Sabalausky" <a a.a> writes:
"foobar" <foo bar.com> wrote in message 
news:rsfpoesjcefsiwxoxgzm forum.dlang.org...
 On Monday, 20 February 2012 at 20:18:58 UTC, Nick Sabalausky wrote:
 "foobar" <foo bar.com> wrote in message
 news:uphvtoqkvtshnzlqoaus forum.dlang.org...
 I meant -
 what's the benefit of:
 throw createEx!AcmeException("....");
 vs.
 throw new AcmeException("....");

Fixed.

Huh? <confused>

You forgot the "new" in the second example. I make that mistake in my own code all the time :)
 I just want to add to the above valid points one comment:
 instead of the proposed inheritCtors D could e perhaps modified to make 
 ctors more uniform with other methods. ctors could be automatically 
 inherited if sub class does not define its own ctors and does not add new 
 fields.

 the above would simply become:

 class AcmeException : Exception {} // inherits super ctors automatically

I often forget that doesn't already happen. I think what confuses me is that non-inherited classes implicitly define "this(){}" if there's no other ctors (Which is very handy though. Haxe doesn't do that which I always find irritating).
Feb 20 2012
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/20/12 1:04 PM, foobar wrote:
 On Monday, 20 February 2012 at 17:47:35 UTC, Andrei Alexandrescu wrote:
 On 2/20/12 11:32 AM, foobar wrote:
 On Monday, 20 February 2012 at 17:12:17 UTC, Andrei Alexandrescu wrote:
 On 2/20/12 11:08 AM, Mafi wrote:
 If it's supposed to be simple factorization, then you should replace
 "throw r" with "return r". Then the name of that function doesn't make
 much sense anymore. But then you can better search for throw in user
 code and the stack traces aren't obfuscated anymore.

 throw createEx!AcmeException("....");

I think that's a great idea, thanks. Andrei

I fail to see the point in this. Why is the above better than throw AcmeException("...."); If you want to avoid boilerplate code in the definition of AcmeException, this can be better accomplished with a mixin.

The advantage is that e.g. the compiler can see that flow ends at throw. Other languages have a "none" type that function may return to signal they never end. Andrei

I meant - what's the benefit of: throw createEx!AcmeException("...."); vs. throw AcmeException("...."); As far as I can see, the former has no benefits over the simpler latter option.

That's simply a workaround for non-inherited constructors - nothing more should be read into it. Andrei
Feb 20 2012
parent "Nick Sabalausky" <a a.a> writes:
"Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message 
news:jhuaue$1rj5$1 digitalmars.com...
 On 2/20/12 1:04 PM, foobar wrote:
 On Monday, 20 February 2012 at 17:47:35 UTC, Andrei Alexandrescu wrote:
 On 2/20/12 11:32 AM, foobar wrote:
 On Monday, 20 February 2012 at 17:12:17 UTC, Andrei Alexandrescu wrote:
 On 2/20/12 11:08 AM, Mafi wrote:
 If it's supposed to be simple factorization, then you should replace
 "throw r" with "return r". Then the name of that function doesn't 
 make
 much sense anymore. But then you can better search for throw in user
 code and the stack traces aren't obfuscated anymore.

 throw createEx!AcmeException("....");

I think that's a great idea, thanks. Andrei

I fail to see the point in this. Why is the above better than throw AcmeException("...."); If you want to avoid boilerplate code in the definition of AcmeException, this can be better accomplished with a mixin.

The advantage is that e.g. the compiler can see that flow ends at throw. Other languages have a "none" type that function may return to signal they never end. Andrei

I meant - what's the benefit of: throw createEx!AcmeException("...."); vs. throw AcmeException("...."); As far as I can see, the former has no benefits over the simpler latter option.

That's simply a workaround for non-inherited constructors - nothing more should be read into it.

The issue is that it's an ugly and unnecessary workaround. A couple alternates have been suggested.
Feb 20 2012
prev sibling parent Sean Kelly <sean invisibleduck.org> writes:
I meant more that by ignoring the type matching system in favor of our =
own runtime convention we've prevented the compiler from doing any =
optimizations (or error checking) that may have been possible.

On Feb 20, 2012, at 12:15 PM, Juan Manuel Cabo wrote:

 I agree to disagree.
=20
 But about your argument for efficiency, any check that you do when
 examining an exception doesn't need to be lightning fast, after all,
 if you are looking at an exception object, it means that the stack
 got unwound, and any nano seconds that you spend doing this:
=20
 	catch (Exception ex) {
 		if ("mycustomfield" in ex) {
 			.. do something ..
 		}
 	}
=20
 which is just an "in" check in an associative array, which might never
 have more than 10 elements (so even linear search is appropiate),
 is overshadowed by the time that the runtime took to unwind the stack
 and serve the exception to your catch block.
=20
 --jm
=20
=20
 On 02/20/2012 05:05 PM, Sean Kelly wrote:
 On Feb 20, 2012, at 11:54 AM, Juan Manuel Cabo wrote:
=20
 About this part:
=20
 What you want is throw a COMException and link it to the original
 Exception. You have to consider Exception as a linkedlist, one
 being the cause of another.


The Variant[string] is an idea to help avoid people creating new =



 of exception types that don't add nothing.

I don't think this makes sense. To effectively use whatever's in the =


possible if type information is lost. Unless this determination is = moved to a run-time check of some field within the exception, and then = I'm making my code that much messier and less efficient by putting in = tests of this identifier against a list of constants. Personally, I = don't see any use for this table beyond providing context, much like we = already have with file, line, etc.
=20

Feb 20 2012
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/20/12 3:01 AM, foobar wrote:
 On Monday, 20 February 2012 at 07:10:39 UTC, Andrei Alexandrescu
 wrote:
 On 2/20/12 12:44 AM, foobar wrote:
 I just died a little reading this. Are you suggesting that in
 order to handle IO exceptions I need to: try { ...whatever... }
 catch (PackageException!"std.io") {...} } catch
 (PackageException!"tango.io") {...} } catch
 (PackageException!"otherLib.io") {...} ...

 What the hell is wrong with just using an IOException?

There's nothing wrong, but there's a possible misunderstanding. If tango.io and otherLib.io cooperate with std, then they'd originate exceptions in std.io (as opposed to their own). Do note that the issue is exactly the same if libraries use IOException - they all must agree on using the same nomenclature, whether it's called PackageException!"std.io" or IOException.

The above is patently wrong. Are you suggesting that tango.io and otherLib.io need to depend on Phobos IO?? If so, that removes the benefits of using 3rd party libraries. If that's not your intention (and I really hope it isn't!) than IOException must be defined in a *separate* module that tango can depend on.

Actually that just shuffles the matter around. Any setup does demand that some library (in this case most probably the standard library) will be a dependency knot because it defines the hierarchy that others should use.
 [Meta] side-note: It's extremely irritating when you demand utmost
 pedantic reasoning from others while you often answer without
 providing such pedantic reasoning yourself or worse answer with a
 single word posts. That shows a complete lack of respect for others.
 You seem to be of high regard for yourself which is not justified at
 all given this attitude.

When giving brief answers I was trying to maximize throughput in a couple of cases when the meaning was obvious from the context. Otherwise I do my best to elaborate my points. But I see how that can be irritating, I won't continue it. Andrei
Feb 20 2012
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/20/12 10:37 AM, foobar wrote:
 On Monday, 20 February 2012 at 15:50:08 UTC, Andrei Alexandrescu wrote:
 Actually that just shuffles the matter around. Any setup does demand
 that some library (in this case most probably the standard library) will
 be a dependency knot because it defines the hierarchy that others should
 use.

Not accurate. A 3rd party library that want to be compatible will no doubt depend on the standard library's _exception hierarchy_ but that does *not* mean it should depend on the parallel functionality in the standard library. Per our example with IO, if I use tango.io I certainly do not want my application code to include redundantly both std.io and tango.io. I wanted to use tango.io as an *alternative* to std.io.

This is a confusion. Using PackageException!"std.io" does not require importing std.io. Conversely, using std.IOException _does_ require importing std.exceptions or whatnot. So from a dependency management viewpoint, PackageException is superior to IOException. The converse disadvantage is that typos won't be caught during compilation. For example, using PackageException!"sdt.io" will go through no problem, but of course won't be caught by people waiting for a PackageException!"std.io". Andrei
Feb 20 2012
parent reply Juan Manuel Cabo <juanmanuel.cabo gmail.com> writes:
With all due respect, I don't see module exception categories
as something good for the categorization of exceptions.

As I said in a previous post, long lost in this mega thread,
please don't create exception categories, unless it makes
sense to write a   catch (MyNewShinyCategory ex) {}  for it.
An artificial category would be in the way of other criteria
that aids catch grouping better.
(For instance, many different modules might want to add a
kind of IOException (not that I advocate the IOException
category, this is just for illustration))

--jm


On 02/20/2012 01:45 PM, Andrei Alexandrescu wrote:
 On 2/20/12 10:37 AM, foobar wrote:
 On Monday, 20 February 2012 at 15:50:08 UTC, Andrei Alexandrescu wrote:
 Actually that just shuffles the matter around. Any setup does demand
 that some library (in this case most probably the standard library) will
 be a dependency knot because it defines the hierarchy that others should
 use.

Not accurate. A 3rd party library that want to be compatible will no doubt depend on the standard library's _exception hierarchy_ but that does *not* mean it should depend on the parallel functionality in the standard library. Per our example with IO, if I use tango.io I certainly do not want my application code to include redundantly both std.io and tango.io. I wanted to use tango.io as an *alternative* to std.io.

This is a confusion. Using PackageException!"std.io" does not require importing std.io. Conversely, using std.IOException _does_ require importing std.exceptions or whatnot. So from a dependency management viewpoint, PackageException is superior to IOException. The converse disadvantage is that typos won't be caught during compilation. For example, using PackageException!"sdt.io" will go through no problem, but of course won't be caught by people waiting for a PackageException!"std.io". Andrei

Feb 20 2012
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/20/12 2:10 PM, Juan Manuel Cabo wrote:
 With all due respect, I don't see module exception categories
 as something good for the categorization of exceptions.

Yah, that's been since destroyed. Bane of my life: I'm never right :o). Andrei
Feb 20 2012
prev sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday, February 19, 2012 14:57:08 Andrei Alexandrescu wrote:
 On 2/19/12 1:19 PM, Nick Sabalausky wrote:
 That wouldn't be as useful. What the catcher is typically interested in is
 *what* happened, not *where* it happened.

But module organization is partitioned by functional areas.

Yes and no. That's mostly true in something like the standard library, but it begins to blur even there. For instance, what if a function ends up doing some unicode related stuff internally (probably as an optimization for string or somesuch) and ends up needs an enforce for something related to that. What's it going to throw? It should throw a UTFException. But the function itself could be in a completely different module than std.utf. _Most_ of the stuff that deals with unicode in a manner that would require throwing a UTFExcepton is in std.utf, but that doesn't mean that it all is. And if someone is writing code outside of Phobos, it could be that a Phobos exception is the best one to use - in fact with a really well designed exception hierarchy, that's often what's supposed to happen. Maybe they're doing something file-related and a FileException makes good sense. Then they should use that. In none of these cases does the module have anything to do with the exception type. Because most modules in Phobos _are_ organized by functionality, the exceptions tend to be done on a per module basis, but that's not really where the focus should be, and in some cases, we've taken it too far, making exception types specifically for a module, because we're giving each module its own exception type rather than because that module happens to encompass a particular type of functionality that merits a specific exception type. So, the focus needs to be on the type of exception, not where it came from. Having exception-handling code care about which module an exception came from would just be increasing coupling to no real benfit IMHO. It may be useful to the programmer, but not the program, and the stack trace should already have it. - Jonathan M Davis
Feb 19 2012
prev sibling next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Sun, Feb 19, 2012 at 01:05:25AM -0600, Andrei Alexandrescu wrote:
[...]
 Ideally we'd have the right number of types - not more, not less.  The
 main purpose of this thread is to figure how many exception types are
 "just right". There has been a tendency in Phobos to just add a
 module-specific exception to certain modules, without a good reason.
 I'm trying to figure out the reasons.

As a first stab at some reasons, what about something along these lines: - Would the *user* of the module care to catch that particular (group of) exception(s) as opposed to just catching all exceptions in general? Does that exception give semantically valuable information that the user might want to act on, or it is just programmer convenience (it throws X just because it belongs to module Y)? - Does it add domain-specific error information that is useful (errno, Oracle error codes, etc.)?
In the case of getopt, at _least_ adding a GetOptException with a
field for the failed flag would be very valuable, and having
additional derived types which indicate _why_ it failed would also be
valuable.

The additional state sounds fine, but I'm not so sure about adding one type per failure reason.

When to stop introducing leaf nodes in the hierarchy is something worth thinking about in more depth. There are some pros and cons to consider. To make the discussion grounded in reality, let's consider the example of file I/O. We could either (1) introduce a base class, say IOException, along with as many subclasses as necessary to capture all possible I/O errors (with possibly multiple layers of exceptions); or (2) introduce a base class IOException with no subclasses, but some attributes like errno or something equivalent, that describes the error. Or (3), some compromise between (1) and (2). For (1): - Pros: - You can be as general or as specific as needed. If you want to catch a specific error, you can. If you want to catch all I/O exceptions, you can. - Encapsulates OS and platform-specific errors in generic cross platform types that can be handled generically. (I.e., you don't have to know that std.io uses errno in interacting with the OS, and you don't have to know what errno values are. A generic FileNotFoundException properly maps to whatever OS you compile on.) - Cons: - Too many tiny classes, many differing only in type with no real additional information beyond the type itself. - Does not necessarily capture everything the user may want to catch. Say under IOException you have FileException and SocketException, with more classes underneath. If you want to catch a subset of classes directly under FileException, there's no clean way to do so unless we expand the type-matching in catch() blocks. - May require a lot of effort to implement but only with diminishing returns (most programs won't bother with the distinction between two finely-differentiated exceptions). For (2): - Pros: - No class bloat. - Don't have to wade through reams of documentation to know what to catch. Just catch IOException and decide what to do based on the data encoded (say, errno value, or equivalent). - Cons: - Risks exposing OS-specific and implementation-specific details to caller unnecessarily. E.g., if you use errno, then caller is tied to C's stdio. Then you can't change the underlying implementation to, say, iostream, or something else altogether, without massive breakage. - To only catch a specific error, you'd have to catch IOException, check the error code, and rethrow if it isn't what you want. Ugly. (It's like saying, oh, I can handle all IOExceptions, oh here's an IOException! Er, nevermind, I was kidding, I don't handle this particular IOException after all.) I don't have the answers. Just want to put the pros and cons down so that we have something concrete to debate about.
And in some cases at least, that would lead to more member fields in
the derived exceptions (like the value of the flag if it were given a
bad value).

And aside from that? The universe of possible errors in getopt is finite, closed, and actually fairly small. Can't we do with fewer types?

Getopt isn't really the best use case for elaborate exception hierarchies. Perhaps we should use file I/O or network/socket I/O instead. [...]
 How about a system in which you can say whether an exception is I/O
 related, network related, recoverable or not, should be displayed to
 the user or not, etc. Such is difficult to represent with inheritance
 alone.

The problem is, the thrower can't make the decision of whether or not something should be displayed to the user. You simply don't have the information nor context to decide that. That is completely dependent on what the calling app is trying to accomplish. A disk diagnostic program will want to display everything to the user, all the full gory details. A 3D shooter just wants to say "yo, something's wrong with da filesystem, I be quittin'!" and abort. Displaying some obscure error about some obscure problem with some obscure data file is worthless to the player. A database server backend has no *way* to display any error at all, just log it to some logfile. And server logfiles generally want to record ALL exceptions, not just some subset arbitrarily assigned by Phobos developers. Same thing with whether something is recoverable. The most horrendous I/O errors are "recoverable" from the POV of a disk repair program. The same errors should cause a word-processing app to die horribly instead of blindly charging forward and causing more data loss. You simply have no way of deciding this at the library level. As for marking whether an exception is network-related: you're just bloating the base class with mostly-irrelevant information. Why does every file I/O exception have to indicate they are not network errors? Why does every parser error have to say they are not network errors? Lexer errors? Regex errors? Database file corruption errors? The whole point of using a *class hierarchy* is so that all network errors derive from NetworkException, which in turn derives from Exception. The value of is_network_related is automatically, cleanly, and logically encapsulated by the act of defining a subclass of Exception. You don't need to explicitly represent it. It's implied by the existence of the subtype (which translates to a kind of representation in the form of the associated Typeinfo, if you'd like to think of it that way). [...]
 I think right now we're erring a bit on the side of defining useless
 and uninformative exception types. As far as the originating module
 goes, it makes perfect sense to make that a field in the base class.

But "useless" and "uninformative" is relative to what the *caller* wants to achieve. The difference between FileNotFound and ReadError is irrelevant to a program that's trying to load startup configuration files. But a program that's trying to decide whether or not to prompt the user for a different filename needs to know this distinction. The *same* program won't care for this distinction when *it* is trying to load configuration files. Basically, there's no sane way you can decide this for the calling app at the library level.
 Anyway, a simple action items right now is improve Exception's
 offering with things like e.g. custom formatting for i18n, more origin
 information, cloning, attributes and capabilities (transitory,
 user-visible etc).

But putting too much into the Exception base class risks having a whole bunch of fields that nobody uses. Why should you need to specify 10 different attributes, most of which you don't care about, every time you throw an exception? Why not only introduce additional fields as necessary? I.e., use a class hierarchy! That's what a class hierarchy is for, to keep the base class providing only *basic* functionality, and derived classes to add additional info for those subsets of exceptions that need that info. Again, I'm not saying that an elaborated exception hierarchy is the be all and end all of exception handling. It's just that I have yet to see a better system that provides those attributes I listed in another reply: the ability to be as general/specific as needed, and a way to programmatically recover from an exception based on information provided by the exception object (which entails domain-specific errors encoded in a sane way). If you can come up with a better system, by all means, please do. I'm listening. T -- If you look at a thing nine hundred and ninety-nine times, you are perfectly safe; if you look at it the thousandth time, you are in frightful danger of seeing it for the first time. -- G. K. Chesterton
Feb 19 2012
prev sibling next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Sat, Feb 18, 2012 at 11:52:00PM -0800, Jonathan M Davis wrote:
[...]
 So, while at first glance, it seems like a good idea, I think that it
 has too many issues as-is to work. It might be possible to adjust the
 idea to make it workable though. Right now, it's possible to do it via
 mixins or calling a function inside the catch, but doing something
 similar to this would certainly be nice, assuming that we could sort
 out the kinks.

I have an idea. What about "signature constraints" for catch, ala template signature constraints? Something like this: try { ... } catch(IOException e) if (e.errno in subsetYouWantToHandle) { ... } Just using IOException as an example. The idea is to allow arbitrary expressions in the constraint so whatever doesn't satisfy the constraint will be regarded as "not caught", even if the base type matches. T -- Gone Chopin. Bach in a minuet.
Feb 19 2012
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday, February 19, 2012 02:04:50 Andrei Alexandrescu wrote:
 On 2/19/12 12:56 AM, Jonathan M Davis wrote:
 I think that being able to have a catch block which took multiple
 exception
 types would be plenty. There are times when it would be very valuable to
 be
 able to use the same catch block for multiple exceptions without having to
 catch their base type (which would then potentially catch other exceptions
 which you didn't want to catch). So, something like that second catch
 block
 that you have there would be very valuable.

So now hierarchies are not good?

No. Sometimes you want to catch specific types and handle a subset of them in a particular way but don't want to handle _all_ of the exceptions with the same base class the same way. For instance, if you had the following and all of them are derived from FileException (save FileException itself): catch(e : FileNotFoundException, NotAFileException) { //... } catch(AccessDeniedException e) { //... } catch(FileException e) { //... } You want to handle certain exceptions differently and you want to handle some of them the same, but you don't want to handle all FileExceptions the same way. Without a way to put multiple exception types in the same block, you tend to end up with code duplication (and no I'm not necessarily advocating that we have those specific exception types - they're just examples). It's even worse if you don't have much of a hierarchy, since if _everything_ is derived from Exception directly, then catching the common type - Exception - would catch _everything_. For instance, what if you want to handle StringException and UTFException together but FileException differently? You can't currently do catch(e : StringException, UTFException) { //... } catch(FileException e) { //... } Right now, you'd have to have separate catch blocks for StringException and UTFException. A well-designed exception hierarchy reduces the problem considerably, because then there's a much higher chance that catching a common exception will catch what you want and not what you don't, but that doesn't mean that you're never going to run into cases where catching the common type doesn't work. - Jonathan M Davis
Feb 19 2012
prev sibling next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Sun, Feb 19, 2012 at 02:02:39AM -0600, Andrei Alexandrescu wrote:
 On 2/19/12 12:54 AM, H. S. Teoh wrote:
On Sun, Feb 19, 2012 at 12:43:58AM -0600, Andrei Alexandrescu wrote:


I'm thinking an error is transient if retrying the operation with
the same exact data may succeed. That's a definition that's simple,
useful, and easy to operate with.

But if that's the case, what's the use of an exception at all?

Centralization.

I don't understand. So instead of providing enough information to the caller about the nature of the problem, you're essentially handing them an anonymous note saying "A generic problem occurred, which _may_ go away if you retry. I have no further information for you. Do you want to retry?"? But without further information, how *can* you even make that decision? Without any way of determining what caused the error or even what it is, how could you know whether it makes sense to retry it? Or is that transient flag intended to mean that it *should* be retried since it "might" succeed the next time round? How should the caller decide whether or not to go ahead with the retry? Flip a coin? Always retry? Always fail? I can't imagine any sane application where code would say "if this operation fails with a transient error, always fail" where any arbitrary set of exceptions might potentially be transient? What's a "transient error" anyway, from the application's POV anyway? What's a "transient error" from a database app's POV? A 3D shooter? A text editor? Is it even possible to have a consistent definition of "transient" that is meaningful across all applications? It seems to me that if an error is "transient", then the called function might as well just always retry it in the first place, instead of throwing an exception. Such an exception is completely meaningless to the caller without further information. Yet it seems that you are suggesting that it's more meaningful than FileNotFoundException? T -- Obviously, some things aren't very obvious.
Feb 19 2012
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday, February 19, 2012 02:06:38 Andrei Alexandrescu wrote:
 On 2/19/12 1:12 AM, Jonathan M Davis wrote:
 On Sunday, February 19, 2012 00:43:58 Andrei Alexandrescu wrote:
 On 2/18/12 8:00 PM, H. S. Teoh wrote:
    From this and other posts I'd say we need to design the base
    exception
 
 classes better, for example by defining an overridable property
 isTransient that tells caller code whether retrying might help.

Just because an exception is transient doesn't mean it makes sense to try again. For example, saveFileMenu() might read a filename from the user, then save the data to a file. If the user types an invalid filename, you will get an InvalidFilename exception. From an abstract point of view, an invalid filename is not a transient problem: retrying the invalid filename won't make the problem go away. But the application in this case *wants* to repeat the operation by asking the user for a *different* filename. On the other hand, if the same exception happens in an app that's trying to read a configuration file, then it *shouldn't* retry the operation.

I'm thinking an error is transient if retrying the operation with the same exact data may succeed. That's a definition that's simple, useful, and easy to operate with.

A core problem with the idea is that whether or not it makes sense to try again depends on what the caller is doing. In general, I think that it's best to give the caller as much useful information is possible so that _it_ can decide the best way to handle the exception.

That sounds like "I violently agree".

Then I'm confused. "As much information as possible" is way more than a transient property. If my code is going to retry or do something else or give up, it needs enough information to know what went wrong, not just whether the function which was called think it might work on a second try. Having an exception hierarchy provides some of that information simply with the types, and makes it easier to organize code based on what went wrong (e.g. separate catch blocks for each type of exception). And having that hierarchy also means that the derived types can have additional information beyond their type which could be useful but is specific to that problem and so wouldn't make sense on a general exception type. I really don't see what transient buys you in comparison to that. - Jonathan M Davis
Feb 19 2012
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday, February 19, 2012 00:26:57 H. S. Teoh wrote:
 On Sat, Feb 18, 2012 at 11:52:00PM -0800, Jonathan M Davis wrote:
 [...]
 
 So, while at first glance, it seems like a good idea, I think that it
 has too many issues as-is to work. It might be possible to adjust the
 idea to make it workable though. Right now, it's possible to do it via
 mixins or calling a function inside the catch, but doing something
 similar to this would certainly be nice, assuming that we could sort
 out the kinks.

[...] I have an idea. What about "signature constraints" for catch, ala template signature constraints? Something like this: try { ... } catch(IOException e) if (e.errno in subsetYouWantToHandle) { ... } Just using IOException as an example. The idea is to allow arbitrary expressions in the constraint so whatever doesn't satisfy the constraint will be regarded as "not caught", even if the base type matches.

Interesting. But I don't think that it really buys you all that much, since normally what I'd think that you would want would simply be a list of the exception types that that particular catch block would handle. What you're suggesting would appear to end up being pretty much just ths: try { //... } catch(IOException e) { if(e.errno in subsetYouWantToHandle) { } else { } } save for the fact that if the condition were false, in your example, you could presumably have a catch(Exception e) block following it which would catch it. Honestly though, I think that something like Daniel's suggesting would be plenty, and it's much more explicit. Also, your suggestion might be a bit confusing in that it looks like a template constraint (which is evaluated at compile time) and yet it would have to be evaluated at runtime. That risks too much confusion IMHO. It's an interesting idea though. - Jonathan M Davis
Feb 19 2012
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday, February 19, 2012 19:00:20 Daniel Murphy wrote:
 I wasn't really serious about implicit fallthrough.

Lately, it seems like I can never tell whether anyone's being serious or not online. :)
 Out of the syntaxes I could come up with:
 catch(Ex1, Ex2 e)
 catch(e : Ex1, Ex2)
 catch(Ex1 | Ex2 e) // java 7 syntax, horrible
 
 I like (e : list) the best.  Naturally it would also accept a type tuple of
 exceptions.
 
 http://d.puremagic.com/issues/show_bug.cgi?id=7540

LOL. Personally, I actually think that the Java 7 syntax looks great (I'd never seen it before), but catch(e : Ex1, Ex2) is just as good and more consistent with the language as a whole, since it doesn't try to give any operators a new meaning (as Java's does). - Jonathan M Davis
Feb 19 2012
prev sibling next sibling parent "Juan Manuel Cabo" <juanmanuel.cabo gmail.com> writes:
Hello D community! This is my first post!! I hope I can bring 
clarity to all this. If not, I apologize.

Some time ago I researched the best way to classify exceptions 
and build a hierarchy. I came up with the following rules:


1) At the top level, there would be RecoverableExceptions and 
FatalExceptions (or you can call them something like 
CatcheableException and FatalExceptions, or Exception and Error).

2) Fatal exceptions shouldn't be catched. They imply that the 
program lost basic guarantees to go on (memory corruption, 
missing essential file, etc.). You could catch them if you wanted 
to, but it makes no sense other than at your top-level method 
(main(), etc.).

3) A RecoverableException can be converted to a FatalException by 
rethrowing it, once a catch decides so. You shouldn't do the 
reverse: a FatalException never should be converted to a 
RecoverableException.

4) It makes no sense to subclass FatalExceptions since there 
won't be a catch that groups them in a base type (since they are 
not catcheable).

5) Only create a new RecoverableException class type if it makes 
sense to write a catch for it alone. Otherwise, use an 
preexisting type.

6) Only group RecoverableExceptions in a category if it makes 
sense to write a catch for that category. Please don't group them 
because its fancy or "cleaner", that is a bad reason.


Who decides when an Exception is Unrecoverable? Library code 
almost never decides it, since an exception is only unrecoverable 
if the whole_program_invariant got broken, and libraries are only 
a part of a program. So they will tend to throw 
RecoverableExceptions, which can be reconverted to Unrecoverable 
by the program.

In some cases, it is clear that an exception is Unrecoverable. 
When you call a function without satisfying its arguments 
precondition (ie: null argument) the only way to fix that is by 
editing the program. You shouldn't have called it like that in 
the first place, why would you? So you let the 
UnrecoverableException bubble up to your main function, and log 
its stacktrace to fix it.

Unrecoverable means the program got 'tainted', basic guarantees 
got broken (possible memory corruption, etc.). Most exceptions 
will be Recoverable.

Now, expanding on the hierarchy: I said that it makes no sense to 
subclass UnrecoverableExceptions. Recoverable exceptions on the 
other hand, need to be subclassed _with_the_catch_on_your_mind_. 
You are passing info from the throw site to the catch site. The 
catch block is the interpreter of the info, the observer. You are 
communicating something to the catch block.

So please, do not create a new types if there is no value in 
writing a catch that only cathes that exception and that can 
recover from that exception. Otherwise, use an existing type.


I wrote these rules some time ago. Please excuse me if they come 
off a bit pedantic!!!!!!!!!!!! Its all only a clarifying 
convention.


According to all this:

* FileNotFoundException is useful. It tells you what happened. It 
is a RecoverableException (under my convention) because until it 
reaches the program, the library doesn't know if the program can 
recover from that (it could be a system missing file, or just a 
document the user asked for).

* DiskFailureException is only useful if someone can write a 
catch for it. If so, then it is a RecoverableException. Only the 
program can decide if it broke basic guarantees.

* Most argument exceptions are Unrecoverable. A function throwing 
shouldn't have been called like that in the first place. The only 
fix is to go back to editing the program. (precondition broken).


Another thing: you cannot decide whether an exception is 
Unrecoverable based only on whether the thing that got broken is 
the postcondition of a function. It is the 
whole_program_invariant that decides that. For instance:  
findStuff(someStuff)  might not know if someStuff is important 
enough for the stability of the program if not found. The 
postcondition is broken if it doesn't return the Stuff. That 
might be recoverable.

And PLEASE: don't make classifications by the point of view of 
the cause of the problem. DO make classifications by the point of 
view of the fixing/recovery of the problem; the catch block is 
who you are talking to. FileNotFoundBecauseFilesystemUnmounted is 
worthless.

So, to sum up: (1) it makes no sense to subclass fatal 
exceptions, and (2) never subclass a RecoverableException if you 
are not helping a catch block with that (but please do if it aids 
recovery).

..so verbose and pedantic for my first post... yikes.. i beg 
forgiveness!!!




On Sunday, 19 February 2012 at 09:27:48 UTC, Jonathan M Davis 
wrote:
 On Sunday, February 19, 2012 19:00:20 Daniel Murphy wrote:
 I wasn't really serious about implicit fallthrough.

Lately, it seems like I can never tell whether anyone's being serious or not online. :)
 Out of the syntaxes I could come up with:
 catch(Ex1, Ex2 e)
 catch(e : Ex1, Ex2)
 catch(Ex1 | Ex2 e) // java 7 syntax, horrible
 
 I like (e : list) the best.  Naturally it would also accept a 
 type tuple of
 exceptions.
 
 http://d.puremagic.com/issues/show_bug.cgi?id=7540

LOL. Personally, I actually think that the Java 7 syntax looks great (I'd never seen it before), but catch(e : Ex1, Ex2) is just as good and more consistent with the language as a whole, since it doesn't try to give any operators a new meaning (as Java's does). - Jonathan M Davis

Feb 19 2012
prev sibling next sibling parent "Juan Manuel Cabo" <juanmanuel.cabo gmail.com> writes:
That proposed syntax is nicer than this, but at least you can do 
it this way:
just call the same function from both catch blocks.

#!/usr/bin/rdmd
import std.stdio, std.utf, std.string;
void main() {
	void handleStringAndUtf(Exception ex) {
		if (typeid(ex).name == "std.utf.UTFException") {
			// .. UtfException specific handling ..
			writeln("length: ", (cast(UTFException)ex).len);
		}
		// .. handling of UtfException and StringException in common ..
		writeln(ex.toString);
		writeln(typeid(ex).name);
	}

	try {
		throw new UtfException("");
		//throw new StringException("aaaa");
	} catch (StringException ex) {
		handleStringAndUtf(ex);
	} catch (UTFException ex) {
		handleStringAndUtf(ex);
	}
}


--jm



On Sunday, 19 February 2012 at 09:12:40 UTC, Jonathan M Davis 
wrote:
 On Sunday, February 19, 2012 02:04:50 Andrei Alexandrescu wrote:
 On 2/19/12 12:56 AM, Jonathan M Davis wrote:

subset of them in a particular way but don't want to handle _all_ of the exceptions with the same base class the same way. For instance, if you had the following and all of them are derived from FileException (save FileException itself): catch(e : FileNotFoundException, NotAFileException) { //... } catch(AccessDeniedException e) { //... } catch(FileException e) { //... } You want to handle certain exceptions differently and you want to handle some of them the same, but you don't want to handle all FileExceptions the same way. Without a way to put multiple exception types in the same block, you tend to end up with code duplication (and no I'm not necessarily advocating that we have those specific exception types - they're just examples). It's even worse if you don't have much of a hierarchy, since if _everything_ is derived from Exception directly, then catching the common type - Exception - would catch _everything_. For instance, what if you want to handle StringException and UTFException together but FileException differently? You can't currently do catch(e : StringException, UTFException) { //... } catch(FileException e) { //... } Right now, you'd have to have separate catch blocks for StringException and UTFException. A well-designed exception hierarchy reduces the problem considerably, because then there's a much higher chance that catching a common exception will catch what you want and not what you don't, but that doesn't mean that you're never going to run into cases where catching the common type doesn't work. - Jonathan M Davis

Feb 19 2012
prev sibling next sibling parent "Juan Manuel Cabo" <juanmanuel.cabo gmail.com> writes:
How about adding a string[string] or a variant[string] to the 
Exception class, so one can know details about the subclassed 
exception without downcasting? How ugly would that be?

For instance:

     ...
     catch (Exception ex) {
       if ("transient" in ex.details) {
             repeatOneMoreTime();
       }
       if ("i18n_code" in ex.details) {
             log(translate(ex.details["i18n_code"]));
       }
     }
     ...

Details can be standard by convention or otherwise custom.
(I can see that this can lead to messy proliferation of details, 
but at least solves most of the issues).

--jm (BIG FAN OF D. GUYS I LOVE ALL YOUR GOOD WORK)




On Sunday, 19 February 2012 at 08:06:38 UTC, Andrei Alexandrescu 
wrote:
 On 2/19/12 1:12 AM, Jonathan M Davis wrote:
 On Sunday, February 19, 2012 00:43:58 Andrei Alexandrescu 
 wrote:
 On 2/18/12 8:00 PM, H. S. Teoh wrote:
   From this and other posts I'd say we need to design the 
 base exception

 classes better, for example by defining an overridable 
 property
 isTransient that tells caller code whether retrying might 
 help.

Just because an exception is transient doesn't mean it makes sense to try again. For example, saveFileMenu() might read a filename from the user, then save the data to a file. If the user types an invalid filename, you will get an InvalidFilename exception. From an abstract point of view, an invalid filename is not a transient problem: retrying the invalid filename won't make the problem go away. But the application in this case *wants* to repeat the operation by asking the user for a *different* filename. On the other hand, if the same exception happens in an app that's trying to read a configuration file, then it *shouldn't* retry the operation.

I'm thinking an error is transient if retrying the operation with the same exact data may succeed. That's a definition that's simple, useful, and easy to operate with.

A core problem with the idea is that whether or not it makes sense to try again depends on what the caller is doing. In general, I think that it's best to give the caller as much useful information is possible so that _it_ can decide the best way to handle the exception.

That sounds like "I violently agree". Andrei

Feb 19 2012
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday, February 19, 2012 12:43:12 Jacob Carlborg wrote:
 On 2012-02-18 23:26, Jonathan M Davis wrote:
 In the case of getopt, what we want is a GetOptException which is for
 anything which goes wrong with getopt so that someone can catch that
 exception type if they want to just handle the case wheree getopt throws
 but don't want to swallow other exception types by calling exception and
 pretty much just want to print an error and exit the program or continue
 without any comand-line arguments if that's what they prefer to do.

Isn't that what CommandLineException would be? Or would "getopt" throw other exceptions that would not derive from CommandLineException? So we end up with this: GetOptException / \ / \ / \ CommandLineException SomeKindOfOtherException

IF you want to call it CommandLineException rather than GetOptException, that's fine. I don't much care. The name isn't the critical part here. GetOptException would be more in line with how we've been naming things, but CommandLineException would be more reusable by code outside of std.getopt, so it's probably better. The critical thing here though is that getopt throw an exception (or exceptions) which actually give you the information that you need to handle the problem, which isn't the case right now. - Jonathan M Davis
Feb 19 2012
prev sibling next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 02/18/2012 07:52 PM, 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

Would giving std.concurrency.OwnerTerminated and std.concurrency.LinkTerminated a common supertype make sense?
Feb 19 2012
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/19/12 7:37 AM, Timon Gehr wrote:
 On 02/18/2012 07:52 PM, 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

Would giving std.concurrency.OwnerTerminated and std.concurrency.LinkTerminated a common supertype make sense?

I am not sure. Andrei
Feb 19 2012
prev sibling next sibling parent "Juan Manuel Cabo" <juanmanuel.cabo gmail.com> writes:
I uploaded my little 2003 article on exception categorization to 
github,
where I detailed my whole point_of_view_of_the_catch and 
whole_program_invariant phylosophy for organizing clasess, and 
compared
C++, .NET and Java hierarchies. Its not a professional article, 
and I never wrote a translation to english:

https://github.com/jmcabo/ExceptionArticle/blob/master/articuloExcepciones_v2.pdf

--jm


On Sunday, 19 February 2012 at 10:30:45 UTC, Juan Manuel Cabo 
wrote:
 Hello D community! This is my first post!! I hope I can bring 
 clarity to all this. If not, I apologize.

 Some time ago I researched the best way to classify exceptions 
 and build a hierarchy. I came up with the following rules:


 1) At the top level, there would be RecoverableExceptions and 
 FatalExceptions (or you can call them something like 
 CatcheableException and FatalExceptions, or Exception and 
 Error).

 2) Fatal exceptions shouldn't be catched. They imply that the 
 program lost basic guarantees to go on (memory corruption, 
 missing essential file, etc.). You could catch them if you 
 wanted to, but it makes no sense other than at your top-level 
 method (main(), etc.).

 3) A RecoverableException can be converted to a FatalException 
 by rethrowing it, once a catch decides so. You shouldn't do the 
 reverse: a FatalException never should be converted to a 
 RecoverableException.

 4) It makes no sense to subclass FatalExceptions since there 
 won't be a catch that groups them in a base type (since they 
 are not catcheable).

 5) Only create a new RecoverableException class type if it 
 makes sense to write a catch for it alone. Otherwise, use an 
 preexisting type.

 6) Only group RecoverableExceptions in a category if it makes 
 sense to write a catch for that category. Please don't group 
 them because its fancy or "cleaner", that is a bad reason.


 Who decides when an Exception is Unrecoverable? Library code 
 almost never decides it, since an exception is only 
 unrecoverable if the whole_program_invariant got broken, and 
 libraries are only a part of a program. So they will tend to 
 throw RecoverableExceptions, which can be reconverted to 
 Unrecoverable by the program.

 In some cases, it is clear that an exception is Unrecoverable. 
 When you call a function without satisfying its arguments 
 precondition (ie: null argument) the only way to fix that is by 
 editing the program. You shouldn't have called it like that in 
 the first place, why would you? So you let the 
 UnrecoverableException bubble up to your main function, and log 
 its stacktrace to fix it.

 Unrecoverable means the program got 'tainted', basic guarantees 
 got broken (possible memory corruption, etc.). Most exceptions 
 will be Recoverable.

 Now, expanding on the hierarchy: I said that it makes no sense 
 to subclass UnrecoverableExceptions. Recoverable exceptions on 
 the other hand, need to be subclassed 
 _with_the_catch_on_your_mind_. You are passing info from the 
 throw site to the catch site. The catch block is the 
 interpreter of the info, the observer. You are communicating 
 something to the catch block.

 So please, do not create a new types if there is no value in 
 writing a catch that only cathes that exception and that can 
 recover from that exception. Otherwise, use an existing type.


 I wrote these rules some time ago. Please excuse me if they 
 come off a bit pedantic!!!!!!!!!!!! Its all only a clarifying 
 convention.


 According to all this:

 * FileNotFoundException is useful. It tells you what happened. 
 It is a RecoverableException (under my convention) because 
 until it reaches the program, the library doesn't know if the 
 program can recover from that (it could be a system missing 
 file, or just a document the user asked for).

 * DiskFailureException is only useful if someone can write a 
 catch for it. If so, then it is a RecoverableException. Only 
 the program can decide if it broke basic guarantees.

 * Most argument exceptions are Unrecoverable. A function 
 throwing shouldn't have been called like that in the first 
 place. The only fix is to go back to editing the program. 
 (precondition broken).


 Another thing: you cannot decide whether an exception is 
 Unrecoverable based only on whether the thing that got broken 
 is the postcondition of a function. It is the 
 whole_program_invariant that decides that. For instance:  
 findStuff(someStuff)  might not know if someStuff is important 
 enough for the stability of the program if not found. The 
 postcondition is broken if it doesn't return the Stuff. That 
 might be recoverable.

 And PLEASE: don't make classifications by the point of view of 
 the cause of the problem. DO make classifications by the point 
 of view of the fixing/recovery of the problem; the catch block 
 is who you are talking to. 
 FileNotFoundBecauseFilesystemUnmounted is worthless.

 So, to sum up: (1) it makes no sense to subclass fatal 
 exceptions, and (2) never subclass a RecoverableException if 
 you are not helping a catch block with that (but please do if 
 it aids recovery).

 ..so verbose and pedantic for my first post... yikes.. i beg 
 forgiveness!!!




 On Sunday, 19 February 2012 at 09:27:48 UTC, Jonathan M Davis 
 wrote:
 On Sunday, February 19, 2012 19:00:20 Daniel Murphy wrote:
 I wasn't really serious about implicit fallthrough.

Lately, it seems like I can never tell whether anyone's being serious or not online. :)
 Out of the syntaxes I could come up with:
 catch(Ex1, Ex2 e)
 catch(e : Ex1, Ex2)
 catch(Ex1 | Ex2 e) // java 7 syntax, horrible
 
 I like (e : list) the best.  Naturally it would also accept a 
 type tuple of
 exceptions.
 
 http://d.puremagic.com/issues/show_bug.cgi?id=7540

LOL. Personally, I actually think that the Java 7 syntax looks great (I'd never seen it before), but catch(e : Ex1, Ex2) is just as good and more consistent with the language as a whole, since it doesn't try to give any operators a new meaning (as Java's does). - Jonathan M Davis


Feb 19 2012
prev sibling next sibling parent reply Jose Armando Garcia <jsancio gmail.com> writes:
On Sun, Feb 19, 2012 at 12:44 PM, Alex R=F8nne Petersen
<xtzgzorex gmail.com> wrote:
 On 19-02-2012 15:41, Andrei Alexandrescu wrote:
 On 2/19/12 7:31 AM, Timon Gehr wrote:
 On 02/19/2012 09:26 AM, H. S. Teoh wrote:
 On Sat, Feb 18, 2012 at 11:52:00PM -0800, Jonathan M Davis wrote:
 [...]
 So, while at first glance, it seems like a good idea, I think that it
 has too many issues as-is to work. It might be possible to adjust the
 idea to make it workable though. Right now, it's possible to do it vi=





 mixins or calling a function inside the catch, but doing something
 similar to this would certainly be nice, assuming that we could sort
 out the kinks.

[...] I have an idea. What about "signature constraints" for catch, ala template signature constraints? Something like this: try { ... } catch(IOException e) if (e.errno in subsetYouWantToHandle) { ... } Just using IOException as an example. The idea is to allow arbitrary expressions in the constraint so whatever doesn't satisfy the constrai=




 will be regarded as "not caught", even if the base type matches.


 T

Nice.

That helps. This quite nicely illustrates that types don't necessarily need to proliferate. Something not much more constraining can be done today: try { ... } catch(IOException e) { if (e.errno !in subsetYouWantToHandle) throw e; ... } Andrei

As I pointed out on the pull request, this is *evil*. It resets the stack trace.

What? Is there a technical reason why throw resets the stack? Java doesn't work this way. In java the stack is created when the object Throwable is created: "A throwable contains a snapshot of the execution stack of its thread at the time it was created. It can also contain a message string that gives more information about the error. Finally, it can contain a cause: another throwable that caused this throwable to get thrown. The cause facility is new in release 1.4. It is also known as the chained exception facility, as the cause can, itself, have a cause, and so on, leading to a "chain" of exceptions, each caused by another. " We should consider changing this to work more like Java. This allows for patterns like: // Log the stack but don't throw auto e =3D new Exception(); writefln(e.stack); Thanks, -Jose
 --
 - Alex

Feb 19 2012
parent "Daniel Murphy" <yebblies nospamgmail.com> writes:
Are you sure about that?

My understanding is java works the same way in terms of re-evaluating stack 
traces, except that it has
'throw;' which keeps the original intact, specifically for rethrowing.

"Jose Armando Garcia" <jsancio gmail.com> wrote in message 
news:mailman.601.1329663665.20196.digitalmars-d puremagic.com...
On Sun, Feb 19, 2012 at 12:44 PM, Alex Rnne Petersen
<xtzgzorex gmail.com> wrote:
 On 19-02-2012 15:41, Andrei Alexandrescu wrote:
 On 2/19/12 7:31 AM, Timon Gehr wrote:
 On 02/19/2012 09:26 AM, H. S. Teoh wrote:
 On Sat, Feb 18, 2012 at 11:52:00PM -0800, Jonathan M Davis wrote:
 [...]
 So, while at first glance, it seems like a good idea, I think that it
 has too many issues as-is to work. It might be possible to adjust the
 idea to make it workable though. Right now, it's possible to do it via
 mixins or calling a function inside the catch, but doing something
 similar to this would certainly be nice, assuming that we could sort
 out the kinks.

[...] I have an idea. What about "signature constraints" for catch, ala template signature constraints? Something like this: try { ... } catch(IOException e) if (e.errno in subsetYouWantToHandle) { ... } Just using IOException as an example. The idea is to allow arbitrary expressions in the constraint so whatever doesn't satisfy the constraint will be regarded as "not caught", even if the base type matches. T

Nice.

That helps. This quite nicely illustrates that types don't necessarily need to proliferate. Something not much more constraining can be done today: try { ... } catch(IOException e) { if (e.errno !in subsetYouWantToHandle) throw e; ... } Andrei

As I pointed out on the pull request, this is *evil*. It resets the stack trace.

What? Is there a technical reason why throw resets the stack? Java doesn't work this way. In java the stack is created when the object Throwable is created: "A throwable contains a snapshot of the execution stack of its thread at the time it was created. It can also contain a message string that gives more information about the error. Finally, it can contain a cause: another throwable that caused this throwable to get thrown. The cause facility is new in release 1.4. It is also known as the chained exception facility, as the cause can, itself, have a cause, and so on, leading to a "chain" of exceptions, each caused by another. " We should consider changing this to work more like Java. This allows for patterns like: // Log the stack but don't throw auto e = new Exception(); writefln(e.stack); Thanks, -Jose
 --
 - Alex 

Feb 19 2012
prev sibling next sibling parent Jose Armando Garcia <jsancio gmail.com> writes:
On Sun, Feb 19, 2012 at 12:51 PM, Alex R=F8nne Petersen
<xtzgzorex gmail.com> wrote:
 On 19-02-2012 15:46, Andrei Alexandrescu wrote:
 On 2/19/12 8:44 AM, Alex R=F8nne Petersen wrote:
 On 19-02-2012 15:41, Andrei Alexandrescu wrote:
 That helps. This quite nicely illustrates that types don't necessarily
 need to proliferate. Something not much more constraining can be done
 today:

 try {
 ...
 } catch(IOException e)
 {
 if (e.errno !in subsetYouWantToHandle) throw e;
 ...
 }


 Andrei

As I pointed out on the pull request, this is *evil*. It resets the stack trace.

I understand, that's a good point. Could the matter be considered an implementation issue? Andrei

Unfortunately, I don't think so. As far as I am aware, there is no good w=

 to solve the problem; you can't know whether the user intended to rethrow
 (and thus continue the stack trace) as opposed to doing a clean throw.

 You could say that if the throw statement is "throw new T();", then a new
 stack trace is created