On Sunday, February 19, 2012 17:35:31 Andrei Alexandrescu wrote: > On 2/19/12 4:20 PM, Nick Sabalausky wrote: > > "Andrei Alexandrescu"<[email protected]> 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
