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

Allen Wirfs-Brock allen at wirfs-brock.com
Wed Feb 1 08:59:57 PST 2012


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
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20120201/3e058909/attachment-0001.html>


More information about the es-discuss mailing list