More exception classes into Phobos?

H. S. Teoh via Digitalmars-d digitalmars-d at puremagic.com
Fri Mar 24 12:38:14 PDT 2017


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


More information about the Digitalmars-d mailing list