Array subclassing, .map and iterables (Re: Jan 30 TC39 Meeting Notes)
Allen Wirfs-Brock
allen at wirfs-brock.com
Sat Feb 9 12:33:03 PST 2013
On Feb 9, 2013, at 2:16 AM, Claus Reinke wrote:
> I am trying to understand the discussion and resolution of
> 'The Array Subclassing "Kind" Issue'. The issue (though not its
> solution) seemed simple enough
>
> class V extends Array { ... }
> m = (new V()).map(val => val);
> console.log( m instanceof V ); // false :(
>
> and I was expecting solutions somewhere along this path:
>
> 1. .map should work for Array subclasses, preserving class
>
> 2. .map is independent of Array and its subclasses, there are
> lots of types for which it makes sense (Sets, EventEmitters, ..)
>
> 3. there should be an interface Mapable, implemented by
> Array and its subclasses, but also by other relevant classes,
> such that
the issue is that a map function can broaden the domain of array elements. For example,
var intArray = new Int32Array([42,85,127649,32768]); //create a typed array from a regular array
var strArray = intArray.map(v=>v.toString());
If intArray.map() produces a new intArray then the above map function is invalid. If intArray.map() produces an Array instance then you intArray.map instance of intArray.constructor desire won't hold. We can't have it both ways without provide some additional mechanism that probably involves additional parameters to some methods or new methods.
The choice we agreed to, at the meeting is
1) Array.prototype.map produces the same kind of array that it was applied to, so:
for the above example
m instance of V will be true.
intArray.map(v=>v.toSring()) produces an Int32Array. The strings produced by the map function get converted back to numbers.
2) If you want to map the elements of an array to different kind of array use <ArrayClass>.from with a map function as the second parameter:
var strArray = Array.from(intArray, v=>v.toString());
This seemed like a less invasive change then adding additional target kind parameters to Array.prototype.map. Also it seems like a very clear way for programmers to state their intent.
>
> class M implements Mapable { ... }
> m = (new M()).map(val => val);
> console.log( m instanceof M ); // true
>
> (in typed variants of JS, this would call for generics, to separate structure class -supporting map- from element class -being mapped)
ES isn't Java or C#. We don't have formalized interfaces (although it is useful to think and talk about informal interfaces) and since we are dynamically typed we don't need to get sucked into the tar pit of generics.
>
> Instead, the accepted approach -if I understood it correctly-
> focuses on conversion and iterables:
It's not about conversion as much as giving the programmer a way of choosing the kind of array that map generates.
>
> Array.from( iterable ) => Array.from( iterable, mapFn )
>
> such that
>
> SubArray.from( iterable, val => val ) instanceof SubArray
>
> This seems very odd to me, because
>
> - it introduces a second form of .map, in .from
How would use produce an Array of strings from an Int32Array?
>
> - instead of limiting to Array, .from-map is now limited to iterables
> (it would work for Set, which is really OrderedSet, but it wouldn't work for WeakMap)
We already have Array.from that works with iterables, how does adding a map function change anything related to the <ArrayClass>.from result domains
>
> - it doesn't address the general problem: how to inherit structural
> functionality (such as mapping over all elements or a container/
> iterable) while preserving class
See above, Array.prototype.map will preserve the receiver's "class".
>
> With a general solution to the issue, I would expect to write
>
> SubArray.from( iterable ).map( val => val ) instanceof SubArray
yes, the above will produce an instance of SubArray. But the above also has the cost of an extra copy and the map function doesn't get to see the original iterable's values.
>
> while also getting
>
> new Mapable().map( val => val ) instanceof Mapable
I don't even know how to interpret the above, as we don't have a class or constructor named Mapable.
>
> Could someone please elaborate why the committee went with an additional map built into structure conversion instead?
>
> Claus
>
> PS. What about array comprehensions and generator expressions?
What about them? Array comprehensions are a for of Array initializer and always produce an Array instance. Generaltor expressions produce iterators (which are iterable).
Allen
More information about the es-discuss
mailing list