The Right Approach to Exceptions

Andrei Alexandrescu SeeWebsiteForEmail at erdani.org
Mon Feb 20 10:19:57 PST 2012


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


More information about the Digitalmars-d mailing list