February 20, 2012
On Mon, Feb 20, 2012 at 2:37 PM, Andrei Alexandrescu < SeeWebsiteForEmail@erdani.org> wrote:

> On 2/20/12 10:16 AM, Nick Sabalausky wrote:
>
>> "Andrei Alexandrescu"<SeeWebsiteForEma**il@erdani.org<SeeWebsiteForEmail@erdani.org>>
>>  wrote in message
>> news:jhtq31$u8q$1@digitalmars.**com...
>>
>>>
>>> Again, I think this thread clarified we need the "Variant[string] info;" member however we define the hierarchy.
>>>
>>>
>> I disagree. I don't see a need for that.
>>
>
> How would we address custom formatting of e.g. error messages?
>

This may not be D. Gettext says to solve it as follow:

throw new Exception(gettext("Cool English message at
%s.").format(DateTime.now()))

The gettext "compiler" goes through the code an generates all the strings that need to be localized. The translation teams modifies those string and you store them in file/map for that language. At runtime the i18n library turns gettext(...) into a query into that map and returns the actual localized string. There is a map for the entire process.

Localization can also be disable at compile time by making gettext a template and generating a "noop" for that operation.

I have been thinking of making a module for i18n model after gettext but taking advantage of D's language features. Is there some interest in this?

Thanks,
-Jose


February 20, 2012
Le 20/02/2012 06:57, H. S. Teoh a écrit :
> On Sun, Feb 19, 2012 at 09:12:25PM -0600, Andrei Alexandrescu wrote:
>> On 2/19/12 8:52 PM, H. S. Teoh wrote:
> [...]
>>> Correct, so that would be a recovery strategy at the operation level,
>>> say at sendHttpRequest or something like that. There is not enough
>>> information available to sendHttpRequest to know whether or not the
>>> caller wants the request to be retried if it fails.
>>>
>>> But if the higher-level code could indicate this by way of a recovery
>>> policy delegate, then this retry can be done at the sendHttpRequest
>>> level, instead of percolating up the call stack all the way to
>>> submitHttpForm, which then has to reparse user data, convert into
>>> JSON, say, and then retry the entire operation all over again.
>>>
>>> I'm really liking the Lisp approach.
>>
>> Now we're talking. Ideas. Outside the box.
> [...]
>
> Alright. This thread has gone on for too long with lots of talk but no
> down-to-earth, real code. So I decided to code up a quick-n-dirty
> proof-of-concept implementation of a Lispian scheme of exception
> handling. The source files are attached.
>
> This implementation is more to demonstrate the *semantics* of such a
> system, rather than a sample code of the real thing. Before anyone jumps
> on me for writing very bad code. ;-)
>
> In a real implementation, I probably wouldn't inherit Condition from
> Exception, for example. A real implementation would have language-level
> support for declaring recovery strategies within the function that
> handles them, rather than the kludge of declaring them separately and
> abusing the exception system to handle recovery actions.
>
> Also, registered handlers would need to be cleaned up upon scope exit,
> so that they don't cause crazy side-effects in unrelated code later on
> in the program.
>
> But anyway. The point of this code is to demonstrate:
>
> (1) How an exception generated in low-level code is recovered within the
> low-level code without unwinding the stack all the way back to the
> top-level caller;
>
> (2) How the top-level caller can provide a delegate that makes decisions
> that only the top-level code is qualified to make, yet does so in the
> context of low-level code so that recovery can proceed without unwinding
> the stack any more than necessary.
>
> I deliberately made the delegate prompt the user for a fixed filename,
> even allowing the option of aborting if the user wants to give up. This
> is to show how the system handles different recovery actions. This, of
> course, isn't the only way to do things; you can programmatically decide
> on a recovery strategy instead of prompting the user, for example.
>
> What do y'all think?
>
>
> T
>

I read your code with great interest ! I'm not sure about this. This is a lot of code to not achieve that much. OK, the stack hasn't been destroyed, but your delegate has its own scope and cannot do a lot.

Isn't a retry function provided by phobos and a transeient property as proposed by Andrei a better alternative ?
February 20, 2012
On Monday, 20 February 2012 at 17:12:17 UTC, Andrei Alexandrescu wrote:
> On 2/20/12 11:08 AM, Mafi wrote:
>> If it's supposed to be simple factorization, then you should replace
>> "throw r" with "return r". Then the name of that function doesn't make
>> much sense anymore. But then you can better search for throw in user
>> code and the stack traces aren't obfuscated anymore.
>>
>> throw createEx!AcmeException("....");
>
> I think that's a great idea, thanks.
>
> Andrei

I fail to see the point in this. Why is the above better than
throw AcmeException("....");

If you want to avoid boilerplate code in the definition of AcmeException, this can be better accomplished with a mixin.
February 20, 2012
Am 20.02.2012 17:36, schrieb Andrei Alexandrescu:
> On 2/20/12 10:15 AM, dennis luehring wrote:
>>>  Again, I think this thread clarified we need the "Variant[string] info;"
>>>  member however we define the hierarchy.
>>
>>  to use an mighty hyper map capable of holding all informative "values"
>>  will just follow in the same amount of non-using code, and the using
>>  code will be filled up with info["blub"], info["blab"], evil castings,
>>  sensless const key-string an still no proper way to show/use the
>>  information in an generic and typesafe way
>>
>>  sorry but feels like throwing anway the signature conecpt and replace it
>>  with an runtime thing ... maybe we should also get rid of function
>>  signatures with "Variant[string] parameter" :}
>
> I understand this seems loose, but again, when you want to do custom
> formatting or i18n this is the way to go. The job of rendering the
> exception as a string must be outsourced (heh) outside the exception,
> and Variant[string] is a simple mechanism to do so.

it does not seem - it is

special_exception --> special_exception_rendere is the way

or else you need to define that clean,compile-time-safe and all the other stuff isn't needed anymore
February 20, 2012
On Monday, 20 February 2012 at 17:11:09 UTC, Andrei Alexandrescu wrote:
> On 2/20/12 11:05 AM, foobar wrote:
>> On Monday, 20 February 2012 at 16:37:28 UTC, Andrei Alexandrescu wrote:
>>> On 2/20/12 10:16 AM, Nick Sabalausky wrote:
>>>> "Andrei Alexandrescu"<SeeWebsiteForEmail@erdani.org> wrote in message
>>>> news:jhtq31$u8q$1@digitalmars.com...
>>>>>
>>>>> Again, I think this thread clarified we need the "Variant[string]
>>>>> info;"
>>>>> member however we define the hierarchy.
>>>>>
>>>>
>>>> I disagree. I don't see a need for that.
>>>
>>> How would we address custom formatting of e.g. error messages?
>>>
>>> Andrei
>>
>> 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.
>
>> 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.
>
>> 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].
>
>
> Andrei

This extra processing is orthogonal to the exception. the same exception can be logged to a file, processed (per above example) and generate graphical notification to the user, etc. The exception contains the information pertaining only to what went wrong. the rest is not part of this discussion.
The exact same exception in the example would also be thrown on a mistyped URL in an application that tries to scrape some info from a website for further processing. The error is still the same - the url is incorrect but different use cases handle it differently. In the former example I might call to a i18n lib (someone already mentioned gettext) while in the latter I'll call a logging library with the the mistyped url (for statistics' sake).
in the first I use the url to find a closest match, in the second I want to log said requested url. Both handled by *other* mechanisms.
in both cases the exception needs a url field and in both cases I have no need for the Variant[string] map.

February 20, 2012
On 2/20/12 11:32 AM, foobar wrote:
> On Monday, 20 February 2012 at 17:12:17 UTC, Andrei Alexandrescu wrote:
>> On 2/20/12 11:08 AM, Mafi wrote:
>>> If it's supposed to be simple factorization, then you should replace
>>> "throw r" with "return r". Then the name of that function doesn't make
>>> much sense anymore. But then you can better search for throw in user
>>> code and the stack traces aren't obfuscated anymore.
>>>
>>> throw createEx!AcmeException("....");
>>
>> I think that's a great idea, thanks.
>>
>> Andrei
>
> I fail to see the point in this. Why is the above better than
> throw AcmeException("....");
>
> If you want to avoid boilerplate code in the definition of
> AcmeException, this can be better accomplished with a mixin.

The advantage is that e.g. the compiler can see that flow ends at throw. Other languages have a "none" type that function may return to signal they never end.

Andrei
February 20, 2012
Le 20/02/2012 18:11, Andrei Alexandrescu a écrit :
> On 2/20/12 11:05 AM, foobar wrote:
>> On Monday, 20 February 2012 at 16:37:28 UTC, Andrei Alexandrescu wrote:
>>> On 2/20/12 10:16 AM, Nick Sabalausky wrote:
>>>> "Andrei Alexandrescu"<SeeWebsiteForEmail@erdani.org> wrote in message
>>>> news:jhtq31$u8q$1@digitalmars.com...
>>>>>
>>>>> Again, I think this thread clarified we need the "Variant[string]
>>>>> info;"
>>>>> member however we define the hierarchy.
>>>>>
>>>>
>>>> I disagree. I don't see a need for that.
>>>
>>> How would we address custom formatting of e.g. error messages?
>>>
>>> Andrei
>>
>> 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.
>
>> 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.
>
>> 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].
>
>
> Andrei

And so variant is the way to go ?

Clearly, this is a very strong arguement in favor of typed Exception, that provide usefull information about what went wrong. This is a safe approach.

Because this Variant stuff is going to require massive ducktyping of Exceptions, with all possible errors involved. The keys in the Variant[string] will depend on the Exception the dev is facing. This should be avoided and should warn us about the requirement of typed Exceptions.
February 20, 2012
Le 20/02/2012 18:28, Jose Armando Garcia a écrit :
> On Mon, Feb 20, 2012 at 2:37 PM, Andrei Alexandrescu
> <SeeWebsiteForEmail@erdani.org <mailto:SeeWebsiteForEmail@erdani.org>>
> wrote:
>
>     On 2/20/12 10:16 AM, Nick Sabalausky wrote:
>
>         "Andrei Alexandrescu"<SeeWebsiteForEma__il@erdani.org
>         <mailto:SeeWebsiteForEmail@erdani.org>>  wrote in message
>         news:jhtq31$u8q$1@digitalmars.__com...
>
>
>             Again, I think this thread clarified we need the
>             "Variant[string] info;"
>             member however we define the hierarchy.
>
>
>         I disagree. I don't see a need for that.
>
>
>     How would we address custom formatting of e.g. error messages?
>
>
> This may not be D. Gettext says to solve it as follow:
>
> throw new Exception(gettext("Cool English message at
> %s.").format(DateTime.now()))
>
> The gettext "compiler" goes through the code an generates all the
> strings that need to be localized. The translation teams modifies those
> string and you store them in file/map for that language. At runtime the
> i18n library turns gettext(...) into a query into that map and returns
> the actual localized string. There is a map for the entire process.
>
> Localization can also be disable at compile time by making gettext a
> template and generating a "noop" for that operation.
>
> I have been thinking of making a module for i18n model after gettext but
> taking advantage of D's language features. Is there some interest in this?
>

This is definitively something that should be integrated into phobos IMO.

> Thanks,
> -Jose
>

February 20, 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
February 20, 2012
On 2/20/12 11:44 AM, foobar wrote:
> This extra processing is orthogonal to the exception. the same exception
> can be logged to a file, processed (per above example) and generate
> graphical notification to the user, etc. The exception contains the
> information pertaining only to what went wrong. the rest is not part of
> this discussion.

Exactly. I don't see how a disagreement follows from here. So isn't it reasonable to design the exception such that it can offer information pertaining to what went wrong, in a uniform manner?

> The exact same exception in the example would also be thrown on a
> mistyped URL in an application that tries to scrape some info from a
> website for further processing. The error is still the same - the url is
> incorrect but different use cases handle it differently. In the former
> example I might call to a i18n lib (someone already mentioned gettext)
> while in the latter I'll call a logging library with the the mistyped
> url (for statistics' sake).
> in the first I use the url to find a closest match, in the second I want
> to log said requested url. Both handled by *other* mechanisms.
> in both cases the exception needs a url field and in both cases I have
> no need for the Variant[string] map.

The Variant[string] map saves a lot of duplication whenever you want to format a human-readable string (which is a common activity with exceptions). It transforms this (I'm too lazy to write code anew by hand, so I'll paste Jonathan's):

try
    getopt(args, ...)
catch(MissingArgumentException mae)
{
    stderr.writefln("%s is missing an argument", mae.flag);
    return -1;
}
catch(InvalidArgumentException iae)
{
    stderr.writelfln("%s is not a valid argument for %s. You must give it a
%s.", mae.arg, mae.flag, mae.expectedType);
    return -1;
}
catch(UnknownFlagException ufe)
{
    stderr.writefln("%s is not a known flag.", ufe.ufe);
    return -1;
}
catch(GetOptException goe)
{
    stderr.writefln("There was an error with %s",  goe.flag);
    return -1;
}
//A delegate that you passed to getopt threw an exception.
catch(YourException ye)
{
    //...
}
catch(Exception e)
{
    stderr.writeln("An unexpected error occured.");
    return -1;
}

into this:

try
    getopt(args, ...)
catch(Exception e)
{
    stderr.writeln(stringTemplate(typeid(e).toString(), e.info));
    return -1;
}

The stringTemplate function loads the formatting template from a table indexed on typeid(e).toString() and formats it with the info. It's simple factorization.


Andrei