The Right Approach to Exceptions

H. S. Teoh hsteoh at quickfur.ath.cx
Mon Feb 20 09:56:14 PST 2012


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.


> >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).

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. There's simply no way around this. Just saying "a problem happened"
is unhelpful. 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!!

At the end of the day, using a Variant is no better than using a deep
class hierarchy. You're just encoding the exact same structure of
information in a different way. 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.

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. At my work project, we have a user-facing
client-side GUI, and a server-side infrastructure which includes, among
other things, a SQL database. There's a middle layer built over the SQL
layer that provides various application-specific functions. Both layers
can encounter any number of errors. But you know what? The GUI side code
displays almost all of these errors as "internal error" to the user.
Why? Because the user doesn't know, nor care, that SQL error 1234
occurred. They don't care that middle layer exception 2345 occurred.
None of this makes any sense to them. The *developers* care which error
it is, so these errors are logged to a *debug channel* that only the
developers care to read. It's not even in the regular syslog, because
the user admins who read the syslog wouldn't understand what the heck
the messages mean anyway. All that's logged is "internal error".

Only a few relevant errors from the lower layers are actually translated
by the GUI code. User-relevant errors such as "name already exists",
"serial number mismatch", and things like that. Which are only a small
subset of problems that could potentially occur.

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). Only a tiny subset of exceptions even
*need* to see the light of day, and require i18n. The developers don't
care about i18n -- in fact, it's harmful, because it obscures exactly
what the error was. If the code throws "Bad parameter error", the
developers want to see the exact string "Bad parameter error". The last
thing they want is to read this string in Japanese, and then have to
figure out what on earth it corresponds with in the code. Translation is
only needed for that small subset of errors that is actually meaningful
to the end-user. Errors meant for the developers need not, and should
not, be translated at all.

So then, how does the GUI code know what to translate and what not to
translate? That's where an exception class hierarchy is necessary. A
hierarchy that allows the code to specifically catch
FileNotFoundException and translate that, and leave out other stuff like
OracleError1234, ConfigFileParseError, and all the stuff that users
don't even remotely understand. Phobos should *not* be the one making
this kind of decision. What it needs to do is full information
disclosure, and let the catch code make use of it as it sees fit.


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

You're just reinventing class hierarchies using variants. To what end?


T

-- 
IBM = I Blame Microsoft


More information about the Digitalmars-d mailing list