February 20, 2012
"Nick Sabalausky" <a@a.a> wrote in message news:jhu61p$1jpm$1@digitalmars.com...
> "Andrei Alexandrescu" <SeeWebsiteForEmail@erdani.org> wrote in message news:jhtss8$12mq$3@digitalmars.com...
>> 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?
>>
>
> Maybe I've misunderstood your intent for this "Variant[string] info;" My understanding is that, for example, you want to replace this:
>
> ----------------------------------------------------
> class Exception {}
> class FooException
> {
>    string fooName;
>    int fooID;
>    bool didBarOccur;
>
>    this(string fooName, int fooID, bool didBarOccur)
>    {
>        this.fooName = fooName;
>        this.fooID= fooID;
>        this.didBarOccur= didBarOccur;
>    }
> }
> ----------------------------------------------------
>
> With this:
>
> ----------------------------------------------------
> class Exception
> {
>    Variant[string] info;
> }
> class FooException
> {
>    string fooName;
>    int fooID;
>    bool didBarOccur;
>
>    this(string fooName, int fooID, bool didBarOccur)
>    {
>        this.fooName = fooName;
>        this.fooID= fooID;
>        this.didBarOccur= didBarOccur;
>
>        info["fooName"] = fooName;
>        info["fooID"] = fooID;
>        info["didBarOccur"] = didBarOccur;
>    }
> }
> ----------------------------------------------------
>
> If so, then I don't see any usefulness of "Variant[string] info" other than to start treating exceptions like JS's abomination of an "object" (Or at least AS2's objects anyway - not 100% certain how much of AS2 is taken from JS). If not, then could you clarify what you meant?
>
> In either case, I am interested to hear in more detail how you see "Variant[string] info" being used to address i18n. I haven't really dealt with a lot of i18n myself.
>

Ok, I just saw your explanation here: http://forum.dlang.org/post/jhu1hk$1cb8$1@digitalmars.com

And, IIUC, I assume that inside "stringTemplate()", you'd then access some table that roughly amounts to (yea, I know we don't have 2D AAs):

string[string, Locale] i18nTable;
i18nTable["FileNotFoundExcepton", English] = "File ${filename} not found";

Then stringTemplate() would look that up, find the string "File ${filename} not found" and essentially do:

return "File "~e.info("filename")~" not found";

Although obviously not hardcoded like that.

Is that right?

In that case, I like the general idea, *but* why not just use reflection to access the members instead of essentially creating a JS-style "class" with AAs? That way we don't have to either A. throw away the benefits that class members have over AAs or B. violate DRY by duplicating field members in an AA.



February 20, 2012
On Feb 20, 2012, at 11:12 AM, Nick Sabalausky wrote:
> 
> ----------------------------------------------------
> class Exception
> {
>    Variant[string] info;
> }
> class FooException
> {
>    string fooName;
>    int fooID;
>    bool didBarOccur;
> 
>    this(string fooName, int fooID, bool didBarOccur)
>    {
>        this.fooName = fooName;
>        this.fooID= fooID;
>        this.didBarOccur= didBarOccur;
> 
>        info["fooName"] = fooName;
>        info["fooID"] = fooID;
>        info["didBarOccur"] = didBarOccur;
>    }
> }
> ----------------------------------------------------
> 
> If so, then I don't see any usefulness of "Variant[string] info" other than to start treating exceptions like JS's abomination of an "object" (Or at least AS2's objects anyway - not 100% certain how much of AS2 is taken from JS). If not, then could you clarify what you meant?
> 
> In either case, I am interested to hear in more detail how you see "Variant[string] info" being used to address i18n. I haven't really dealt with a lot of i18n myself.

Localized error messages are typically generated by a localization team and placed in some sort of a lookup table, indexed by language and error code.  Say the code is roughly like this:

displayLocalizedError(Exception e) {
    auto t = loadTable(e.info["lang"]); // info["lang"] returns "ja" for japanese, etc.
    string m = t.findMessage(typeid(e).toString);
    writeln(buildLocalizedMessage(m, e.info));
}

Where m (in english) may be something like:

"Saving to {LocationType} {!LocationName} failed because the destination is full."

The message is parsed and when {LocalizationType} is encountered, the parser knows to replace it with another localized string indexed by "LocationType".  Then {!LocationName} is replaced by e.info["LocationName"], which is specific to the actual error that occurred.  In essence, each line that has to be localized has a monetary (and time) cost for doing so, so error lines are reused when possible.  Then specifics that may or may not themselves be localized are potentially pasted in to generate the final message.
February 20, 2012
On Feb 20, 2012, at 11:44 AM, deadalnix wrote:

> That wouldn't work, because you'll erase the stacktrace.

It wouldn't be difficult to not overwrite a stack trace if one already exists on throw.
February 20, 2012
On Monday, February 20, 2012 20:42:28 deadalnix wrote:
> Le 20/02/2012 20:27, Jonathan M Davis a écrit :
> > On Monday, February 20, 2012 11:15:08 H. S. Teoh wrote:
> >> That's why I proposed to use runtime reflection to scan the exception object for applicable fields. Then you get the best of both worlds: the message formatter doesn't need to know what the fields are, and you get full compile-time type checking for catching code that directly accesses the fields.
> > 
> > That would certainly be better.
> > 
> > - Jonathan M Davis
> 
> This is way better than Variant[string], but unimplemented ATM.

Yes, but you can use compile-time constructs to generate it. And as you pointed out in another post, tupleof should do the trick. Regardless, the point is that using reflection of some kind is a much better solution than using variant.

- Jonathan M Davis
February 20, 2012
On Mon, Feb 20, 2012 at 08:36:56PM +0100, Andrej Mitrovic wrote:
> On 2/20/12, Juan Manuel Cabo <juanmanuel.cabo@gmail.com> wrote:
> > will be trouble. Instead please do:
> >
> >         "The '%1$s' file's size is %2$d which is wrong"
> >
> 
> That is the shittiest formatting specifier ever invented. The unreadability of it is why I never, ever, use it. Python solved this nicely with its {0} {1} syntax:
> 
> >>> print '{0} and {1}'.format('foo', 'bar')

Actually, even that isn't ideal. How is the translator to know what on earth {0} and {1} are? Sometimes you need to know in order to make a good translation. This would be even better:

	"The ${file}'s size is ${size}, which is wrong"

The usefulness of named arguments is even more apparent in complex message like this one:

	"${file}:${line}: Expecting ${expectedtoken}, got ${inputtoken}"

Without named parameters, you'd have:

	"{0}:{1}: expecting {2}, got {3}"

which is almost impossible to translate. What are {0} and {1}? What are {2} and {3}? Does it mean "12:30pm: expecting program to succeed, got general protection fault"?

Using named parameters makes it clear this is a parser error, not something else. This difference may mean using a completely different grammatical structure to translate the message.


T

-- 
Never ascribe to malice that which is adequately explained by incompetence. -- Napoleon Bonaparte
February 20, 2012
Le 20/02/2012 20:34, H. S. Teoh a écrit :
> On Mon, Feb 20, 2012 at 08:11:28PM +0100, deadalnix wrote:
> [...]
>> Considering the handler, This would be great to see it as scoped.
>> This imply using a scope delegate, and probably something as RAII to
>> create it and delete it when we go out of scope.
>
> Yeah, that definitely should be done, otherwise a handler installed by a
> function that's no longer in scope will still kick in when the same
> exception is triggered by unrelated code. That would be very bad.
>
>
>> I'm more doubtfull about what can be done on the throw side. This need
>> to be refined, and to be honnest, the idea is too new in my head to
>> come up with something significant. The concept needs some
>> decantation.
>>
>> I wonder however, how to do something clean. The delegate will need
>> some knowledge of the thrower onternal to do something really usefull,
>> but then, we make the codebase more fragile (because the caler will
>> depend on implementation of the calee, something that we want to
>> avoid).
>
> I've tried to do this by encapsulating the recovery options in the
> Condition object. So the delegate knows, here are the options I have of
> recovering, but it doesn't need to know how the low-level function
> implements those recovery strategies.
>
> For example, the FileNotFoundCond object has a restart() method, and an
> inherited abort() method. So these are the options available to the
> delegate. These methods just return an opaque object to the recovery
> system, which passes it to the low-level function. Then the low-level
> function tries to recover according to which object it receives. So the
> delegate doesn't actually know how each option is implemented, it just
> knows these are available options to recover.
>
> Also, the restart() method takes a new filename as parameter, so the
> delegate even knows what data is needed to provide to a particular
> recovery option in order to help the low-level function recover.
>
> Of course, the current implementation is very skeletal, and there are
> still many open issues:
>
> - How does the delegate know *which* low-level function throws a
>    particular Condition? For example, what if main() calls some function
>    that calls openDataFile() many times? What if openDataFile() is called
>    at different points in the function call hierarchy? How does the
>    delegate know which recovery choice is appropriate for every
>    FileNotFoundCond() that it catches?
>
> - Every Condition the program might encounter requires a lot of code:
>    you need to define a Condition subclass, populate it with recovery
>    strategies, and then have the throwing code check and handle each
>    option. Is there a way to reduce the amount of code you need to write
>    for every Condition?
>
> - Also, Conditions seem to specific to a particular situation. It's not
>    too bad for generic problems like FileNotFoundCond; we know what it
>    means when the OS says "file not found", and we generally know what
>    can be done to fix that. But what about ParseErrorCond? The delegate
>    would need to know what kind of parse error, or even which parser
>    issued that error, otherwise how would it know how to correct the
>    parse error?
>
>    But this means that every different parser in the program will require
>    its own ParseError subclass, along with the associated recovery
>    options. Seems like a lot of work for only occasional benefit.
>
> - Are there general recovery options that apply generally across the
>    majority of Conditions? If so, we can alleviate delegates from having
>    to know the intimate details of a particular operation, which
>    introduces too much coupling between high-level and low-level code.
>
> A more minor issue is that the current implementation is not very well
> written. :) I'll have to think about this more, to know how to better
> implement it. But I wanted to get the semantics of it out first, so that
> at least we can start discussing the conceptual aspects of the idea.
>
>
> T
>

So if I sum up things a bit (and refactor the design a bit according to what is best as I understand it) :

1/ lib don't throw exception, they raise a Condition.

2/ If an handler exists for that condition, then the handler is called, and the condition is passed as parameter. The condition provide an interface so the handler can specify how to handle that condition.

3/ The condition provide a way to throw in its interface. This part of the interface of Condition.

4/ If no handler is found, the throw method of the Condition is called.

5/ The handler is provided in a scoped manner.

6/ This mecanism is provided as a lib.

7/ The mecanism is provided as a richer way to use Exceptions.

Is it a solid base to start on ? I seems we need to start working on std.condition ;) BTW, do we need language support to implement that ? I don't think so, but I'm quite new to the issue.
February 20, 2012
> That wouldn't work, because you'll erase the stacktrace.
>
> Plus, you are confusing inheritance with composition here. What you want is throw a COMException and link it to the original Exception. You have to consider Exception as a linkedlist, one being the cause of another.

You are correct. But it doesn't change the FILE and LINE attributes of the exception.
The code below changes the msg of the exception and rethrows it.
Please note that the stacktrace is changed as you say. But the:
       object.Exception@t.d(17): another
points to the site where it was produced originally:

#!/usr/bin/rdmd
import std.stdio;
void main () {
    anotherFunc();
}
void anotherFunc() {
    try {
        writeln("another func");
        badfunc();
    } catch (Exception ex) {
        ex.msg = "another";
        throw ex;
    }
}
void badfunc() {
    writeln("bad func");
    throw new Exception("badfunc");
}


another func
bad func
object.Exception@t.d(17): another
----------------
./t(void t.anotherFunc()+0x2b) [0x42a1c7]
./t(_Dmain+0x9) [0x42a195]
./t(extern (C) int rt.dmain2.main(int, char**).void runMain()+0x17) [0x43c003]
./t(extern (C) int rt.dmain2.main(int, char**).void tryExec(scope void delegate())+0x2a) [0x43b97a]
./t(extern (C) int rt.dmain2.main(int, char**).void runAll()+0x42) [0x43c056]
./t(extern (C) int rt.dmain2.main(int, char**).void tryExec(scope void delegate())+0x2a) [0x43b97a]
./t(main+0xd3) [0x43b90b]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xff) [0x7fc83b628eff]
----------------






On 02/20/2012 04:44 PM, deadalnix wrote:
> Le 20/02/2012 20:32, Juan Manuel Cabo a écrit :
>> So, if your boss wants the URL of the request that was made when the standard library threw you a FileNotFoundException, you can do:
>>
>>
>>     try {
>>           ...
>>          } catch (Exception ex) {
>>                  //Rethrow the exception with the added detail:
>>         ex.details["custom_url"] = getenv("URI");
>>                  throw ex;
>>          }
>>
>> (don't beat me if URI wasn't the envvar that apache sends for uri, I don't remember it at the moment).
>>
>> --jm
>>
>>
>>
>> On 02/20/2012 04:27 PM, Juan Manuel Cabo wrote:
>>>> 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.
>>>
>>>
>>> Some of the things that characterize an exception, their traits, are transversal to any hierachy that you can imagine, now and in the future.
>>>
>>> You can choose to keep changing a hierarchy, or build in some mechanism to the Exception base class, that will allow you to get your traits without downcasting.
>>>
>>> Say that at your place of work the boss decides that all exception classes should have a COM error code, or that all exception classes should provide the URL of the request that generated it.
>>>
>>> You know what will happen?
>>>
>>> Your boss will require you to derive all your exception classes from COMException or from WebRequestException and then redefine FileNotFound as a subclass of them. So you will have your FileNotFoundException different than the standard library exception.
>>>
>>> Don't believe me? It happened to .NET... they've got a COMException that wraps any other kind of error during a COM call.
>>>
>>>
>>> --jm
>>>
>>
February 20, 2012
Le 20/02/2012 20:45, Jonathan M Davis a écrit :
> On Monday, February 20, 2012 20:42:28 deadalnix wrote:
>> Le 20/02/2012 20:27, Jonathan M Davis a écrit :
>>> On Monday, February 20, 2012 11:15:08 H. S. Teoh wrote:
>>>> That's why I proposed to use runtime reflection to scan the exception
>>>> object for applicable fields. Then you get the best of both worlds: the
>>>> message formatter doesn't need to know what the fields are, and you get
>>>> full compile-time type checking for catching code that directly accesses
>>>> the fields.
>>>
>>> That would certainly be better.
>>>
>>> - Jonathan M Davis
>>
>> This is way better than Variant[string], but unimplemented ATM.
>
> Yes, but you can use compile-time constructs to generate it. And as you
> pointed out in another post, tupleof should do the trick. Regardless, the
> point is that using reflection of some kind is a much better solution than
> using variant.
>
> - Jonathan M Davis

Agreed !
February 20, 2012
Am Mon, 20 Feb 2012 11:13:29 -0800
schrieb Sean Kelly <sean@invisibleduck.org>:

> On Feb 20, 2012, at 9:55 AM, deadalnix wrote:
> > 
> > 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.
> 
> I think its debatable whether a Variant[string] or string[string] is ideal here, but either way I think the point of the table is for localized error messages.  I wouldn't expect any data relevant for filtering the exception within the table.
> 

I think this whole localization discussion is useless as long as we
have no std.localization module. For example this
Variant[string] / string[string] stuff could be part of a
TranslatedMessage struct, which could be used like this

auto msg = TranslatedMessage("Couldn't connect to IP %s", ip);

TranslatedMessage could then automatically convert ip to a string and store it in a Variant[string] / string[string] internally. Then when msg.toString() is called, TranslatedMessage would translate the format string and use the entries in the Variant/string table to construct the message. Exception could then simply store a TranslatedMessage and this could be useful for other code, not only for exceptions. I don't see why we should invent some special exception only localization now.
February 20, 2012
About this part:

>> What you want is throw a COMException and link it to the original Exception. You have to consider Exception as a linkedlist, one being the cause of another.

The Variant[string] is an idea to help avoid people creating new kinds of exception types that don't add nothing.

I guess that you are proving my point.

--jm



On 02/20/2012 04:48 PM, Juan Manuel Cabo wrote:
>> That wouldn't work, because you'll erase the stacktrace.
>>
>> Plus, you are confusing inheritance with composition here. What you want is throw a COMException and link it to the original Exception. You have to consider Exception as a linkedlist, one being the cause of another.
> 
> You are correct. But it doesn't change the FILE and LINE attributes of the exception.
> The code below changes the msg of the exception and rethrows it.
> Please note that the stacktrace is changed as you say. But the:
>        object.Exception@t.d(17): another
> points to the site where it was produced originally:
> 
> #!/usr/bin/rdmd
> import std.stdio;
> void main () {
>     anotherFunc();
> }
> void anotherFunc() {
>     try {
>         writeln("another func");
>         badfunc();
>     } catch (Exception ex) {
>         ex.msg = "another";
>         throw ex;
>     }
> }
> void badfunc() {
>     writeln("bad func");
>     throw new Exception("badfunc");
> }
> 
> 
> another func
> bad func
> object.Exception@t.d(17): another
> ----------------
> ./t(void t.anotherFunc()+0x2b) [0x42a1c7]
> ./t(_Dmain+0x9) [0x42a195]
> ./t(extern (C) int rt.dmain2.main(int, char**).void runMain()+0x17) [0x43c003]
> ./t(extern (C) int rt.dmain2.main(int, char**).void tryExec(scope void delegate())+0x2a) [0x43b97a]
> ./t(extern (C) int rt.dmain2.main(int, char**).void runAll()+0x42) [0x43c056]
> ./t(extern (C) int rt.dmain2.main(int, char**).void tryExec(scope void delegate())+0x2a) [0x43b97a]
> ./t(main+0xd3) [0x43b90b]
> /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xff) [0x7fc83b628eff]
> ----------------
> 
> 
> 
> 
> 
> 
> On 02/20/2012 04:44 PM, deadalnix wrote:
>> Le 20/02/2012 20:32, Juan Manuel Cabo a écrit :
>>> So, if your boss wants the URL of the request that was made when the standard library threw you a FileNotFoundException, you can do:
>>>
>>>
>>>     try {
>>>           ...
>>>          } catch (Exception ex) {
>>>                  //Rethrow the exception with the added detail:
>>>         ex.details["custom_url"] = getenv("URI");
>>>                  throw ex;
>>>          }
>>>
>>> (don't beat me if URI wasn't the envvar that apache sends for uri, I don't remember it at the moment).
>>>
>>> --jm
>>>
>>>
>>>
>>> On 02/20/2012 04:27 PM, Juan Manuel Cabo wrote:
>>>>> 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.
>>>>
>>>>
>>>> Some of the things that characterize an exception, their traits, are transversal to any hierachy that you can imagine, now and in the future.
>>>>
>>>> You can choose to keep changing a hierarchy, or build in some mechanism to the Exception base class, that will allow you to get your traits without downcasting.
>>>>
>>>> Say that at your place of work the boss decides that all exception classes should have a COM error code, or that all exception classes should provide the URL of the request that generated it.
>>>>
>>>> You know what will happen?
>>>>
>>>> Your boss will require you to derive all your exception classes from COMException or from WebRequestException and then redefine FileNotFound as a subclass of them. So you will have your FileNotFoundException different than the standard library exception.
>>>>
>>>> Don't believe me? It happened to .NET... they've got a COMException that wraps any other kind of error during a COM call.
>>>>
>>>>
>>>> --jm
>>>>
>>>