February 20, 2012
On Mon, Feb 20, 2012 at 02:45:25PM -0500, Jonathan M Davis wrote:
> 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.
[...]

I agree, we should use tupleof for this purpose.

I always view Variant has a last resort hack that we should only use if there's absolutely no other way around it. The lack of compile-time type checking on Variant makes it prone to nasty issues like everything compiling with no error and then blowing up at runtime. This is especially bad in code related to exceptions, because they are by definition exceptional, so problems like these often won't get caught until they crash the customer's production server.


T

-- 
Right now I'm having amnesia and deja vu at the same time. I think I've forgotten this before.
February 20, 2012
"Nick Sabalausky" <a@a.a> wrote in message news:jhu7kj$1mae$1@digitalmars.com...
> "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.
>

Using reflection instead of "Variant[string] info" has another benefit: It allows the i18n table to *automatically* support any type, not just Exceptions. By contrast, with "Variant[string] info", a member has to be explicity (or via inheritance) added to every type that the i18n table wants to handle. So you're actally *creating* unnecessary cupling with i18n by using "Variant[string] info". If you replace "Variant[string] info" with reflection, then you've *completely* factored out i18n.

Admittedly, if the i18n engine populates its tables at runtime, then the reflection would have to be runtime reflection, and I don't know offhand if we currently have that particular form of runtime reflection yet or not. But if not, then this is a good use-case for why it should get implemented sooner or later.


February 20, 2012
On Monday, 20 February 2012 at 19:41:07 UTC, Nick Sabalausky wrote:
> 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.

Seconded. Reflection seems a much better solution for this.
The i18n table would map (exception type) -> (language, format string)
and the i18n formatter would use reflection to map field values to the format string.
a format string would be e.g. "File {filename} not found" and the formatter would replace {filename} with e.class.getField("filename").

This does not require any modifications to the exception mechanism.
February 20, 2012
On Feb 20, 2012, at 11:54 AM, Juan Manuel Cabo wrote:

> 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 don't think this makes sense.  To effectively use whatever's in the table I pretty much have to know what error I'm handling, and this isn't possible if type information is lost.  Unless this determination is moved to a run-time check of some field within the exception, and then I'm making my code that much messier and less efficient by putting in tests of this identifier against a list of constants.  Personally, I don't see any use for this table beyond providing context, much like we already have with file, line, etc.
February 20, 2012
With all due respect, I don't see module exception categories as something good for the categorization of exceptions.

As I said in a previous post, long lost in this mega thread,
please don't create exception categories, unless it makes
sense to write a   catch (MyNewShinyCategory ex) {}  for it.
An artificial category would be in the way of other criteria
that aids catch grouping better.
(For instance, many different modules might want to add a
kind of IOException (not that I advocate the IOException
category, this is just for illustration))

--jm


On 02/20/2012 01:45 PM, Andrei Alexandrescu wrote:
> On 2/20/12 10:37 AM, foobar wrote:
>> On Monday, 20 February 2012 at 15:50:08 UTC, Andrei Alexandrescu wrote:
>>> Actually that just shuffles the matter around. Any setup does demand that some library (in this case most probably the standard library) will be a dependency knot because it defines the hierarchy that others should use.
>>
>> Not accurate. A 3rd party library that want to be compatible will no doubt depend on the standard library's _exception hierarchy_ but that does *not* mean it should depend on the parallel functionality in the standard library. Per our example with IO, if I use tango.io I certainly do not want my application code to include redundantly both std.io and tango.io. I wanted to use tango.io as an *alternative* to std.io.
> 
> This is a confusion. Using PackageException!"std.io" does not require importing std.io. Conversely, using std.IOException _does_ require importing std.exceptions or whatnot. So from a dependency management viewpoint, PackageException is superior to IOException.
> 
> The converse disadvantage is that typos won't be caught during compilation. For example, using PackageException!"sdt.io" will go through no problem, but of course won't be caught by people waiting for a PackageException!"std.io".
> 
> 
> Andrei
> 

February 20, 2012
I agree to disagree.

But about your argument for efficiency, any check that you do when examining an exception doesn't need to be lightning fast, after all, if you are looking at an exception object, it means that the stack got unwound, and any nano seconds that you spend doing this:

	catch (Exception ex) {
		if ("mycustomfield" in ex) {
			.. do something ..
 		}
	}

which is just an "in" check in an associative array, which might never
have more than 10 elements (so even linear search is appropiate),
is overshadowed by the time that the runtime took to unwind the stack
and serve the exception to your catch block.

--jm


On 02/20/2012 05:05 PM, Sean Kelly wrote:
> On Feb 20, 2012, at 11:54 AM, Juan Manuel Cabo wrote:
> 
>> 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 don't think this makes sense.  To effectively use whatever's in the table I pretty much have to know what error I'm handling, and this isn't possible if type information is lost.  Unless this determination is moved to a run-time check of some field within the exception, and then I'm making my code that much messier and less efficient by putting in tests of this identifier against a list of constants.  Personally, I don't see any use for this table beyond providing context, much like we already have with file, line, etc.

February 20, 2012
On Mon, Feb 20, 2012 at 08:53:24PM +0100, deadalnix wrote: [...]
> 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.

Looks good.


> 6/ This mecanism is provided as a lib.

Not necessarily; it would be nice to have language support. But at least it's implementable as a lib (as I've shown), so we don't have to invest the time and effort to implement language support until we're sure it's a good idea, after thorough testing with the 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.

I'm not sure yet, but probably language support is be needed to reduce the amount of syntax you need to write just to define Conditions. Unless we can figure out a clever way of using templates or mixins or some such to make it easier to write.

Ideally, you'd want to declare Conditions inside the catch block of the function, instead of making a separate class definition outside. But I'm not sure how to make such a thing work if the handler needs to know what methods are available in the first place. Perhaps a compromise is to use some templates with mixins to generate code for the Condition subclass and for the custom catch block for handling recovery strategies.

Also, another issue that's still unclear to me is, if no handler is found and the Condition gets thrown, does it still make sense to throw the Condition object? Because once the stack unwinds, the recovery methods are probably no longer meaningful. Also, the fact that no handler was found means that no handler will ever be found, so the stack will just unwind to a catch block. And it's doubtful that at that level, the recovery options encoded in the Condition are usable at all.

So some parts of this system need some rethinking.


T

-- 
Don't modify spaghetti code unless you can eat the consequences.
February 20, 2012
On 2012-02-20 18:05, 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.
>
> 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.
>
> 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.

I completely agree, that's exactly how it should be done. I can't understand how difficult it seems to not see this.

-- 
/Jacob Carlborg
February 20, 2012
"foobar" <foo@bar.com> wrote in message news:uphvtoqkvtshnzlqoaus@forum.dlang.org...
>
> I meant -
> what's the benefit of:
> throw createEx!AcmeException("....");
> vs.
> throw new AcmeException("....");
>

Fixed.

> As far as I can see, the former has no benefits over the simpler latter option.
>

The benefit is that you don't have to create any boilerplate ctors in the definition of AcmeException.

*However*, I think that:

1. That's an insufficient improvement to justify breaking the ultra-common "throw new NameOfException(args)" idiom.

2. The solution fails to cover the *entire* scope of the *real* problem: Classes that need to write boilerplate ctors which merely forward to the base class's ctors.  This issue is *not* limited to Exceptions, but Andrei's proposes solution *only* covers the case with Exceptions.

A better solution has already been proposed:

    class AcmeException : Exception
    {
        mixin inheritCtors!();  // Actual name open for bikeshedding
    }

Now, yes, this *is* admittedly more boilerplate than:

    class AcmeException : Exception {}

However, it's superior overall, because:

1. The solution is *contained* to the author of the exception's class. It's *not* a solution that leeks out and infects user code.

2. It solves the *whole* problem in the general case, not just for Exceptions.

And you know what? Even if it's still deemed too much bolierplate, we can still just do this:

    mixin( createInheritedClass("AcmeException", "Exception") );

Or maybe this:

    // Second param is optional.
    // Not sure if this can be a template mixin or has to be string mixin.
    mixin createException!("AcmeException", "Exception");


February 20, 2012
On Mon, Feb 20, 2012 at 09:01:27PM +0100, foobar wrote:
> On Monday, 20 February 2012 at 19:41:07 UTC, Nick Sabalausky wrote:
[...]
> >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.
> 
> Seconded. Reflection seems a much better solution for this.  The i18n table would map (exception type) -> (language, format string) and the i18n formatter would use reflection to map field values to the format string.  a format string would be e.g. "File {filename} not found" and the formatter would replace {filename} with e.class.getField("filename").
> 
> This does not require any modifications to the exception mechanism.

Unfortunately, I don't think D supports runtime reflection yet. At least, not to the point we need (accessing field values of a general Object).

Although, as some have said, it's possible to work around this by using .tupleof, which is still a better solution than Variant[string], because at least you'd have compile-time type checking.


T

-- 
The peace of mind---from knowing that viruses which exploit Microsoft system vulnerabilities cannot touch Linux---is priceless. -- Frustrated system administrator.