On Fri, Mar 24, 2017 at 01:44:02AM +0000, Adam D. Ruppe via Digitalmars-d wrote: > On Friday, 24 March 2017 at 00:28:16 UTC, Walter Bright wrote: > > The string is what gets printed to the user like: > > > > "your password has to have at least one upper case character in > > it" > > > > In general, such is not deducible from the type/arguments. > > Yes, of course they are. In the simplest case in D, we could do throw > new ExceptionImpl!string instead of throw new Exception(string), then > it is absolutely deducible from the type, since it is a part of the > type by definition. > > That also becomes catchable independently of other exceptions (have > you ever done something like `catch(Exception e) if(e.msg != "foo") > throw e;`? I actually have literally done that with a crappy D library > that threw an exception that I could recover from with a retry...), > avoids any runtime allocation of the string, and is potentially easier > to internationalize if you want to present it to the end user.
Catching an Exception by message? That sounds like horrible code smell to me. Upgrade the 3rd party library (which may change the message) and suddenly your program breaks. Bad idea. It probably makes sense for 3rd party libraries to have at least a subclass of Exception, so that you can catch errors originating from that library rather than everything in general. Beyond that, though, you're treading into implementation details territory, which is IMO a bad idea in terms of breaking encapsulation, overly tight coupling between thrower/catcher, etc.. A better way, though, is for the library to provide a retry mechanism in the first place, as part of the official API, than to rely on the caller catching an exception and identifying it. > That's not how I'd literally do it, but it is something we CAN do in D > (and would be an improvement from the status quo, without being a > hassle of hierarchies). Generally, I find that I do subclass Exception in my own code, for obvious reasons, including that since it's my own code, the rest of the code knows what to catch and what to do about it. It basically becomes an API common to code in that particular project that defines certain exception subclasses with known semantics. In 3rd party libraries, I can see a handful of exception subclasses publicly documented for that library as part of its API as stuff specific to that library that may get thrown. But I don't see how to generalize that across libraries... by that point, it will have to become so generic that it's no better than just throwing Exception with a string message. > > Exceptions are not for debugging the code (that's what Errors are > > for), Exceptions are for informing the user that he did it wrong. > > All the more reason to actually make them as informative as possible (...and > likely to cut out most the irrelevant stack trace). If the user is another > programmer calling your function, they ought to be able to catch and inspect > the exception in order to retry. > > Moreover, informing the user wants to have as much info as possible. We've > probably all been frustrated by in D by "Range violation" but it doesn't > tell us what the value actually was. Attaching an int and sinking it into > the error message ought to be trivial... and it IS, yet druntime doesn't do > it. (and yes, I know that's an Error, not an Exception, but same reasoning > applies) Actually, I see this as evidence *against* having RangeError in the first place. If we had stuck to throwing Exception or, in this case, Error, that would have prompted whoever wrote the bounds check code to actually write a useful error message instead of just throwing RangeError with zero additional details. (Since, after all, the exception type ought to be enough to tell the user what needs to be known.) > I only care about the hierarchy insomuch as I intend to catch it > without catching something else from the same block of code. > > > try { > throw new RangeErrorImpl!(size_t, size_t, size_t)(x, 0, array.length); > } catch(RangeError e) { > // this suddenly became a billion times more useful > e.toString(sink); // RangeError, 10, 0, 8 > } Except that it's not really *that* useful if you don't know what those numbers mean. They could be values taken from CPU registers in a stacktrace for all I know, which tells me nothing. Meaning that you might as well have formatted those numbers into part of the string message in the first place. A similar discussion came up some years ago, where Andrei proposed adding a Variant[string] field to Exception where the thrower can stick whatever details he likes into it. Again, though, the problem is, how would the catcher know what string keys to use to get useful info out of that field, and what the values might mean. Unless there is some documented standard, it's really not of much use. It's like receiving an XML file with no DTD. Yeah I'm sure the meaning is all there encoded in the structure somehow, but it does me no good if I don't know what it all means. Besides, even if we had Variant[string] in Exception, would people really bother to write the additional code to populate it with meaningful data? (Much less read a spec on standard key/value mappings.) Usually when I write a `throw Exception`, my mind is thinking, "yeah this is a case the code can't handle, and the user shouldn't pass to us, so just throw an Exception, get it out of the way, and let me get on to the real algorithm already." I wouldn't have the motivation to want to manually populate a hash with details that the user code might not even care about in the end. It's already kind enough on my part to actually want to use std.format to produce a message that a human reader might find helpful, like "syntax error on line 36: unexpected character 'x'". I could have just done a `throw new Exception("syntax error");` and be done with it. At least I, the human, would read that message if the Exception got thrown, but put in additional effort so that it's parseable by code? No thanks. T -- Lottery: tax on the stupid. -- Slashdotter