2nd day meeting comments on the latest i18n API proposal

# Nebojša Ćirić (15 years ago)

Eric proposed to remove the derive method from all API objects and do something like this:

var loc = new LocaleInfo({....}); // {...} are the options we construct LocaleInfo object with. var opt2 = loc.options; // This returns a copy of options from loc object. opt2.currency = "USD"; var loc2 = new LocaleInfo(opt2);

This approach yields the same result with more code, but it's a more in sync with how people expect JavaScript API to work.

In this case LocaleInfo needs to have options property that holds original inputs. This approach could also help with inferred values - one can compare options with actual property and see if they are the same.

var loc = LocaleInfo({currency: 'USD'}); if (loc.options.currency == loc.currency) ...

# Shawn Steele (15 years ago)

The list didn’t like my email address…, trying again.

From: Shawn Steele Sent: Poʻakolu, Ianuali 19, 2011 6:12 hours To: 'Nebojša Ćirić'; es-discuss at mozilla.org Subject: RE: 2nd day meeting comments on the latest i18n API proposal

I think a “common” case is to tweak a part of a LocaleInfo object, so I think it’d be nice if there were a less code approach. But I agree that this is how JavaScript devs would expect this to work.

The v0.5 doc already had an options property, and we’d agreed that it was useful yesterday, although we qualified it being read-only. It’s not clear to me if most javascript options properties are read only or writeable?

-Shawn

From: es-discuss-bounces at mozilla.org<mailto:es-discuss-bounces at mozilla.org> [mailto:es-discuss-bounces at mozilla.org]<mailto:[mailto:es-discuss-bounces at mozilla.org]> On Behalf Of Nebojša Ciric

Sent: Poʻakolu, Ianuali 19, 2011 5:02 hours To: es-discuss at mozilla.org<mailto:es-discuss at mozilla.org>

Subject: 2nd day meeting comments on the latest i18n API proposal

Eric proposed to remove the derive method from all API objects and do something like this:

var loc = new LocaleInfo({....}); // {...} are the options we construct LocaleInfo object with. var opt2 = loc.options; // This returns a copy of options from loc object. opt2.currency = "USD"; var loc2 = new LocaleInfo(opt2);

This approach yields the same result with more code, but it's a more in sync with how people expect JavaScript API to work. In this case LocaleInfo needs to have options property that holds original inputs. This approach could also help with inferred values - one can compare options with actual property and see if they are the same.

var loc = LocaleInfo({currency: 'USD'}); if (loc.options.currency == loc.currency) ...

-- Nebojša Ćirić

# Mark S. Miller (15 years ago)

On Wed, Jan 19, 2011 at 5:02 PM, Nebojša Ćirić <cira at google.com> wrote:

Eric proposed to remove the derive method from all API objects and do something like this:

var loc = new LocaleInfo({....}); // {...} are the options we construct LocaleInfo object with. var opt2 = loc.options; // This returns a copy of options from loc object. opt2.currency = "USD"; var loc2 = new LocaleInfo(opt2);

The above code does suggest that the assignment is changing the options that are stored at loc.options. That is of course not the intention. Either it should give a fresh mutable copy on each request or it should give frozen options object. The first was intended. On reflection, I think it would be clearer if options were obtained using a method call such as "loc.options()" rather than using an accessor with non-storage semantics.

# Erik Arvidsson (15 years ago)

On Wed, Jan 19, 2011 at 20:23, Mark S. Miller <erights at google.com> wrote:

On Wed, Jan 19, 2011 at 5:02 PM, Nebojša Ćirić <cira at google.com> wrote:

Eric proposed to remove the derive method from all API objects and do something like this: var loc = new LocaleInfo({....});  // {...} are the options we construct LocaleInfo object with. var opt2 = loc.options;  // This returns a copy of options from loc object. opt2.currency = "USD"; var loc2 = new LocaleInfo(opt2);

The above code does suggest that the assignment is changing the options that are stored at loc.options. That is of course not the intention. Either it should give a fresh mutable copy on each request or it should give frozen options object. The first was intended. On reflection, I think it would be clearer if options were obtained using a method call such as "loc.options()" rather than using an accessor with non-storage semantics.

I agree that having a method that returns a copy of the options is clearer.

# Allen Wirfs-Brock (15 years ago)

Wait...

The whole point of the derive method was for the new object to have the former object as its direct [[Prototype]] so they could share common state. That is lost in what is proposed below. Also, the state of an object is not necessarily wholly reflected by the options that are passed in when it is created. Using Object.defineProperty as an example the property descriptor is analyzed in association with the current state of the property that is being defined. If a descriptor does not explicit include one of its options and the property does not exist it is given a default state. But if the property already exists and the descriptor does not explicitly include one of the options then the current state of the corresponding property attribute is keep unchanged.

In general, after one of these objects is created. the state of the object is inrepresented by its property values, not by the set of option values that were used to create it. There is not necessarily a 1::1 correspondence between the two. While there may be some small value in keeping track of the original options parameter value (for debugging??) there are actual hazards and costs to this. Would you save the actual option object or a copy? (It better be a copy, who knows what the caller is doing with it). What about its prototype chain from which it may be inheriting option values? How do you copy it. Do you get a fresh copy each time it is access?? Etc. Overall, I'm not sure it is worth trying to retain. We certainly don't do so for property descriptors.

While I don't like the nam "derive", I think this pattern is very true to the core nature of JavaScript. It is using prototype inherence the way god and Ungar originally intended it. As an alternative to the name "derive" I would consider "new":

var loc= LocaleInfo(); //get default value print(loc.currency); //"EUR" (whatever the Euro code is...) print(loc.currencySymbol); //€
var loc2=loc.new({preferredCurreny: :"USD"}); print(loc.currency); //"USD" (whatever the Euro code is...) print(loc.currencySymbol); //$

(the above is just a hypothethical example and isn't actually proposing anything about the actual properties of a LocaleInfo))

# Nebojša Ćirić (15 years ago)

But if the object is immutable, then only parameters to the constructor are important, and any state object has gets reconstructed at construction time. Or I am mistaken and you can change [[Prototype]] on immutable object?

Током 20. јануар 2011. 09.14, Allen Wirfs-Brock <allen at wirfs-brock.com> је написао/ла:

# Allen Wirfs-Brock (15 years ago)

No, [[Prototype]] is supposed to be immutable.

Prototype inheritance permits state sharing among objects. this means that expensive state that sill applies does not need to be replicated when deriving a new object.

Also, you seem to be assuming that there is a 1::1 correspondence between the options and the object properties. Options could contain arbitrary parameters to the constructor such as:

var loc = LocaleInfo({loadFrom: "http:localeCentral.org/lib/en-us", filterBy: {currency:"USD", timezone:+3}});

# Shawn Steele (15 years ago)

I didn't expect them to share a common state, rather I expected the new object to have a copy of the original object's state, modified by the new options.

Returning an immutable object for options seems nice, but then it'd be hard to update the options to get new options for the new object.

FWIW: The original idea for doing this was to include the base locale as an option:

var loc = new LocaleInfo({....}); // {...} are the options we construct LocaleInfo object with. var loc2 = new LocaleInfo({ localeInfo: loc, currency: "USD" });

The expectation then being that the original localeInfo would provide the base state, and additional options would modify that state.

I don't really care how we do it, but obviously we need the idea. I'd be happiest if the code for the concept was short, which .derive seems to do.

# Nebojša Ćirić (15 years ago)

If we were to go with loc.options, I would define it as having only items that were explicitly listed when object was constructed. So:

var loc = new LocaleInfo({'calendar':'hijri', 'lang': 'en-US', 'inferValues': true});

loc.options would contain: loc.options.calendar - hijri loc.options.lang - en-US loc.options.inferValues - true

loc object would have: loc.currency loc.lang loc.inferValues loc.calendar (maybe) loc.region ...

One could implement isInferred(x) method like this: function isInferred(x) { if (x in loc.options && x in loc && loc.options.x === x) return false; else return true;

There would be some duplication of data with this approach, but it would give us easy way to check what were the options we passed in when constructed the object, and to detect if the value was inferred or not.

Btw. I think we agreed to have inferreValues set to true by default.

  1. јануар 2011. 22.15, Peter Constable <petercon at microsoft.com> је написао/ла:
# Axel Hecht (15 years ago)

I for one am very much against setting inferValues to true by default. That's just obfuscating i18n bugs, and I think we should design our APIs so that it's easy to get it right. Shooting yourself in the foot should be hard, if at all possible.

Axel

# Mark Davis ☕ (15 years ago)

I would actually rather not have it be a construction argument, because it is easier for people to make mistakes that way.

When I look this over, there are relatively few fields that need this. So what about having API like:

// get an explicitly-set region, or null if there was no region parameter in the constructor.

region1 = myLocaleInfo.region;

// gets myLocaleInfo.region if not null // otherwise infers the region from other information in the LocaleInfo.

aRegion = myLocaleInfo.inferRegion();

The ones where this would be done would be:

inferBaseLanguage() inferScript() inferRegion() inferCurrency() // later: inferTimeZone()

Mark

— Il meglio è l’inimico del bene —

# Shawn Steele (15 years ago)

IMO that’s going overboard in the other direction ☺ It’d be nice to find some middle ground.

Sometimes inferring can be very bad. Sometimes it can be very good. The problem isn’t that one is “right” or “wrong” for all apps, but rather that it might be simple for developers to accidentally make the wrong choice, or not realize they need to make a choice. If I need to infer, it should be easy to get a fully-inferred LocaleInfo. If I don’t need to infer, then it should be easy to avoid inferring for LocaleInfo.

-Shawn

From: es-discuss-bounces at mozilla.org [mailto:es-discuss-bounces at mozilla.org] On Behalf Of Mark Davis ? Sent: Friday, January 21, 2011 12:44 PM To: Axel Hecht Cc: Derek Murman; es-discuss at mozilla.org Subject: Re: 2nd day meeting comments on the latest i18n API proposal

I would actually rather not have it be a construction argument, because it is easier for people to make mistakes that way.

When I look this over, there are relatively few fields that need this. So what about having API like:

// get an explicitly-set region, or null if there was no region parameter in the constructor.

region1 = myLocaleInfo.region;

// gets myLocaleInfo.region if not null // otherwise infers the region from other information in the LocaleInfo.

aRegion = myLocaleInfo.inferRegion();

The ones where this would be done would be:

inferBaseLanguage() inferScript() inferRegion() inferCurrency() // later: inferTimeZone()

Mark

— Il meglio è l’inimico del bene —

On Fri, Jan 21, 2011 at 11:19, Axel Hecht <axel at mozilla.com<mailto:axel at mozilla.com>> wrote:

I for one am very much against setting inferValues to true by default. That's just obfuscating i18n bugs, and I think we should design our APIs so that it's easy to get it right. Shooting yourself in the foot should be hard, if at all possible.

Axel

On 21.01.11 09:39, Nebojša Ćirić wrote: If we were to go with loc.options, I would define it as having only items that were explicitly listed when object was constructed. So:

var loc = new LocaleInfo({'calendar':'hijri', 'lang': 'en-US', 'inferValues': true});

loc.options would contain: loc.options.calendar - hijri loc.options.lang - en-US loc.options.inferValues - true

loc object would have: loc.currency loc.lang loc.inferValues loc.calendar (maybe) loc.region ...

One could implement isInferred(x) method like this: function isInferred(x) { if (x in loc.options && x in loc && loc.options.x === x) return false; else return true;

There would be some duplication of data with this approach, but it would give us easy way to check what were the options we passed in when constructed the object, and to detect if the value was inferred or not.

Btw. I think we agreed to have inferreValues set to true by default.

  1. јануар 2011. 22.15, Peter Constable <petercon at microsoft.com<mailto:petercon at microsoft.com> <mailto:petercon at microsoft.com<mailto:petercon at microsoft.com>>> је написао/ла:

We had talked about having an option on the LocaleInfo constructor to control whether values not explicitly set could be inferred. E.g.

var loc = new LocaleInfo({lang:”en-US”, inferValues:true});

var curr = loc.currency; // is USD

With the approach of having .derive() methods, then the constructed clone is exactly the same. So, for instance,

var loc2 = loc.derive({calendar:hijri});

if (loc2.isInferred(currency)) // is true

Considering this other approach, when I get opt2…

var opt2 = loc.options;

what all does opt2 include? Does it include only options that were explicitly passed in when loc was constructed, or does it include specific values for all LocaleInfo properties that could be set as options and could be inferred? If the latter, does it reflect that most of them were inferred?

Peter *From:*Nebojša Ćirić [mailto:cira at google.com<mailto:cira at google.com> <mailto:cira at google.com<mailto:cira at google.com>>]

Sent: Wednesday, January 19, 2011 5:02 PM To: es-discuss at mozilla.org<mailto:es-discuss at mozilla.org> <mailto:es-discuss at mozilla.org<mailto:es-discuss at mozilla.org>>

Subject: 2nd day meeting comments on the latest i18n API proposal

Eric proposed to remove the derive method from all API objects and do something like this:

var loc = new LocaleInfo({....}); // {...} are the options we construct LocaleInfo object with.

var opt2 = loc.options; // This returns a copy of options from loc object.

opt2.currency = "USD";

var loc2 = new LocaleInfo(opt2);

This approach yields the same result with more code, but it's a more in sync with how people expect JavaScript API to work.

In this case LocaleInfo needs to have options property that holds original inputs. This approach could also help with inferred values

  • one can compare options with actual property and see if they are the same.

var loc = LocaleInfo({currency: 'USD'});

if (loc.options.currency == loc.currency) ...

-- Nebojša Ćirić

-- Nebojša Ćirić

# Mark Davis ☕ (15 years ago)

The problem I see is that if I hand you a LocaleInfo, and there is only one API to get the region, then it (in your words) is easy to "accidentally make the wrong choice, or not realize they need to make a choice".

  • x.region may be an explicit value or may be computed: I have to call some other API to find out.

If we have a separate API, then I, the developer, have it clearly in front of me what choice I am to to make, and it is harder to accidentally make the wrong choice, &c.

  • x.region is always an explicit value
  • x.inferRegion() is computed if there is no explicit value.

Mark

— Il meglio è l’inimico del bene —

# Shawn Steele (15 years ago)

I think a reasonable approach is to tag the localeinfo itself as “please infer” or “do not infer”. Then I don’t have to use special code in a helper API for a caller that wants one behavior or the other. And it’s reasonably trivial for me to collect the information. Presumably I could even do a .Derive() (or whatever it turns out to be) and get an inferred object from an non-inferred object (or visa versa). While I’d prefer “please infer” by default, I recognize that approach doesn’t avoid user error as much as the “do not infer” by default approach. I think that requiring “please infer” in the input options would help avoid accidentally not making a choice, yet still making it fairly easy to use. -Shawn

From: mark.edward.davis at gmail.com [mailto:mark.edward.davis at gmail.com] On Behalf Of Mark Davis ? Sent: Friday, January 21, 2011 4:01 PM To: Shawn Steele Cc: Axel Hecht; Derek Murman; es-discuss at mozilla.org Subject: Re: 2nd day meeting comments on the latest i18n API proposal

The problem I see is that if I hand you a LocaleInfo, and there is only one API to get the region, then it (in your words) is easy to "accidentally make the wrong choice, or not realize they need to make a choice".

  • x.region may be an explicit value or may be computed: I have to call some other API to find out.

If we have a separate API, then I, the developer, have it clearly in front of me what choice I am to to make, and it is harder to accidentally make the wrong choice, &c.

  • x.region is always an explicit value
  • x.inferRegion() is computed if there is no explicit value.

Mark

— Il meglio è l’inimico del bene —

On Fri, Jan 21, 2011 at 14:04, Shawn Steele <Shawn.Steele at microsoft.com<mailto:Shawn.Steele at microsoft.com>> wrote:

IMO that’s going overboard in the other direction ☺ It’d be nice to find some middle ground.

Sometimes inferring can be very bad. Sometimes it can be very good. The problem isn’t that one is “right” or “wrong” for all apps, but rather that it might be simple for developers to accidentally make the wrong choice, or not realize they need to make a choice. If I need to infer, it should be easy to get a fully-inferred LocaleInfo. If I don’t need to infer, then it should be easy to avoid inferring for LocaleInfo.

-Shawn

From: es-discuss-bounces at mozilla.org<mailto:es-discuss-bounces at mozilla.org> [mailto:es-discuss-bounces at mozilla.org<mailto:es-discuss-bounces at mozilla.org>] On Behalf Of Mark Davis ?

Sent: Friday, January 21, 2011 12:44 PM To: Axel Hecht Cc: Derek Murman; es-discuss at mozilla.org<mailto:es-discuss at mozilla.org>

Subject: Re: 2nd day meeting comments on the latest i18n API proposal

I would actually rather not have it be a construction argument, because it is easier for people to make mistakes that way.

When I look this over, there are relatively few fields that need this. So what about having API like:

// get an explicitly-set region, or null if there was no region parameter in the constructor.

region1 = myLocaleInfo.region;

// gets myLocaleInfo.region if not null // otherwise infers the region from other information in the LocaleInfo.

aRegion = myLocaleInfo.inferRegion();

The ones where this would be done would be:

inferBaseLanguage() inferScript() inferRegion() inferCurrency() // later: inferTimeZone()

Mark

— Il meglio è l’inimico del bene — On Fri, Jan 21, 2011 at 11:19, Axel Hecht <axel at mozilla.com<mailto:axel at mozilla.com>> wrote:

I for one am very much against setting inferValues to true by default. That's just obfuscating i18n bugs, and I think we should design our APIs so that it's easy to get it right. Shooting yourself in the foot should be hard, if at all possible.

Axel

On 21.01.11 09:39, Nebojša Ćirić wrote: If we were to go with loc.options, I would define it as having only items that were explicitly listed when object was constructed. So:

var loc = new LocaleInfo({'calendar':'hijri', 'lang': 'en-US', 'inferValues': true});

loc.options would contain: loc.options.calendar - hijri loc.options.lang - en-US loc.options.inferValues - true

loc object would have: loc.currency loc.lang loc.inferValues loc.calendar (maybe) loc.region ...

One could implement isInferred(x) method like this: function isInferred(x) { if (x in loc.options && x in loc && loc.options.x === x) return false; else return true;

There would be some duplication of data with this approach, but it would give us easy way to check what were the options we passed in when constructed the object, and to detect if the value was inferred or not.

Btw. I think we agreed to have inferreValues set to true by default.

  1. јануар 2011. 22.15, Peter Constable <petercon at microsoft.com<mailto:petercon at microsoft.com> <mailto:petercon at microsoft.com<mailto:petercon at microsoft.com>>> је написао/ла:

We had talked about having an option on the LocaleInfo constructor to control whether values not explicitly set could be inferred. E.g.

var loc = new LocaleInfo({lang:”en-US”, inferValues:true});

var curr = loc.currency; // is USD

With the approach of having .derive() methods, then the constructed clone is exactly the same. So, for instance,

var loc2 = loc.derive({calendar:hijri});

if (loc2.isInferred(currency)) // is true

Considering this other approach, when I get opt2…

var opt2 = loc.options;

what all does opt2 include? Does it include only options that were explicitly passed in when loc was constructed, or does it include specific values for all LocaleInfo properties that could be set as options and could be inferred? If the latter, does it reflect that most of them were inferred?

Peter *From:*Nebojša Ćirić [mailto:cira at google.com<mailto:cira at google.com> <mailto:cira at google.com<mailto:cira at google.com>>]

Sent: Wednesday, January 19, 2011 5:02 PM To: es-discuss at mozilla.org<mailto:es-discuss at mozilla.org> <mailto:es-discuss at mozilla.org<mailto:es-discuss at mozilla.org>>

Subject: 2nd day meeting comments on the latest i18n API proposal

Eric proposed to remove the derive method from all API objects and do something like this:

var loc = new LocaleInfo({....}); // {...} are the options we construct LocaleInfo object with.

var opt2 = loc.options; // This returns a copy of options from loc object.

opt2.currency = "USD";

var loc2 = new LocaleInfo(opt2);

This approach yields the same result with more code, but it's a more in sync with how people expect JavaScript API to work.

In this case LocaleInfo needs to have options property that holds original inputs. This approach could also help with inferred values

  • one can compare options with actual property and see if they are the same.

var loc = LocaleInfo({currency: 'USD'});

if (loc.options.currency == loc.currency) ...

-- Nebojša Ćirić

-- Nebojša Ćirić

# Allen Wirfs-Brock (15 years ago)

The pattern of capturing constructor arguments for later inspection isn't currently used by any of the current built-in ECMAScript objects. Not even by those that explicit use an "options object" to pass complex sets of optional arguments. In general, I think capture of constructor arguments is a pretty bad idea and that should be avoided. One reason is a matter of factoring of concerns. Generally the construction technique used to create an object should not be of concern to a consumer of the object.

I'm going to be quite firm on this for now: The objects in this API should not have an option properties. If an options object is passed to the constructor (or "derive method") that is used to create one of these objects a reference to the option object is not retained by the resulting object.

Being more concrete, assuming that a LocaleObject may be created by an expression like: var loc = new LocaleInfo({'calendar':'hijri', 'lang': 'en-US', 'inferValues': true}); It is OK for loc to have properties such as: loc.clalendar loc.lang loc.hasInferredValues

It is not OK to say loc.options and expect to get back: {'calendar':'hijri', 'lang': 'en-US', 'inferValues': true}

This is a principle that should be applied to all of the I18N objects.

Specifically in regard to inferred values, I suspect that it isn't correct to equate "inferred" with "not explicitly specified in the object constructor". My understanding, from the discussion at the meeting, was that "inferred" meant was that the value was not explicitly provided by the source definition of a "locale" o(r other related I18N object) component object. I assume that the source definition is not necessarily restricted to property initialization values provide to a constructor via an options object. In many cases the locale definition is going to come from a host or web resource. If either case the concept of "inferred" would presumably be the same: that the original defining authority did not provide the value and so an inference process was used to obtain a value.

There are many ways that the inferred properties might be tracked within one of these objects: as a bitmap, as a collection of the names of properties that were inferred, as a marker object that wraps the actual value object, as a bit in the state of the value object, etc.

The public API to test that a property value is inferred is largely independent on that that fact is actually tracked. It might be the loc.isInferred("propName") interface or loc.namesInferredProperties or something else.