I18n - defining format() and compare() as getters to simplify usage

Norbert Lindenberg ecmascript at norbertlindenberg.com
Sun Feb 5 20:47:59 PST 2012


I have updated the specification such that Collator.prototype.compare is now a getter that returns a bound function, which is cached by the first call to the getter:

1. If the [[boundCompare]] internal property of this Collator is undefined, then:
    a. Let that be this.
    b. Let bc be a function that takes the arguments x and y and performs the following steps:
        i. Return the result of calling the [[Compare]] internal method of that with arguments x and y.
    c. Set the [[boundCompare]] internal property of this Collator to bc.
2. Return the value of the [[boundCompare]] internal property of this Collator.

[[Compare]] is what used to be Collator.prototype.compare in previous specification drafts.

The [[Set]] attribute is undefined.

I have no evidence that the format() methods are "likely to be routinely invoked as a standalone function", so I haven't changed those functions.

Norbert



On Feb 1, 2012, at 8:59 , Allen Wirfs-Brock wrote:

> 
> On Jan 31, 2012, at 4:23 PM, Nebojša Ćirić wrote:
> 
>> 31. јануар 2012. 15.50, Norbert Lindenberg <ecmascript at norbertlindenberg.com> је написао/ла:
>> I can imagine doing this for Collator.prototype.compare because Array.prototype.sort is such a common use case for it, but why for the format() methods?
> 
> I think there is a straightforward design rule to apply in deciding whether such a property should be a unbound method or a bound function.
> 
> If the value of the property is a function that is dependent upon the state of its access object and the function is likely to be routinely invoke as a standalone function then use a bound (or otherwise closed over the object) function as the property value. 
> More simply,  if the property is routinely accessed with an expression like:
>     obj.prop
> then it should be a bound function.  If it is routinely accessed with an expression like:
>    obj.prop()
> then it can be a normal method.
> 
> We have a good use case for Collator.compare that strongly suggests it should be bound:
>     array.sort(col.compare)
> 
> Do we have comparable use cases for Formatter.format?
> 
>> 
>> I heard a couple of reasons for format methods to be bound:
>> 
>> 1. Can be passed as functions, thus hiding the object details
> 
> would this be routinely done?  If so it is a strong use case for a bound function
>     displayDate(dt,  mdyFmtr.format)
> 
>> 2. Makes it symmetrical to compare (in case we follow Allen's advice)
>    symmetry is only important if the use cases of the two functions are similar.  I assume that booth Collator and Formatter have plenty of properties that are normally invoked as regular methods.
> 
>> 3. No binding gotchas for users
> There are always gotchas if you try to dissociate a function that has (explicit or implicit,if built-in) this reference dependencies. That is the reason for the above design rule. Retrieving a bound function is a different logical operation than extracting a method implementation as an reflective operation.
> 
>>  
>> We don't want to impose the overhead of creating a bound function on each call to format() unless there's a good reason...
>> 
>> Would caching first one resolve the overhead, like so (not sure about syntax):
> 
> Yes, you would definitely want to memoize the function on first access or perhaps even upon object creation. 
> 
>> 
>> NumberFormat.prototype = {
>>   get format(date) {
>>      var that = this;
>>      if (that.__bound === undefined)
>>        that.__bound = function(a) { uses that; return a < b };
>>      return that.__bound;
>>   }
>> }
> 
> Yes, logically.  However a built-in implementation would use a private internal state variable [[CompareFunction]] instead of a regular property.  An ES6 implementation in ECMAScript would use a private name property.
> 
> Alternatively, you could simply create the bound function when you instantiate the  Format instancet object and make it the value of the format (in this example) own property of the new instance.  However, that technique would create issues if anybody ever wants to do prototypal inheritance from such an instance.  For that reason, I would stick with the accessor on the prototype pattern.
> 
>> 
>> Of course, doing it for one and not the other is somewhat inconsistent. And since ES 5 has Function.prototype.bind (implemented in the leading browsers except Safari), it's not hard to bind compare() without library support:
>> myArray.sort(collator.compare.bind(collator));
>> 
> 
> Do you really want a naive HTML coder to have to remember this pattern?
> 
> 
>> Yes, we should probably pick one or the other - go jQuery route and force developers to .bind manually, or any other library where they either bind for the users or provide two versions of the same method (one bind the other one not).
> 
> For these cases,  I don't see why you would need the unbound method.  Such a unbound method is really only useful it you are going to move it to another object and this isn't any particular reason why that should be expected to work in this scenario.  Even though the function is bound you can still say:
> 
>   col.compare(a,b)
> 
> if you want to invoke it using a method invocation style
> 
> Allen



More information about the es-discuss mailing list