February 21, 2012
On 2/20/12 6:25 PM, H. S. Teoh wrote:
> On Mon, Feb 20, 2012 at 05:15:17PM -0600, Andrei Alexandrescu wrote:
> Formatting should use class reflection. We already discussed that, and
> we already agreed that was the superior approach.

Jose's argument convinced me otherwise. I retract my agreement.

> When you're catching a specific exception, you're catching it with the
> view that it will contain precisely information X, Y, Z that you need to
> recover from the problem. If you don't need to catch something, then
> don't put the catch block there.

That's extremely rare in my experience, and only present in toy examples that contain a ton of "..." magic.

> The problem with using Variant[string] is that everything gets lumped
> into one Exception object, and there's no way to only catch the
> Exception that happens to have variables "p", "q", and "r" set in the
> Variant[string].

No. You are still free to define as many exception classes as you deem necessary. Please let's not pit the hash against the hierarchy again. This is confusing the role of the two. Consider the hash an interface function you want to occasionally implement.

> You have to catch an exception type that includes all
> sorts of combinations of data in Variant[string], then manually do tests
> to single out the exception you want, and rethrow the rest. That's where
> the ugliness comes from.

Yah, that would suck, but it's not at all what I say.

> [...]
>> The code with Variant[string] does not need combinatorial testing if
>> it wants to do a uniform action (such as formatting). It handles
>> formatting uniformly, and if it wants to look for one particular field
>> it inserts a test.
>
> Again, we've already agreed class reflection is the proper solution to
> this one.

Agreement rescinded. Sorry! Jose's point was just too good, and reminded me of a pain point I so long had with exception, I'd got used to it as a fact of life.

>>> And then what do you do if you're depending on a particular field to
>>> be set, but it's not? Rethrow the exception? Then you have the stack
>>> trace reset problem.
>>
>> Don't forget that Variant[string] does not preclude distinct
>> exception types. It's not one or the other.
> [...]
>
> Agreed. But it shouldn't be the be-all and end-all of data passed in
> exceptions. If anything, it should only be rarely used, with most
> exception classes using static fields to convey relevant information.

And to perfectly help code duplication everywhere.

> I can see the usefulness of using Variant[string] as a way of
> "decorating" exceptions with "extra attributes", but it shouldn't be the
> primary way of conveying information from the throw site to the catch
> site.
>
> As for iterating over the information in the most derived class, for
> formatting, etc., class reflection is the way to go.

Agreement rescinded as far as exceptions go. That doesn't make reflection any less necessary btw. It just reflects the dynamism of exception paths.

> We shouldn't be
> using Variant[string] for this, because there's another problem
> associated with it: suppose MyException sometimes gets "extra_info1" and
> "extra_info2" tacked onto it, on its way up the call stack, and
> sometimes not. Now what should the catcher do?

Use a central loop to render the information.

> How do you format this
> exception?

With a string template as has been discussed.

> Should the format string include extra_info1 and extra_info2,
> or not? If it doesn't, what's the use of this extra info? If it does,
> what happens if these fields are missing?

Decision belongs to the string template engine.

> This is what I mean by not being able to depend on whether some data is
> there. Ultimately, to do anything useful with the info in the object,
> you need to know what's there.

No. You do realize you are positioning yourself straight against every OO principle there is? The entire OO world is predicated on the assumption you _can_ do useful stuff with an object WITHOUT knowing what's there.

> Preferably, the object's type will tell
> you exactly what's there, then you do a simple map from type to list of
> available attributes (e.g., map exception type to format string with
> known, static list of attributes). But if the type doesn't guarantee
> what data will be present, then your code becomes vastly more complex,
> you have to deal with potentially all possible combinations of what's
> there and what isn't.

I think you are misunderstanding the mechanisms involved. There is no combinatorial code, just simple iteration and such. Dealing with distinct exceptions with distinct code IS combinatorial, repetitive, and non-scalable.

> Instead of a single format string for a single
> exception type, you now have a combinatorial explosion of format strings
> for every possible combination of missing/present fields in the
> exception object.

No, I'm afraid there's a sizeable misunderstanding here.


Andrei
February 21, 2012
On Mon, Feb 20, 2012 at 9:33 PM, Nick Sabalausky <a@a.a> wrote:
>
> "Jose Armando Garcia" <jsancio@gmail.com> wrote in message news:mailman.672.1329758926.20196.digitalmars-d@puremagic.com...
> >
> > 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?
> >
>
> I have some comments on that, but first a little story:
>
> At a previous job with a company that shall remain unnamed, our flagship product was this VB6 thing that handled i18n by wrapping all strings intended to be seen by the user like this:
>
> XLate("Hello, all ") + (UBound(customerArr)-LBound(customerArr)) + XLate("
> of you suckers!")
>
> For non-english users, that XLate function would then lookup the right localization for the given message. IIRC, any missing translation just returned the original english text.
>
> This was the first time I had actually dealt with i18n, and yet I was pretty certain that was a completely moronic approach (not that it was a stretch to believe: there wasn't much going on at main sequence techn...errmm...I mean at unnamed company, that wasn't completely moronic).
>
> I always felt it would be far better ("better" == "won't screw up everytime someone sneezes near the code" and "doesn't make certain things impossible to translate correctly") to have a statically-checked *identifier* that represented a particular message (and not message fragments, either). And then the user-facing string would be looked up via identifier and any appropriate values substituted in. A fairly obvious way to improve^Wfix the system, really.
>

I used to work a large engineering firm that used a different system but had similar problems to the ones you mentioned below. Instead of using a human readable string they would use an assumed to be unique key. My main problem with this is that the person reading the code has no idea what the message is suppose to say. They would have to look it up in this translation database. One of the cool things of the system was that it used "run-time" reflection to look up the parameter. "Run-time" reflection worked in our C++ environment because a lot of the abstract classes (interfaces) were generated from an IDL.

Another interesting part of the system was that it can do recursive translation but if my memory serves me correctly they they needed it because of other limitations in the system.

I think the basic problem with both system is that the set of keys in the translation table is decouple from the code. This "gettext compiler" is a way of making this connection.

> Ok, end of story.
>
> Your mention of gettext, and specifically a "gettext 'compiler'", does introduce a way that the same basic idea could be made to actually work:
>
> Obviously, the big problems with XLate above are:
>
> 1. No way to make sure all messages actually *are* translated (or to prune
> unused translations).
>
> 2. Changing the english text embedded in the code will automatically break all translations.
>
> 3. It fails to handle cases where two inserted values need to be rearranged.
>
> 4. If two messages happen to have a common message fragments, the fragments will always be translated the same even if their differing context dictates they be translated differently
>
> The issues #3 and #4 can obviously be solved by using only full messages with named placeholders and passing the substitution data into xlate(). So those issues are alredy out of the way.
>

These are some really good observations and suggestion. Lets move the conversation to I18N module thread.

> By "gettext 'compiler'" I assume you're taking about some preprocessor. Probably appropriate for C/C++, but seems kinda sloppy, potentially error prone, and inefficient. Fortunately, D has kick-ass metaprogramming, so if you require all translations be compiled in (not sure how realistic that is in general), you could do:
>
> xlate!"Hello world"
>
> And then generate a compile-time error (or a mere notice) for all messages that are unhandled. Of course, if you're loading localizations at runtime, you could still do something similar and just throw a runtime error, or log a warning, etc., that lists all unhandled messages upon loading a localization. Or, hell, a hybrid approach: Compile-time errors for built-in localizations and runtime for the rest. Or generate a list of all messages when compiled with a special version(), which is then handed off to the translation team. All sorts of possibilities. That's all sufficient to solve #1 and #2.
>
> Although there is still the other problem Andrei mentioned of over-eagerness (not an issue in the compile-time case, though).
>
>
February 21, 2012
"H. S. Teoh" <hsteoh@quickfur.ath.cx> wrote:
> On Mon, Feb 20, 2012 at 03:17:44PM -0500, Nick Sabalausky wrote: [...]
>> 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
>>     }
> 
> Somewhere in this giant thread, somebody has proposed that there should be language support for implicitly inheriting base class ctors. That is, if you write:
> 
> 	class Base {
> 		this(string, int, float) { ... }
> 	}
> 
> 	class Derived : Base { }
> 
> then the compiler will automatically insert this into Derived:
> 
> 	this(string s, int i, float f) {
> 		super(s, i, f);
> 	}
> 
> I think this is a very useful mechanism that should be added to D. It will eliminate a lot of ctor boilerplate that you have to put in derived classes (not just exceptions, but in general). For example, if you have:
> 
> 	class Base {
> 		this(string) {...}
> 		this(int) {...}
> 		this(float) {...}
> 	}
> 
> then currently, *every single derived class* must write wrappers to forward ctor arguments to each of those ctors, if they want to provide the same construction API as the base class. With the proposed enhancement, you'd just "inherit" all of the ctors automatically. (Not in the strict sense of inherit, but the compiler will create forwarding ctors for you automatically.)
> 
> 
> T

Note that this should work:

class Derived : Base {
  this(T...)(T values) {
    super(values);
  }
}
February 21, 2012
On Mon, Feb 20, 2012 at 06:19:32PM -0600, Andrei Alexandrescu wrote:
> On 2/20/12 5:46 PM, Nick Sabalausky wrote:
[...]
> >You've suggested adding "Variant[string] info" to Exception for the sake of i18n. I think that's what he's referring to. You *could* argue that's not technically i18n, but so far i18n seems to be the only real use-case for it (although even that much has been disputed in light of reflection).
> 
> All formatting and rendering of the exception information is helped. And I think that's great because that's the most common activity one would do with exceptions.
[...]

Then, obviously, different development environments are leading to vastly different conclusions, because I can't for the life of me imagine that the most common activity you would do with an exception is to print it.  That may be true for a certain class of applications, say command-line tools like compilers and stuff. But that's absolutely not the case at my work project. There, exceptions are usually handled by code and almost never output anywhere, and even when they are, for the most part they just print a simple string literal right from the source code into a debug log channel. Only a tiny percentage of exceptions actually make it all the way to being formatted into a nice localized string displayed to the user.

For the more common case of printing to the debug log, the current Exception facilities (string msg) are more than good enough (since the intended audience is the developers themselves). The i18n stuff is handled by catching the relevant exception by type and looking up the localized string at the catcher level. Any additional info inside the exception object itself isn't used for formatting the localized string anyway, since the user has no idea what it all means.


T

-- 
The day Microsoft makes something that doesn't suck is probably the day they start making vacuum cleaners... -- Slashdotter
February 21, 2012
>
> Jose's argument convinced me otherwise. I retract my agreement.
>
...
>
> No, I'm afraid there's a sizeable misunderstanding here.
>
>
> Andrei

Hahah, yeah, I think there is a sizeable misunderstanding: unless you
are referring to another guy with a spanish name in this thread,
(which I haven't found). My name is Juan Manuel (people call me:
Juanma, JM or Juan, but José is a first! I wouldn't mind John
which is the 'translation' of my name).

Back to the nasty argument. I think that the example that everyone wants is this one. If anyone solves this one without Variant[string] then it's a better solution than Variant[string]. (I repaste it from an above reply I gave):

  [..]
  For instance: a C library wrapper, which gets the library errors encoded
  as some error code and throws them as exceptions. Shouldn't the library
  throw a FileNotFoundException when that's the error, instead of throwing
  a LibraryException that has the error code in a field?

  So the correct thing to do is: after a library call, the wrapper
  checks the last error code number with a switch statement, and deciding
  which standard exception type to throw (defaulting to whatever you like
  if the error code doesn't map to a standard D exception). Then you
  add the error code to the Variant[string], and any other extra info.

  That way, exception types can be standard.

  So, to keep D exception types standard reusable and respected by
  future code, you must follow the Open-Closed design principle
  (nicest principle of OO design ever).
  [..]

Adding the Variant[string] is considered applying the great
Open-Closed Design Principle:
	-Open for reuse.
	-Closed for modification.
        http://www.objectmentor.com/resources/articles/ocp.pdf

--jm


On 02/20/2012 09:38 PM, Andrei Alexandrescu wrote:
> On 2/20/12 6:25 PM, H. S. Teoh wrote:
>> On Mon, Feb 20, 2012 at 05:15:17PM -0600, Andrei Alexandrescu wrote: Formatting should use class reflection. We already discussed that, and we already agreed that was the superior approach.
> 
> Jose's argument convinced me otherwise. I retract my agreement.
> 
>> When you're catching a specific exception, you're catching it with the view that it will contain precisely information X, Y, Z that you need to recover from the problem. If you don't need to catch something, then don't put the catch block there.
> 
> That's extremely rare in my experience, and only present in toy examples that contain a ton of "..." magic.
> 
>> The problem with using Variant[string] is that everything gets lumped into one Exception object, and there's no way to only catch the Exception that happens to have variables "p", "q", and "r" set in the Variant[string].
> 
> No. You are still free to define as many exception classes as you deem necessary. Please let's not pit the hash against the hierarchy again. This is confusing the role of the two. Consider the hash an interface function you want to occasionally implement.
> 
>> You have to catch an exception type that includes all
>> sorts of combinations of data in Variant[string], then manually do tests
>> to single out the exception you want, and rethrow the rest. That's where
>> the ugliness comes from.
> 
> Yah, that would suck, but it's not at all what I say.
> 
>> [...]
>>> The code with Variant[string] does not need combinatorial testing if it wants to do a uniform action (such as formatting). It handles formatting uniformly, and if it wants to look for one particular field it inserts a test.
>>
>> Again, we've already agreed class reflection is the proper solution to this one.
> 
> Agreement rescinded. Sorry! Jose's point was just too good, and reminded me of a pain point I so long had with exception, I'd got used to it as a fact of life.
> 
>>>> And then what do you do if you're depending on a particular field to be set, but it's not? Rethrow the exception? Then you have the stack trace reset problem.
>>>
>>> Don't forget that Variant[string] does not preclude distinct exception types. It's not one or the other.
>> [...]
>>
>> Agreed. But it shouldn't be the be-all and end-all of data passed in exceptions. If anything, it should only be rarely used, with most exception classes using static fields to convey relevant information.
> 
> And to perfectly help code duplication everywhere.
> 
>> I can see the usefulness of using Variant[string] as a way of "decorating" exceptions with "extra attributes", but it shouldn't be the primary way of conveying information from the throw site to the catch site.
>>
>> As for iterating over the information in the most derived class, for formatting, etc., class reflection is the way to go.
> 
> Agreement rescinded as far as exceptions go. That doesn't make reflection any less necessary btw. It just reflects the dynamism of exception paths.
> 
>> We shouldn't be
>> using Variant[string] for this, because there's another problem
>> associated with it: suppose MyException sometimes gets "extra_info1" and
>> "extra_info2" tacked onto it, on its way up the call stack, and
>> sometimes not. Now what should the catcher do?
> 
> Use a central loop to render the information.
> 
>> How do you format this
>> exception?
> 
> With a string template as has been discussed.
> 
>> Should the format string include extra_info1 and extra_info2,
>> or not? If it doesn't, what's the use of this extra info? If it does,
>> what happens if these fields are missing?
> 
> Decision belongs to the string template engine.
> 
>> This is what I mean by not being able to depend on whether some data is there. Ultimately, to do anything useful with the info in the object, you need to know what's there.
> 
> No. You do realize you are positioning yourself straight against every OO principle there is? The entire OO world is predicated on the assumption you _can_ do useful stuff with an object WITHOUT knowing what's there.
> 
>> Preferably, the object's type will tell
>> you exactly what's there, then you do a simple map from type to list of
>> available attributes (e.g., map exception type to format string with
>> known, static list of attributes). But if the type doesn't guarantee
>> what data will be present, then your code becomes vastly more complex,
>> you have to deal with potentially all possible combinations of what's
>> there and what isn't.
> 
> I think you are misunderstanding the mechanisms involved. There is no combinatorial code, just simple iteration and such. Dealing with distinct exceptions with distinct code IS combinatorial, repetitive, and non-scalable.
> 
>> Instead of a single format string for a single
>> exception type, you now have a combinatorial explosion of format strings
>> for every possible combination of missing/present fields in the
>> exception object.
> 
> No, I'm afraid there's a sizeable misunderstanding here.
> 
> 
> Andrei

February 21, 2012
oops, sorry!! I just saw a post by someone named Jose. My thousand apollogies!!

On 02/20/2012 10:01 PM, Juan Manuel Cabo wrote:
>>
>> Jose's argument convinced me otherwise. I retract my agreement.
>>
> ...
>>
>> No, I'm afraid there's a sizeable misunderstanding here.
>>
>>
>> Andrei
> 
> Hahah, yeah, I think there is a sizeable misunderstanding: unless you
> are referring to another guy with a spanish name in this thread,
> (which I haven't found). My name is Juan Manuel (people call me:
> Juanma, JM or Juan, but José is a first! I wouldn't mind John
> which is the 'translation' of my name).
> 
> Back to the nasty argument. I think that the example that everyone wants is this one. If anyone solves this one without Variant[string] then it's a better solution than Variant[string]. (I repaste it from an above reply I gave):
> 
>   [..]
>   For instance: a C library wrapper, which gets the library errors encoded
>   as some error code and throws them as exceptions. Shouldn't the library
>   throw a FileNotFoundException when that's the error, instead of throwing
>   a LibraryException that has the error code in a field?
> 
>   So the correct thing to do is: after a library call, the wrapper
>   checks the last error code number with a switch statement, and deciding
>   which standard exception type to throw (defaulting to whatever you like
>   if the error code doesn't map to a standard D exception). Then you
>   add the error code to the Variant[string], and any other extra info.
> 
>   That way, exception types can be standard.
> 
>   So, to keep D exception types standard reusable and respected by
>   future code, you must follow the Open-Closed design principle
>   (nicest principle of OO design ever).
>   [..]
> 
> Adding the Variant[string] is considered applying the great
> Open-Closed Design Principle:
> 	-Open for reuse.
> 	-Closed for modification.
>         http://www.objectmentor.com/resources/articles/ocp.pdf
> 
> --jm
> 
> 
> On 02/20/2012 09:38 PM, Andrei Alexandrescu wrote:
>> On 2/20/12 6:25 PM, H. S. Teoh wrote:
>>> On Mon, Feb 20, 2012 at 05:15:17PM -0600, Andrei Alexandrescu wrote: Formatting should use class reflection. We already discussed that, and we already agreed that was the superior approach.
>>
>> Jose's argument convinced me otherwise. I retract my agreement.
>>
>>> When you're catching a specific exception, you're catching it with the view that it will contain precisely information X, Y, Z that you need to recover from the problem. If you don't need to catch something, then don't put the catch block there.
>>
>> That's extremely rare in my experience, and only present in toy examples that contain a ton of "..." magic.
>>
>>> The problem with using Variant[string] is that everything gets lumped into one Exception object, and there's no way to only catch the Exception that happens to have variables "p", "q", and "r" set in the Variant[string].
>>
>> No. You are still free to define as many exception classes as you deem necessary. Please let's not pit the hash against the hierarchy again. This is confusing the role of the two. Consider the hash an interface function you want to occasionally implement.
>>
>>> You have to catch an exception type that includes all
>>> sorts of combinations of data in Variant[string], then manually do tests
>>> to single out the exception you want, and rethrow the rest. That's where
>>> the ugliness comes from.
>>
>> Yah, that would suck, but it's not at all what I say.
>>
>>> [...]
>>>> The code with Variant[string] does not need combinatorial testing if it wants to do a uniform action (such as formatting). It handles formatting uniformly, and if it wants to look for one particular field it inserts a test.
>>>
>>> Again, we've already agreed class reflection is the proper solution to this one.
>>
>> Agreement rescinded. Sorry! Jose's point was just too good, and reminded me of a pain point I so long had with exception, I'd got used to it as a fact of life.
>>
>>>>> And then what do you do if you're depending on a particular field to be set, but it's not? Rethrow the exception? Then you have the stack trace reset problem.
>>>>
>>>> Don't forget that Variant[string] does not preclude distinct exception types. It's not one or the other.
>>> [...]
>>>
>>> Agreed. But it shouldn't be the be-all and end-all of data passed in exceptions. If anything, it should only be rarely used, with most exception classes using static fields to convey relevant information.
>>
>> And to perfectly help code duplication everywhere.
>>
>>> I can see the usefulness of using Variant[string] as a way of "decorating" exceptions with "extra attributes", but it shouldn't be the primary way of conveying information from the throw site to the catch site.
>>>
>>> As for iterating over the information in the most derived class, for formatting, etc., class reflection is the way to go.
>>
>> Agreement rescinded as far as exceptions go. That doesn't make reflection any less necessary btw. It just reflects the dynamism of exception paths.
>>
>>> We shouldn't be
>>> using Variant[string] for this, because there's another problem
>>> associated with it: suppose MyException sometimes gets "extra_info1" and
>>> "extra_info2" tacked onto it, on its way up the call stack, and
>>> sometimes not. Now what should the catcher do?
>>
>> Use a central loop to render the information.
>>
>>> How do you format this
>>> exception?
>>
>> With a string template as has been discussed.
>>
>>> Should the format string include extra_info1 and extra_info2,
>>> or not? If it doesn't, what's the use of this extra info? If it does,
>>> what happens if these fields are missing?
>>
>> Decision belongs to the string template engine.
>>
>>> This is what I mean by not being able to depend on whether some data is there. Ultimately, to do anything useful with the info in the object, you need to know what's there.
>>
>> No. You do realize you are positioning yourself straight against every OO principle there is? The entire OO world is predicated on the assumption you _can_ do useful stuff with an object WITHOUT knowing what's there.
>>
>>> Preferably, the object's type will tell
>>> you exactly what's there, then you do a simple map from type to list of
>>> available attributes (e.g., map exception type to format string with
>>> known, static list of attributes). But if the type doesn't guarantee
>>> what data will be present, then your code becomes vastly more complex,
>>> you have to deal with potentially all possible combinations of what's
>>> there and what isn't.
>>
>> I think you are misunderstanding the mechanisms involved. There is no combinatorial code, just simple iteration and such. Dealing with distinct exceptions with distinct code IS combinatorial, repetitive, and non-scalable.
>>
>>> Instead of a single format string for a single
>>> exception type, you now have a combinatorial explosion of format strings
>>> for every possible combination of missing/present fields in the
>>> exception object.
>>
>> No, I'm afraid there's a sizeable misunderstanding here.
>>
>>
>> Andrei
> 

February 21, 2012
On Mon, Feb 20, 2012 at 06:38:08PM -0600, Andrei Alexandrescu wrote: [...]
> >We shouldn't be using Variant[string] for this, because there's another problem associated with it: suppose MyException sometimes gets "extra_info1" and "extra_info2" tacked onto it, on its way up the call stack, and sometimes not. Now what should the catcher do?
> 
> Use a central loop to render the information.

Then it's useless for i18n, unless you can magically translate a sequence of unknown values into a different language in a consistent way.

It sounds like all this is good for is to print a list of "name=value" pairs. Which is useful, I'll admit, for development/debugging purposes. I don't see how such a thing is useful in i18n.


[...]
> >This is what I mean by not being able to depend on whether some data is there. Ultimately, to do anything useful with the info in the object, you need to know what's there.
> 
> No. You do realize you are positioning yourself straight against every OO principle there is? The entire OO world is predicated on the assumption you _can_ do useful stuff with an object WITHOUT knowing what's there.

And the way OO does this is by having the *derived classes* do useful stuff behind a polymorphic interface. What the Variant[string] does is, as you have said previously, to replace the need for many derived classes save a few, thereby throwing away the polymorphism and presenting what amounts to an array of void* to the catch block.

For you to now accuse me of going against OO principles is a really strange argument, I must say. I was in fact *advocating* the use of OO by putting the useful information in the derived classes, where they belong.


> >Preferably, the object's type will tell you exactly what's there, then you do a simple map from type to list of available attributes (e.g., map exception type to format string with known, static list of attributes). But if the type doesn't guarantee what data will be present, then your code becomes vastly more complex, you have to deal with potentially all possible combinations of what's there and what isn't.
> 
> I think you are misunderstanding the mechanisms involved. There is no combinatorial code, just simple iteration and such. Dealing with distinct exceptions with distinct code IS combinatorial, repetitive, and non-scalable.

Then pray tell how "simple iteration" will achieve the translation of the data in Variant[string] into a localized string, when there is no guarantee any field will be in the hash at all. Format strings obviously won't work, since you can't have a format string unless you already know what arguments are present.

Or are you just going to individually print out "name=value" pairs? In which case, we've been talking at cross purposes. There is no i18n here at all.


T

-- 
Those who've learned LaTeX swear by it. Those who are learning LaTeX swear at it. -- Pete Bleackley
February 21, 2012
On Monday, February 20, 2012 18:38:08 Andrei Alexandrescu wrote:
> On 2/20/12 6:25 PM, H. S. Teoh wrote:
> > This is what I mean by not being able to depend on whether some data is there. Ultimately, to do anything useful with the info in the object, you need to know what's there.
> 
> No. You do realize you are positioning yourself straight against every OO principle there is? The entire OO world is predicated on the assumption you _can_ do useful stuff with an object WITHOUT knowing what's there.

Exceptions aren't very OO really. Aside from getting a string out of them, you generally don't have any polymorphism involved, and even with the string, there's a good chance that you don't. For instance, the message field which toString uses to generate its string is passed as an argument to Exception's constructor.

With exceptions, it's the types that matter. It's not a case of changing behavior based on implementation. With OO, you try _not_ to care about what the type is. With exceptions, on the other hand, you care a great deal. It's inverted from OO. Exceptions hold data but not much behavior. The behavior goes in the catch blocks.

If we had a construct which allowed inheritance but not polymorphism (which we don't, since for better or wors, inheritance is conflated with polymorphism in D), then that would make a lot of sense for exceptions.

It doesn't really hurt us that exceptions have polymorphism, but it doesn't generally help them do their job at all. If you're going to do anything beyond simply print an error message, you need to know what the exception type is and what data it has. And that's not OO at all.

- Jonathan M Davis
February 21, 2012
Andrei Alexandrescu wrote:
> On 2/20/12 4:25 PM, Jonathan M Davis wrote:
>> I definitely think that we should favor putting data in member
>> variables, not
>> in a hashtable of Variants.

+1

> Whenever you put data in member variables you set yourself up for code
> bloat. Do you agree?

It's a problem of classes as a whole, not just exceptions. I'm afraid such 'pressure' will limit the use of classes in Phobos.
February 21, 2012
On Mon, Feb 20, 2012 at 10:01:03PM -0300, Juan Manuel Cabo wrote: [...]
> Back to the nasty argument. I think that the example that everyone wants is this one. If anyone solves this one without Variant[string] then it's a better solution than Variant[string]. (I repaste it from an above reply I gave):
> 
>   [..]
>   For instance: a C library wrapper, which gets the library errors encoded
>   as some error code and throws them as exceptions. Shouldn't the library
>   throw a FileNotFoundException when that's the error, instead of throwing
>   a LibraryException that has the error code in a field?
> 
>   So the correct thing to do is: after a library call, the wrapper
>   checks the last error code number with a switch statement, and deciding
>   which standard exception type to throw (defaulting to whatever you like
>   if the error code doesn't map to a standard D exception). Then you
>   add the error code to the Variant[string], and any other extra info.

But why bother with the error code at all? If you get a FileNotFoundException, you already know all there is to know about the problem, adding errno to it is redundant and only encourages code that's bound to a specific implementation.

Instead, Phobos should present a self-consistent API that's independent
of what it uses to implement it, be it C stdio (errno) or C++ iostreams
or Oracle driver (Oracle-specific error codes) or Postgresql driver
(Postgresql-specific error codes), or what have you.

For error codes that *don't* have a direct mapping to standard exceptions, you can just encapsulate the errno (or whatever) inside a specific catch-all exception type dedicated to catch these sorts of unmapped cases, so that code that *does* know what errno can just catch this exception and interpret what happened. General, platform-independent code need not know what this exception is at all, they can just treat it as a general problem and react accordingly.  We don't (and shouldn't) expect every app out there to know or care about the errno of a failed operation, especially if it doesn't map to one of the standard exception types.


>   That way, exception types can be standard.
> 
>   So, to keep D exception types standard reusable and respected by
>   future code, you must follow the Open-Closed design principle
>   (nicest principle of OO design ever).
>   [..]
> 
> Adding the Variant[string] is considered applying the great
> Open-Closed Design Principle:
> 	-Open for reuse.
> 	-Closed for modification.
>         http://www.objectmentor.com/resources/articles/ocp.pdf
[...]

Please bear in mind, I'm not saying that Variant[string] is *completely* useless. I'm just saying that most of the time it's not necessary. Sure there are some cases where it's useful, I've no problem with it being used in those cases. But we shouldn't be using it for all kinds of stuff that can be handled in better ways, e.g., static fields in a derived exception class.


T

-- 
My program has no bugs! Only undocumented features...