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