February 20, 2012
On Feb 20, 2012, at 12:54 PM, Andrei Alexandrescu wrote:

> On 2/20/12 1:41 PM, Sean Kelly wrote:
>> 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)); }
> 
> I'd amend your example a bit as follows:
> 
> displayLocalizedError(Exception e) {
>    auto t = loadTable(g_lang); // g_lang contains "ja" for japanese, etc.
>    string m = t.findMessage(typeid(e).toString);
>    writeln(buildLocalizedMessage(m, e.info));
> }
> 
> I mean the exception has no notion of "lang"uage.

I wrote it that way because the systems I work on don't have a global concept of language.  It's request-specific.  I wasn't sure how to succinctly represent this in the code, so I just dumped the field in the lookup table.
February 20, 2012
On 2/20/12 2:31 PM, Juan Manuel Cabo wrote:
>> ...
>> Sure. Again, this is not advocating replacement of exception hierarchies with tables!
>> ...
>>
>> Andrei
>>
>
> I think that the case of rethrowing an exception with added detail is the worst
> enemy of clean Exception hierarchies.
> The idea of Variant[string] remedies that case without creating a new exception
> class just for the added fields. If that case is solved, then the tipical need
> for creating new exception types that don't really aid selecting them for
> catching and recovery is solved too.

Good point. One thing somewhat particular to D, you can always throw a new exception linked to the current one.

Andrei


February 20, 2012
I like one golden rule that I have:

 "You should only create a new exception type, if it makes sense to write
  a  catch(MyNewShinyType ex){}  "

other reasons for creating a new exception class I don't consider them valid
(but that's just me!).
This is because, exception types (in my head) are only a way to distinguish
whether to select them or let them go when I write a catch(), and to
help the catch recover.  Now, if I can't distinguish the *what* of the error,
I cannot recover well. The *cause* of the error goes inside the exception
object, not encoded in the type. Other details of the error go inside of
the exception object, not encoded in the type name.

So all I care about is the *what* of the error, so that it will fall in the correct catch statement. Other criteria obscures that.

The Variant[string] helps keep the hierarchy clean. The hierachy should
tell the *what* of the error so that I can pick one when writing a catch block.

--jm


On 02/20/2012 05:51 PM, Jonathan M Davis wrote:
> On Monday, February 20, 2012 17:31:28 Juan Manuel Cabo wrote:
>>> ...
>>> Sure. Again, this is not advocating replacement of exception hierarchies
>>> with tables! ...
>>>
>>> Andrei
>>
>> I think that the case of rethrowing an exception with added detail is the
>> worst enemy of clean Exception hierarchies.
>> The idea of Variant[string] remedies that case without creating a new
>> exception class just for the added fields. If that case is solved, then the
>> tipical need for creating new exception types that don't really aid
>> selecting them for catching and recovery is solved too.
> 
> Having derived exceptions with additional information is a _huge_ boon, and I contend that it's vasty better with variant, which would be highly error prone, because it's not properly statically checked. Changes to what's put in the variant could kill code at runtime - code which by its very definiton is not supposed to be the normal code path, so you're less likely to actually run into the problem before you ship your product. Whereas with the information in actual member variables, if they get changed, you get a compilation error, and you know that you have to fix your code.
> 
> Rethrowing is a separate issue. And in many cases, the correct thing to do is to chain exceptions. You catch one, do something with it, and then you throw a new one which took the first one as an argument. Then you get both. That functionality is already built into Exception.
> 
> - Jonathan M Davis

February 20, 2012
On 2/20/12 2:45 PM, Jacob Carlborg wrote:
> On 2012-02-20 19:47, Andrei Alexandrescu wrote:
>> On 2/20/12 12:50 PM, deadalnix wrote:
>>> This is bad design IMO. Exception are here to provide information about
>>> what is wrong. It has nothing to do with internationalisation
>>> whatsoever.
>>
>> 100% agree.
>>
>> Andrei
>
> It sure seems you want to but internationalization into exceptions.

How did you infer that? I don't want to put internationalization into exceptions /at all/.

Andrei
February 20, 2012
On 2012-02-20 20:12, Nick Sabalausky wrote:
> 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.

Internationalization in Ruby on Rails works something like this:

# set the locale
I18n.locale = :se

This will then, at an appropriate time load, a YAML file containing the translations, defaults  too <rails_app>/config/<locale>.yml. The YAML file can look something like this:

se:
    customer:
        address:
            first_name: Förnamn
            last_name: Efternamn




Then to use the translation, it looks like this:

I18n.translate("customer.address.first_name")

Results in "Förnamn".

It's also possible to pass variables to the translation:

se:
    product:
        price: %{price} kr

I18n.translate("product.price", :price => 300)

":price => 300" is a hash map-literal.

-- 
/Jacob Carlborg
February 20, 2012
Le 20/02/2012 22:05, Andrei Alexandrescu a écrit :
> On 2/20/12 2:31 PM, Juan Manuel Cabo wrote:
>>> ...
>>> Sure. Again, this is not advocating replacement of exception
>>> hierarchies with tables!
>>> ...
>>>
>>> Andrei
>>>
>>
>> I think that the case of rethrowing an exception with added detail is
>> the worst
>> enemy of clean Exception hierarchies.
>> The idea of Variant[string] remedies that case without creating a new
>> exception
>> class just for the added fields. If that case is solved, then the
>> tipical need
>> for creating new exception types that don't really aid selecting them for
>> catching and recovery is solved too.
>
> Good point. One thing somewhat particular to D, you can always throw a
> new exception linked to the current one.
>

And this is great design. We should continue in that direction.

> Andrei
>
>

February 20, 2012
On 2012-02-20 20:49, H. S. Teoh wrote:
> 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
>

Ruby on Rails uses name parameters:

http://guides.rubyonrails.org/i18n.html

-- 
/Jacob Carlborg
February 20, 2012
On 2/20/12 2:51 PM, Jonathan M Davis wrote:
> On Monday, February 20, 2012 17:31:28 Juan Manuel Cabo wrote:
>>> ...
>>> Sure. Again, this is not advocating replacement of exception hierarchies
>>> with tables! ...
>>>
>>> Andrei
>>
>> I think that the case of rethrowing an exception with added detail is the
>> worst enemy of clean Exception hierarchies.
>> The idea of Variant[string] remedies that case without creating a new
>> exception class just for the added fields. If that case is solved, then the
>> tipical need for creating new exception types that don't really aid
>> selecting them for catching and recovery is solved too.
>
> Having derived exceptions with additional information is a _huge_ boon, and I
> contend that it's vasty better with variant, which would be highly error
> prone, because it's not properly statically checked. Changes to what's put in
> the variant could kill code at runtime - code which by its very definiton is
> not supposed to be the normal code path, so you're less likely to actually run
> into the problem before you ship your product. Whereas with the information in
> actual member variables, if they get changed, you get a compilation error, and
> you know that you have to fix your code.
>
> Rethrowing is a separate issue. And in many cases, the correct thing to do is
> to chain exceptions. You catch one, do something with it, and then you throw a
> new one which took the first one as an argument. Then you get both. That
> functionality is already built into Exception.

I think we'd be quick to dismiss Juan's point. There are many execution contexts for any given exception, and summarily saying that we'll handle them with distinct types doesn't sound exactly scalable. I've long wanted to be able to plant context information in a scope, that was automatically added to any exception possibly leaving that scope.

Andrei

February 20, 2012
Yeah.. that is a problem! :-) Thanks for liking the idea, now we can talk about the fine details!!

One way is to not let the user direct access to the associative array,
but wrap the e.info["MyDetail"] call in a nothrow function, such as
e.info("MyDetail"), and an e.hasInfo("MyDetail"), and of course:
e.addInfo("MyDetail", value) and e.allInfoNames() or something.

The nothrow function would return an empty value if not found (I fear that it might not be of the same Variant subtype as the Variant value was intended when present).

--jm


On 02/20/2012 05:53 PM, H. S. Teoh wrote:
> On Mon, Feb 20, 2012 at 05:31:28PM -0300, Juan Manuel Cabo wrote:
>>> ...
>>> Sure. Again, this is not advocating replacement of exception hierarchies with tables!
>>> ...
>>>
>>> Andrei
>>>
>>
>> I think that the case of rethrowing an exception with added detail is the worst enemy of clean Exception hierarchies.
> 
> Hmm. This is a valid point. Sometimes you want to add contextual details to an exception in order to provide the final catching code with more useful information. Otherwise you may end up with a chain of mostly redundant exception classes:
> 
> 	class UTFError : Exception {...}
> 	class LexUTFError : LexUTFError {
> 		int line, col;
> 		...
> 	}
> 	class ConfigFileParseError : LexUTFError {
> 		string cfgfile_name;
> 	}
> 
> 	auto decodeUTF(...) {
> 		...
> 		throw new UTFError;
> 	}
> 
> 	auto configLexer(...) {
> 		try {
> 			...
> 			decodeUTF(...);
> 		} catch(UTFError e) {
> 			throw new LexUTFError(...);
> 		}
> 	}
> 
> 	auto configParser(...) {
> 		try {
> 			...
> 			configLexer(...);
> 		} catch(LexUTFError e) {
> 			throw new ConfigFileParseError(...);
> 		}
> 	}
> 
> 
>> The idea of Variant[string] remedies that case without creating a new exception class just for the added fields. If that case is solved, then the tipical need for creating new exception types that don't really aid selecting them for catching and recovery is solved too.
> [...]
> 
> However, I still hesitate about using Variant[string]. How would you address the following problem:
> 
> 	// Module A
> 	class MyException : Exception {
> 		this() {
> 			info["mydetail"] = ...;
> 		}
> 	}
> 
> 	// Module B
> 	auto func() {
> 		try {
> 			...
> 		} catch(MyException e) {
> 			if (e.info["mydetail"] == ...) {
> 				...
> 			}
> 		}
> 	}
> 
> If module A's maintainer renames "mydetail" to "detail", then module B will still compile with no problem, but now e.info["mydetail"] doesn't exist and will cause a runtime error at worst. At best, the catch block won't be able to recover from the error as it did before, because now it can't find the info it was looking for.
> 
> If "mydetail" had been a field stored in MyException, then module B would get a compile-time error, and the problem can be fixed immediately, instead of going unnoticed until it blows up at the customer's production server.
> 
> 
> T
> 

February 20, 2012
On 2/20/12 2:53 PM, H. S. Teoh wrote:
> On Mon, Feb 20, 2012 at 05:31:28PM -0300, Juan Manuel Cabo wrote:
>>> ...
>>> Sure. Again, this is not advocating replacement of exception hierarchies with tables!
>>> ...
>>>
>>> Andrei
>>>
>>
>> I think that the case of rethrowing an exception with added detail is
>> the worst enemy of clean Exception hierarchies.
>
> Hmm. This is a valid point. Sometimes you want to add contextual details
> to an exception in order to provide the final catching code with more
> useful information. Otherwise you may end up with a chain of mostly
> redundant exception classes:
[snip]

Yah, it does look we need to better investigate the Variant[string] info() primitive.

Andrei