February 20, 2012
On Monday, 20 February 2012 at 17:47:35 UTC, Andrei Alexandrescu wrote:
> 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

I meant -
what's the benefit of:
throw createEx!AcmeException("....");
vs.
throw AcmeException("....");

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

February 20, 2012
Am Mon, 20 Feb 2012 15:28:33 -0200
schrieb Jose Armando Garcia <jsancio@gmail.com>:

> 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?

Yep we need this. I also thought about developing an i18n module maybe
we can work together on one. I won't have time for it the next 3
weeks though.
Anyway, I think we should base our i18n module on boost.locale
http://www.boost.org/doc/libs/1_48_0/libs/locale/doc/html/index.html
but boost.locale is pretty big, we should probably start with the
translation part only:
http://www.boost.org/doc/libs/1_48_0/libs/locale/doc/html/index.html

The API is quite good imho. It'll need some changes to fit D better, but I think this could become a nice D style API.

Boost.locale uses the gettext format internally which is important. This way we can use all the gettext tools & websites and the translator community (https://www.transifex.net/start/).

BTW: I already have code to read the gettext format, so that could be used.
> 
> Thanks,
> -Jose
> 

February 20, 2012
Le 20/02/2012 19:53, H. S. Teoh a écrit :
> On Mon, Feb 20, 2012 at 06:37:07PM +0100, deadalnix wrote:
>> Le 20/02/2012 06:57, H. S. Teoh a écrit :
> [...]
>>> 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.
> [...]
>> 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.
>
> The delegate has full access to the scope of main(), or wherever it was
> registered from, which the low-level error recovery code has no access
> to.
>
>
>> Isn't a retry function provided by phobos and a transeient property as
>> proposed by Andrei a better alternative ?
>
> The problem is that by the time you regain control at the catch, the
> stack has already unwound to main(), so if you need to retry, you need
> to call openDataFile() all over again.
>
> In this case it's not too bad, because openDataFile() is very simple.
> But imagine if this openDataFile() was nested deeply inside a few layers
> of functions called from main(), then by the time you unwind the stack
> to main(), all the previous work done is lost, and you have to restart
> from the beginning.
>
> Also, how does the transient property help main() know that it should
> prompt the user for a different filename? What if a different exception
> was caught, like NetworkDriveUnreachable? It wouldn't make sense to
> prompt the user for another filename in this case.
>
>
> T
>

Yes, I see the point. This has clearly some advantages.

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.

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).
February 20, 2012
Le 20/02/2012 20:04, Johannes Pfau a écrit :
> Am Mon, 20 Feb 2012 15:28:33 -0200
> schrieb Jose Armando Garcia<jsancio@gmail.com>:
>
>> 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?
>
> Yep we need this. I also thought about developing an i18n module maybe
> we can work together on one. I won't have time for it the next 3
> weeks though.
> Anyway, I think we should base our i18n module on boost.locale
> http://www.boost.org/doc/libs/1_48_0/libs/locale/doc/html/index.html
> but boost.locale is pretty big, we should probably start with the
> translation part only:
> http://www.boost.org/doc/libs/1_48_0/libs/locale/doc/html/index.html
>
> The API is quite good imho. It'll need some changes to fit D better,
> but I think this could become a nice D style API.
>
> Boost.locale uses the gettext format internally which is important.
> This way we can use all the gettext tools&  websites and the translator
> community (https://www.transifex.net/start/).
>
> BTW: I already have code to read the gettext format, so that could be
> used.
>>
>> Thanks,
>> -Jose
>>
>

Great initiative, but can you please start this in a new thread ? This one is already quite hard to follow :D
February 20, 2012
On 02/20/2012 02:57 PM, Andrei Alexandrescu wrote:
> 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

February 20, 2012
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.

February 20, 2012
On Mon, Feb 20, 2012 at 01:52:15PM -0500, Jonathan M Davis wrote: [...]
> So, instead of
> 
> catch(SpecificException1 e)
> {
>  //use fields specific to this exception to do whatever you need to do
> }
> catch(SpecificException2 e)
> {
>  //use fields specific to this exception to do whatever you need to do
> }
> catch(GenericException e)
> {
>  //handle the generic exception as best you can given the lack of
>  //a specific one
> }
> 
> you end up with something effectively along the lines of
> 
> catch(GenericException e)
> {
>  if(/* e is SpecificException1 */)
>  {
>  //use fields specific to this exception to do whatever you need to do
>  }
>  else if(/* e is SpecificException2 */)
>  {
>  //use fields specific to this exception to do whatever you need to do
>  }
>  else
>  {
>  //handle the generic exception as best you can given the lack of
>  //a specific one
>  }
> }
[...]

I think what Andrei wants to do is more along these lines:

	catch(Exception e) {
		writeln(formatLocaleString(e.fmt, e.data));
	}

I think there's some merit to this idea. However, I'm still unsure about storing stuff in a Variant.

For one thing, you either need some sort of format string in the exception object (as I have above), which is bad, as somebody else pointed out, because now you're mixing i18n code into exception code, or you need some way of figuring out what format to use for which exception. So ultimately, you'll still end up with with a huge switch statement, or a global table of all exceptions (not good for maintenance, now every time someone changes an exception he has to remember to update the table).

One solution, perhaps, is to have an i18n file containing mappings of exception types to message formats. So that you can have:

	class Exception : Error {
		Variant[string] info;
		...
	}

	string[string] exceptionFormats = loadLocaleData();

	string formatExceptionMsg(LocaleInfo li, Exception e) {
		if (typeid(e).name in exceptionFormats) {
			return format(exceptionFormats[typeid(e)],
				e.info);
		}
		return e.msg;
	}

This may be acceptable, if the catching code knows which formats are actually defined in the locale data, so it will only call formatExceptionMsg() if there's actually a format defined for it. This way, you don't need to have *every* exception translated, just those that your catching code will actually use.

The one objection I have with this, though, is that if the catching code wants to specifically catch, say, FileNotFoundException, and extract the offending filename from the exception object, it would have to do a string lookup in Exception.info, rather than just accessing FileNotFoundException.filename directly.

Then if the Phobos maintainer renames the field, the code still compiles since the compiler has no way to know that Exception.info["filename"] is no longer set by the ctor of FileNotFoundException, so the problem will go undetected until the exception actually occurs, at which time the catching code gets a runtime error (very bad).

Having the field directly in FileNotFoundException is better, because you get a compile-time error when the field is renamed, so the developer can fix it before it ships, rather than have the customer run into the runtime error.

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.


T

-- 
Never wrestle a pig. You both get covered in mud, and the pig likes it.
February 20, 2012
"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.


February 20, 2012
Le 20/02/2012 20:12, Nick Sabalausky a écrit :
> "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.
>
>

Why not use tupleof on the Exception instead of Variant[string] ?
February 20, 2012
I like the idea!

Remember please for anyone reading: Use positional arguments in format strings. Otherwise:

	"The '%s' file's size is %d which is wrong"

        translated to

	"El tamaño %d es incorrecto para el archivo %s"

will be trouble. Instead please do:

        "The '%1$s' file's size is %2$d which is wrong"

specially for standard library messages. This would be very helpful!

--jm



> 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