Design principles for extending ES object abstractions

Allen Wirfs-Brock allen at wirfs-brock.com
Mon Jul 11 08:58:16 PDT 2011


On Jul 10, 2011, at 4:01 PM, Brendan Eich wrote:

> On Jul 10, 2011, at 3:54 PM, Brendan Eich wrote:
> 
>> On Jul 10, 2011, at 3:02 PM, David Herman wrote:
>> 
>>>>> I'm not sure what Array.prototype methods would or wouldn't work on instances of SubArray.
>>>> 
>>>> All of them.  They are all generic.
>>> 
>>> We're speaking too broadly here. It depends on what we want to work how. For example, .map can't magically know how to produce a SubArray as its result if that's how SubArray wants it to work.
> 
> 
> This is the real concern, I believe. The Array methods (all generic in that they take array-like |this| -- at least one is Array-specific in argument handling: concat) that return a new array always make an instance of Array.

This is something that is one my list to cleanup as part of my [[Class]] reform effort: http://wiki.ecmascript.org/doku.php?id=strawman:es5_internal_nominal_typing 


> 
> Alex Russell brought up the idea of extending these array-producing methods to call a user-defined constructor. We discussed doing it by reflecting on this.constructor, but that is not a compatible change: people today call Array.prototype.slice.call(arguments) intending to get an Array instance whose elements come from the arguments object. It would be wrong to change this code to return a new Object, but that's what reflecting on this.constructor would do, because:

My thinking was to make allow to have such methods look for a specific instance property (Smalltalk called its "species", but we might want to use a private name) whose value is the constructor to use to create the private name.

The default value of the property would be Array.  Same if it was missing.  For example:

const augmentedArrayProto = {
   species: AugmentedArray,
   ...
};
function AugmentedArray (...args) {
   return augmentedArrayProto <| [...args]
};

The main problem with this approach is that the developer has to explicitly decide to do so.  However, if isn't clear that there is an "automatic" scheme that would preserve backwards compat.  Also, if you actually are create a new kind of Array-like collection this is probably something you do want to think about as you don't necessarily always want to produce the same kind of collection as the this value.


> 
> js> function f(){return arguments.constructor}
> js> f()
> function Object() {[native code]}
> 
> So we have two choices:
> 
> 1. Duplicate the array methods with new twins mutated to reflect on this.constructor -- this is ugly and bloaty, even if we can segregate the methods so as to avoid mangling the twins' names.

I've considered providing an Array.extensiblePrototype object that would be a better [[Prototype]] than Array.prototype for new collections. However, I always concluded that it would just create confusion.  It would be better to take the "species" approach which is a target solution to a specific problem.

> 
> 2. Add a new protocol, perhaps enabled by setting a well-known private name object (i.e., a unique public name), denoted it kResultConstructor, so that the methods can detect something like this[kResultConstructor].
see above
> 
> (2) adds a bit of runtime cost to the methods, which are not super-optimized in many way in today's engines. Not sure that hurts it much, compared to (1).

Except for very small collections, this extra indirection should be a small overhead compared to the actual cost of processing the collection.


> Do modules help us make a better, less bloaty form of (1)?
> 
> /be
> _______________________________________________
> es-discuss mailing list
> es-discuss at mozilla.org
> https://mail.mozilla.org/listinfo/es-discuss

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20110711/0f065c84/attachment.html>


More information about the es-discuss mailing list