Re: Array subclassing, .map and iterables (Re: Jan 30 TC39 Meeting Notes)
On Feb 14, 2013, at 8:47 AM, Nathan Wall wrote: Hey Allen, thanks for clarifying. What will happen to other Array methods which currently return Arrays? `filter` is the primary one that comes to mind. Will `uint32array.filter((v) = v != 0)` return a Uint32Array? (I think it should behave the same way `map` does.) filter, splice, splice, reverse, etc. all copy elements that from an existing array or array subclass so the result can/should be the same class is the source array. Map is different because it can produce new element values of an arbitrary kind that may not fit into the same kind of array as the source array Additionally, what will happen with the return values of `slice`, `splice`, and `reverse`?.. not to mention `concat`, which I know is a much more complex beast. WRT to concat, see last three slides of http://wiki.ecmascript.org/lib/exe/fetch.php?id=meetings%3Ameeting_jan_29_2013cache=cachemedia=meetings:subclassing_builtins.pdf Nathan ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
RE: Array subclassing, .map and iterables (Re: Jan 30 TC39 Meeting Notes)
Hey Allen, thanks for clarifying. What will happen to other Array methods which currently return Arrays? `filter` is the primary one that comes to mind. Will `uint32array.filter((v) = v != 0)` return a Uint32Array? (I think it should behave the same way `map` does.) Additionally, what will happen with the return values of `slice`, `splice`, and `reverse`?.. not to mention `concat`, which I know is a much more complex beast. Nathan Subject: Re: Array subclassing, .map and iterables (Re: Jan 30 TC39 Meeting Notes) From: al...@wirfs-brock.com Date: Wed, 13 Feb 2013 17:49:27 -0700 CC: claus.rei...@talk21.com; e-t...@ecma-international.org; es-discuss@mozilla.org To: nathan.w...@live.com On Feb 11, 2013, at 7:13 PM, Nathan Wall wrote: Thank you for this explanation. This is very interesting! If you don't mind, I have some questions/concerns I'd like to clear up. (In particular I want to make sure I understand; I don't intend to argue for one side or the other ATM, but this change may require me to refactor some old code to be future-ready). Allen Wirfs-Brock wrote: 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. Sounds cool! Does it work? I rely on the genericness of Array methods quite a lot. For example, it's fairly common for me to write something like the following: var map = Function.prototype.call.bind(Array.prototype.map), select = document.querySelectorAll.bind(document); var ids = map(select('div'), function(el) { return el.id; }); Currently this would get me an array of string ids for every div on a page. In a future ES6 with a future DOM where NodeList extends Array, will `ids` no longer hold an array of strings but try to remain a NodeList? Anything that works with ES5 semantics should continue to work, assuming nothing else changes. If NodeList was turned into an actual subclass of Array, then the default behavior of Array.prototype.map when applied to a NodeList will be to create a new NodeList, assuming that NodeList supports all the mechanism that map uses to create its result object. Of course, the design of this new NodeList has the option of over-riding the inherited definition of map and/or opt-ing out of the create something other than Array instance behavior. Regardless, because of backwards compatibility concerns, I'm skeptical that NodeList could ever be made into an Array subclass. It seems more likely that a new kind of DOM Node collection that was an Array subclass might be introduced There's a good chance this will break some of my code. I'm capable of changing my code and writing this to be more future-friendly from now on (I'm not one who prefers backwards compatibility over a better language). But I would have always assumed I was doing things correctly before, and I'm curious if the rest of the internet will be okay..? We can't break existing code. Where things get messy is when old code is combined with new code that uses new features. For example, you map function will produce a SubArray instance if it is called with a SubArray instance assuming that SubArray is a real ES6 subclass of Array as its first argument. Allen ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Array subclassing, .map and iterables (Re: Jan 30 TC39 Meeting Notes)
On 14 February 2013 02:35, Allen Wirfs-Brock al...@wirfs-brock.com wrote: As we discussed at the meeting, to rationally place Typed Arrays into the same class hierarchy as Array would require refactoring Array.prototype into multiple abstract superclasses. This seems like too large of a change. Instead the plan that we agreed to is to introduce TypedArray as an abstract superclass of all of the specific Typed Array constructors. TypedArray.prototype will be specified to have all of the Array.prototype methods that don't dynamic change the length of an array. (TypedArray.prototype will also define set and subarray from the Khronos Typed Array spec.) I'm still not convinced that introducing a separate class only for masking a handful of length-modifying array methods is worthwhile. After all, ordinary arrays can be sealed or frozen, too, while still providing those methods, so typed arrays are not new or different in that regard. I'd say that either we properly clean up the Array hierarchy, or we leave it alone. A half-baked solution that only applies to typed arrays, and divorces them from the Array hierarchy, seems less attractive than just doing the naive thing, i.e., TypedArray Array. /Andreas ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Array subclassing, .map and iterables (Re: Jan 30 TC39 Meeting Notes)
Nathan Wall wrote: Hey Allen, thanks for clarifying. What will happen to other Array methods which currently return Arrays? `filter` is the primary one that comes to mind. Will `uint32array.filter((v) = v != 0)` return a Uint32Array? (I think it should behave the same way `map` does.) I know I already said that, but I repeat: `map` transforms elements, `filter` just selects a subset. Therefore I am 100% for `filter` doing the same kind of collection as the receiver. Bit not necessarily for `map`. So I think, not, `filter` should not behave same as `map`. Additionally, what will happen with the return values of `slice`, `splice`, and `reverse`?.. not to mention `concat`, which I know is a much more complex beast. I think `filter` should behave the same as `slice`, `splice`, `reverse` and `concat`. They have clear rationale to use same kind of container. Nathan Herby ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Array subclassing, .map and iterables (Re: Jan 30 TC39 Meeting Notes)
Andreas Rossberg wrote: I'd say that either we properly clean up the Array hierarchy, or we leave it alone. A half-baked solution that only applies to typed arrays, and divorces them from the Array hierarchy, seems less attractive than just doing the naive thing, i.e., TypedArray Array. Agree with that, and I'll go further: we should leave alone what's already shipped and in use for a long time. TypedArray Array sounds good to me. /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Array subclassing, .map and iterables (Re: Jan 30 TC39 Meeting Notes)
On Feb 11, 2013, at 7:13 PM, Nathan Wall wrote: Thank you for this explanation. This is very interesting! If you don't mind, I have some questions/concerns I'd like to clear up. (In particular I want to make sure I understand; I don't intend to argue for one side or the other ATM, but this change may require me to refactor some old code to be future-ready). Allen Wirfs-Brock wrote: 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. Sounds cool! Does it work? I rely on the genericness of Array methods quite a lot. For example, it's fairly common for me to write something like the following: var map = Function.prototype.call.bind(Array.prototype.map), select = document.querySelectorAll.bind(document); var ids = map(select('div'), function(el) { return el.id; }); Currently this would get me an array of string ids for every div on a page. In a future ES6 with a future DOM where NodeList extends Array, will `ids` no longer hold an array of strings but try to remain a NodeList? Anything that works with ES5 semantics should continue to work, assuming nothing else changes. If NodeList was turned into an actual subclass of Array, then the default behavior of Array.prototype.map when applied to a NodeList will be to create a new NodeList, assuming that NodeList supports all the mechanism that map uses to create its result object. Of course, the design of this new NodeList has the option of over-riding the inherited definition of map and/or opt-ing out of the create something other than Array instance behavior. Regardless, because of backwards compatibility concerns, I'm skeptical that NodeList could ever be made into an Array subclass. It seems more likely that a new kind of DOM Node collection that was an Array subclass might be introduced There's a good chance this will break some of my code. I'm capable of changing my code and writing this to be more future-friendly from now on (I'm not one who prefers backwards compatibility over a better language). But I would have always assumed I was doing things correctly before, and I'm curious if the rest of the internet will be okay..? We can't break existing code. Where things get messy is when old code is combined with new code that uses new features. For example, you map function will produce a SubArray instance if it is called with a SubArray instance assuming that SubArray is a real ES6 subclass of Array as its first argument. Allen ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Array subclassing, .map and iterables (Re: Jan 30 TC39 Meeting Notes)
On Feb 12, 2013, at 4:54 AM, Aymeric Vitte wrote: Le 10/02/2013 19:05, Allen Wirfs-Brock a écrit : All the presentation decks from the meeting are publicly available at http://wiki.ecmascript.org/doku.php?id=meetings:meeting_jan_29_2013 the short form: In ES5, today you can use Array.prototype as the [[Prototype]] of non-Array objects or apply the Array.prototype methods to non-arrays. The behaviors they get are non-necessarily what you would expect if you would really subclass Array. We need to preserve the existing behaviors for these existing use cases but would like to provide the more reasonable behavior when class extends Array is used to create a subclass. Array.prototype.concat is the most problematic of the existing methods in this regard. After reading this thread and the slides, what is the plan for Typed Arrays (dedicated array like methods or Array subclassed like) ? As we discussed at the meeting, to rationally place Typed Arrays into the same class hierarchy as Array would require refactoring Array.prototype into multiple abstract superclasses. This seems like too large of a change. Instead the plan that we agreed to is to introduce TypedArray as an abstract superclass of all of the specific Typed Array constructors. TypedArray.prototype will be specified to have all of the Array.prototype methods that don't dynamic change the length of an array. (TypedArray.prototype will also define set and subarray from the Khronos Typed Array spec.) How I hope to specify TypedArray.prototype is to say that any of its properties that are the same as the corresponding Array.prototype property may, at the discretion of the implementation, be implemented using the same function objects as Array.prototype or by using a different function that conforms to the same specification. This would give an implementation an option of using either the same Array.prototype methods or of providing versions of the methods that are optimized for use with Typed Arrays. It's possible that this may not fly. Such a choice of implementation approach allows more freedom then we normally give implementations and the choice would be detectible by user code doing something like: Array.prototype.map === TypedArray.prototype.map If we find we can't allow that flexibility my next choice is to to say that both Array.prototype and TypedArray.prototype share in common those function objects and leave it to implementations to hide any Type Array specific optimizations inside of the functions. The reasons this is my preference, over simply saying they are all different function objects that share a common specification, is that I'm generally trying to minimize the number of new built-in functions that need to be created every-time a Realm (eg, iframe) is created. I see the constraints for subclassing, but the solution a.from(b,map) seems a little bit strange (even if I agree that it should return the same instance as a) , and what about concat, slice, etc as you mention? Couldn't we add something like a parameter to the methods : example.map(f,thisArg, true) or example.map(f,true) returns example's instance instead of Array (same processing as .of, .from), so you can explicitely say that you want to return the same instance and don't have backward compatilities issues (hopefully...) ? note it is A.from(b,mapfn), eg; Array.from(b, mapfn) or Uint32Array.from(b,mapfn). A boolean valued parameter isn't sufficient because you need the flexibility to identify the specific kind of array you want to map into. Either of the above read perfectly fine to me: I want to create a new Array (or Uint32Array) from the elements of b transformed by mapfn. All the essential pieces of information are there and the conceptual model seems just fine. I have used a lot Typed Arrays for [1] [2] [3] and all along wondering why Array like optimized methods were not available, especially concat, and why it was speced entirely in TC39 while a spec already exists (which specifies slice/subarray but surprisingly not concat), but it will be extremely usefull to have the Array like methods in the ES specs for Typed Arrays. The intent of TC39 adopting Typed Array is to allow us to fully integrate it into the language, rather than it being a add on that doesn't follow the normal ES conventions. Make the (applicable) Array.prototype methods available is part of that integration. Allen Regards, [1] https://www.github.com/Ayms/node-Tor [2] https://www.github.com/Ayms/iAnonym [3] https://www.github.com/Ayms/node-typedarray -- jCore Email : avi...@jcore.fr iAnonym : http://www.ianonym.com node-Tor : https://www.github.com/Ayms/node-Tor GitHub : https://www.github.com/Ayms Web :www.jcore.fr Webble : www.webble.it Extract Widget Mobile : www.extractwidget.com BlimpMe! : www.blimpme.com
Re: Array subclassing, .map and iterables (Re: Jan 30 TC39 Meeting Notes)
Rick Waldron wrote: On Monday, February 11, 2013, Nathan Wall wrote: Thank you for this explanation. This is very interesting! If you don't mind, I have some questions/concerns I'd like to clear up. (In particular I want to make sure I understand; I don't intend to argue for one side or the other ATM, but this change may require me to refactor some old code to be future-ready). Allen Wirfs-Brock wrote: 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. Sounds cool! Does it work? I rely on the genericness of Array methods quite a lot. For example, it's fairly common for me to write something like the following: var map = Function.prototype.call.bind(Array.prototype.map), select = document.querySelectorAll.bind(document); var ids = map(select('div'), function(el) { return el.id http://el.id; }); Currently this would get me an array of string ids for every div on a page. In a future ES6 with a future DOM where NodeList extends Array, will `ids` no longer hold an array of strings but try to remain a NodeList? There's a good chance this will break some of my code. I'm capable of changing my code and writing this to be more future-friendly from now on (I'm not one who prefers backwards compatibility over a better language). But I would have always assumed I was doing things correctly before, and I'm curious if the rest of the internet will be okay..? Since you're using Array.prototype.map there, your code will indeed give you an array of string IDs, as it always did. No it won't. In the current proposal (which I'd like to change, I think Array is better default container for map), if NodeList would subclass Array, applying (generic) Array.prototype.map on instance of NodeList would create a new NodeList and would try to put ids on there. Herby Rick Nathan ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Array subclassing, .map and iterables (Re: Jan 30 TC39 Meeting Notes)
Le 10/02/2013 19:05, Allen Wirfs-Brock a écrit : All the presentation decks from the meeting are publicly available at http://wiki.ecmascript.org/doku.php?id=meetings:meeting_jan_29_2013 the short form: In ES5, today you can use Array.prototype as the [[Prototype]] of non-Array objects or apply the Array.prototype methods to non-arrays. The behaviors they get are non-necessarily what you would expect if you would really subclass Array. We need to preserve the existing behaviors for these existing use cases but would like to provide the more reasonable behavior when class extends Array is used to create a subclass. Array.prototype.concat is the most problematic of the existing methods in this regard. After reading this thread and the slides, what is the plan for Typed Arrays (dedicated array like methods or Array subclassed like) ? I see the constraints for subclassing, but the solution a.from(b,map) seems a little bit strange (even if I agree that it should return the same instance as a) , and what about concat, slice, etc as you mention? Couldn't we add something like a parameter to the methods : example.map(f,thisArg, true) or example.map(f,true) returns example's instance instead of Array (same processing as .of, .from), so you can explicitely say that you want to return the same instance and don't have backward compatilities issues (hopefully...) ? I have used a lot Typed Arrays for [1] [2] [3] and all along wondering why Array like optimized methods were not available, especially concat, and why it was speced entirely in TC39 while a spec already exists (which specifies slice/subarray but surprisingly not concat), but it will be extremely usefull to have the Array like methods in the ES specs for Typed Arrays. Regards, [1] https://www.github.com/Ayms/node-Tor [2] https://www.github.com/Ayms/iAnonym [3] https://www.github.com/Ayms/node-typedarray -- jCore Email : avi...@jcore.fr iAnonym : http://www.ianonym.com node-Tor : https://www.github.com/Ayms/node-Tor GitHub : https://www.github.com/Ayms Web :www.jcore.fr Webble : www.webble.it Extract Widget Mobile : www.extractwidget.com BlimpMe! : www.blimpme.com ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Array subclassing, .map and iterables (Re: Jan 30 TC39 Meeting Notes)
[to limit the length of my reply, I had to avoid responding to every detail, trying to answer the gist of your message instead; please let me know if I missed anything important] Of course, you might argue that I could just call it like: NodeList.from( [ div, span, p ].map(nodeName = document.createElement(nodeName)) ); Indeed, this would be my preferred choice. It would be more modular than packaging the combination of container change and element conversion into a single operation. However, I understand the conflict between type-changing element maps and element-type-constrained containers. Let me change the example to bring out this point: if we convert from an array of Int32 to an array of Double, we cannot map on the source array nor can we map on the target array. So we do have to map the elements in transit, after extracting them from the Int32 source and before placing them in the Double target, preferably without creating an intermediate Array. Since the container change (.from()) is specced via iterators, I suggest to ensure support for .map() on iterators and map the elements in the iteration, after extraction from source, before integration into target. Integrating the element map into the container conversion (.from()) instead, which is a static method to enforce target-type specification, solves the issue you were trying to address, for Array subclasses, but it leaves us with a host of residual issues: - there are now two operations for one generic task, Array.prototype.map( function )// type-preserving TargetClass.from( source, function ) // type-changing - the two -clearly related- operations do not have a common interface, in fact, one is an object method, the other a static/class method - the latter operation is really a family of operations, and the static type prefixes of the family members are difficult to abstract over (do I try to get the target type from the target object context or from the function result, or do I force the user to pass it in at every call site) - even with this additional complexity, we still do not have support for mapping over the elements of other, non-Array containers I suspect that the problem of establishing target container types is separate from the element mapping, so I would like to keep .from() and .map() separate. But even in the merged design programming will be awkward. ...But the arraylike or iterable might not have a .map() method of its own, which will cause issues if I'm in a JS-target transpilation scenario... And I would like not only to root out any arraylike or iterable that do not support .map(), but would also like to extend the reach of .map() to other cases where it makes sense (I've listed examples in previous messages). (function( root ) { root.converter = function( ctor, iterable, map ) { return this[ ctor ].from( iterable, map ); }.bind(root); }( this )); What you're saying here is that (1) .from() should support .map()-like functionality for all iterables (even if they do not support .map()), that (2) we can't use .map() because it may not be supported for all iterables and the 'map' parameter might be type-changing, and that (3) you don't know how to get the target type generically, so it'll have to be passed in at each call site. None of this is promising when I think of writing generic code that employs mapping over different container types, even if we assume that the mapping .from() replaces .map() as the general interface. Are you going to pass around 'ctor's as dynamic type hints? Since we need the target classes, we can't even extract the class from the source arrays. This, and the inability to de-structure things like Int32Array into its components, are among the outstanding language design issues generated in this area. My point was that map is far more widely useful, not limited to Array (Array.prototype.map), and not limited to Array construction from Iterables (Array.prototype.from with second parameter). Consider map on event emitters, on promises, on exceptions, on generators, .. I don't have an alternative solution that would cover all use cases in ES uniformly, because the existing solutions in other languages do not translate directly. However, I wanted to ring a warning bell that adding a different partial solution for every new use case is not going to scale well (especially with things being so difficult to change once they are in ES), and misses potential for writing generic library code. Can you show an example of this? Example of what (can't resolve 'this';-)? I listed several examples of classes that I'd like to see map() support on. You gave an example of how you couldn't write generic code using map() because not all relevant classes support that method (using .from() on iterables doesn't work, either). If you mean difficulties of evolving ES designs after release, think no further than existing code
Re: Array subclassing, .map and iterables (Re: Jan 30 TC39 Meeting Notes)
Allen Wirfs-Brock wrote: On Feb 10, 2013, at 3:40 AM, Herby Vojčík wrote: But I still think it is simpler to specify .map returning array, always. You know you get an Array, you don't have to worry about magic of Set, Map etc., if you wish to have it in other kind of container, use Container.from(...). But wouldn't you want: var my16bitVector = Uint16Array.from([1,2,3,4,5,6]); ... var y = my16bitVector.map(v=v+1) someExternalAPI(y); to create a Uint16Array for y? It seems like the most common use cases want to produce the same kind of elements. Mapping to a different or broader kind of element is also common, but I think less common. So it's the case that should take to more to express. Well, here's the difference. Nearly every kind of .map use I remember (or `collect:` in Smalltalk, maybe I should not mix them but they are mixed in my mind) are transformation of the object: map objects to only some specific property, wrap collection of primitives to objects that are created from them, etc. I honestly can't remember any use of .map concept that preserves element types beyond educatory `(1 to: 10) collect: [ :x | x*x ]`. That is, my conviction is, .map is mainly used for bigger transformations of its elements (I think your v=v+1 example much more often happens in-place, that is, forEach; map use-case seems rare for me). Allen Herby ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Array subclassing, .map and iterables (Re: Jan 30 TC39 Meeting Notes)
On Feb 10, 2013, at 1:04 PM, Tab Atkins Jr. wrote: From what I've seen in the examples in this topic so far, it looks like Array#from takes a second optional argument, which is a function that you map the iterable elements through first, before giving them to the constructor. So, you would write: NodeList.from( [div, span, p], (nodeName)=document.createElement(nodeName) ); Exactly. Of course, in defining class NodeList extends Arrar {...} you could decide to over-ride the inherited definition of .from with a version that implicitly did the above coercion on string valued elements. It up to the designer of the class interface. Allen ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
RE: Array subclassing, .map and iterables (Re: Jan 30 TC39 Meeting Notes)
Thank you for this explanation. This is very interesting! If you don't mind, I have some questions/concerns I'd like to clear up. (In particular I want to make sure I understand; I don't intend to argue for one side or the other ATM, but this change may require me to refactor some old code to be future-ready). Allen Wirfs-Brock wrote: 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. Sounds cool! Does it work? I rely on the genericness of Array methods quite a lot. For example, it's fairly common for me to write something like the following: var map = Function.prototype.call.bind(Array.prototype.map), select = document.querySelectorAll.bind(document); var ids = map(select('div'), function(el) { return el.id; }); Currently this would get me an array of string ids for every div on a page. In a future ES6 with a future DOM where NodeList extends Array, will `ids` no longer hold an array of strings but try to remain a NodeList? There's a good chance this will break some of my code. I'm capable of changing my code and writing this to be more future-friendly from now on (I'm not one who prefers backwards compatibility over a better language). But I would have always assumed I was doing things correctly before, and I'm curious if the rest of the internet will be okay..? Nathan ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Array subclassing, .map and iterables (Re: Jan 30 TC39 Meeting Notes)
On Monday, February 11, 2013, Nathan Wall wrote: Thank you for this explanation. This is very interesting! If you don't mind, I have some questions/concerns I'd like to clear up. (In particular I want to make sure I understand; I don't intend to argue for one side or the other ATM, but this change may require me to refactor some old code to be future-ready). Allen Wirfs-Brock wrote: 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. Sounds cool! Does it work? I rely on the genericness of Array methods quite a lot. For example, it's fairly common for me to write something like the following: var map = Function.prototype.call.bind(Array.prototype.map), select = document.querySelectorAll.bind(document); var ids = map(select('div'), function(el) { return el.id; }); Currently this would get me an array of string ids for every div on a page. In a future ES6 with a future DOM where NodeList extends Array, will `ids` no longer hold an array of strings but try to remain a NodeList? There's a good chance this will break some of my code. I'm capable of changing my code and writing this to be more future-friendly from now on (I'm not one who prefers backwards compatibility over a better language). But I would have always assumed I was doing things correctly before, and I'm curious if the rest of the internet will be okay..? Since you're using Array.prototype.map there, your code will indeed give you an array of string IDs, as it always did. Rick Nathan ___ es-discuss mailing list es-discuss@mozilla.org javascript:; https://mail.mozilla.org/listinfo/es-discuss ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Array subclassing, .map and iterables (Re: Jan 30 TC39 Meeting Notes)
Thanks for the explanations and additional details. Let me first try to rephrase, to see whether I've understood your reasoning: The problem comes from the partial integration of types in ES, specifically having typed arrays but no easy way to express and control the types of the functions mapped over them. And your solution is to fix Array.map to being type-preserving, and to use an auxiliary map in Container.from instead of Array.map when type changing mappings have to be expressed. Using for type parameters, = for function types, and suppressing a few details (such as prototype, this, index parameter), we can write the types of the two groups of operations as ArrayElem.map : (Elem = Elem) = ArrayElem ContainerElem2.from : IterableElem1 = (Elem1 = Elem2) = ContainerElem2 where the ES5 Array is seen as ArrayAny (so arbitrary mappings are still supported at that level), and ArrayInt32, etc are written as type-level constants Int32Array, for lack of type-level constructor syntax (the parameterized Interface Iterable is also inexpressible). Since ES cannot guarantee that the mappings have the expected types, an implicit conversion of the mapped elements to the expected element type will be enforced (probably with a type check to avoid unexpected conversions?). So int32Array.map( f ) will be read as roughly int32Array.map( (elem) = Number( f(elem) ) ) and Int32Array.from( iterable, f ) as Int32Array.from( iterable, (elem) = Number( f(elem) ) ) Do I have this right, so far? 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. It is this additional mechanism which I'm after. In typed languages, it is long-established practice to put the additional parameters at the type level and to hang the interface on the type-level constructor, and I wonder how much of that could be translated for use in ES. For instance, being able to specify an overloaded map function was the motivating example for introducing type constructor classes in Haskell A system of constructor classes: overloading and implicit higher-order polymorphism Mark P. Jones, In FPCA '93: Conference on Functional Programming Languages and Computer Architecture, Copenhagen, Denmark, June 1993. http://web.cecs.pdx.edu/~mpj/pubs/fpca93.html 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. Given the history of implicit conversions in ES, have you considered just doing runtime type checks, without those new implicit conversions? 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. 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. If a programming concept is as useful as interfaces are, it usually pays to think about language support for it. And I was certainly not thinking of Java or C#, more of TypeScript, where the team seems to be working on JavaScript-suited generics for the next version, to be able to type current JavaScript library code. Btw, parametric polymorphism in ML and its refinements and extensions in Haskell were elegant and concise tools before they got watered down in a multi-year process to fit into Java. If you have bad experience with generics, they probably come from Java's adaption. How would use produce an Array of strings from an Int32Array? Somewhat like Array.from( int32Array ).map( (elem) = elem.toString() ) Implementations would be free to replace the syntactic pattern with an optimized single pass (in more conventional optimizing language implementations, such fusion of implicit or explicit loops is standard, but even ES JIT engines -with their limited time for optimization- should be able to spot the syntactic pattern). - instead of limiting to Array, .from-map is now limited to iterables (it would work for Set, which is really
Re: Array subclassing, .map and iterables (Re: Jan 30 TC39 Meeting Notes)
Allen Wirfs-Brock wrote: On Feb 9, 2013, at 3:01 PM, Herby Vojčík wrote: Allen Wirfs-Brock wrote: ethods. 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 useArrayClass.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. You chose one default, but I think it was not the simplest one. It is good to see that map is often transforming types, so the same type may not be the best default (filter is another story). I think the best would be (it is dead simple): - to _always_ use Array in map result In your previous post you said: 1. .map should work for Array subclasses, preserving class are you changing that position? That's probably not me who said it :-) Also, there is another subtlety that is on slide 25 of the deck I present. Existing ES6 code may create objects that inherit from Array.prototype. When running on an ES6 implementation uses of Array.prototype.map (etc.) in such code can't change their behavior. I am not TC39 member, so I did not see that slide, but if you say .map always produces Array, it does not change the behaviour. Could you elaborate more, please? So, the selection of the result class can't be based solely on the [[Prototype]] inheritance chain. - to leave Array.from (as well as Map.from, V.from etc.) as is, generator comprehension does the mapping for you if you wish one. So, the examples would be V.from(x for x in new V); // you say you want the results in V I don't understand? This produces the same result as new V; but with an extra V allocation, creation of a generator, etc. It was rewrite of Claus' example you were discussing about: class V extends Array { ... } m = (new V()).map(val = val); console.log( m instanceof V ); // false Another issue, is that some array-like classes instances must have their size fixed at allocation. This is the case for all the TypedArrays. For iterators automatically derived from most arrays, we can make the size information available. But for a generator, there is no way to know how many iterations it will make without actually running it. For specification purposes, we may specify the Hm. Yes, this is problem, then. from method as accumulating the element values into a List, allocatininge most TypedArray uses) that the double copy can be optimized away. That means that in the usual case the size must be available at the beginning which precludes using a generator expression as the usual case pattern. Yes, then the second point is not-starter. But I still think it is simpler to specify .map returning array, always. You know you get an Array, you don't have to worry about magic of Set, Map etc., if you wish to have it in other kind of container, use Container.from(...). Of course, I don't know what that slide 25 was about, but for now I argue map is different from concat, filter etc. do not transform contents, just manipulate containers. Allen Herby ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Array subclassing, .map and iterables (Re: Jan 30 TC39 Meeting Notes)
On Sun, Feb 10, 2013 at 4:33 AM, Claus Reinke claus.rei...@talk21.comwrote: (snip) How would use produce an Array of strings from an Int32Array? Somewhat like Array.from( int32Array ).map( (elem) = elem.toString() ) Implementations would be free to replace the syntactic pattern with an optimized single pass (in more conventional optimizing language implementations, such fusion of implicit or explicit loops is standard, but even ES JIT engines -with their limited time for optimization- should be able to spot the syntactic pattern). (Assuming a future where the DOM's NodeList inherits from Array) How would you produce a NodeList from an arbitrary array of strings? NodeList.from( [ div, span, p ], nodeName = document.createElement(nodeName) ); Because... NodeList.from( strings ); Would try to make a NodeList, but with items that of a type that it disallows, meaning: NodeList.from( strings ).map( nodeName = document.createElement(nodeName) ); Would call .map() on an empty NodeList, since the string value items had been rejected. Of course, you might argue that I could just call it like: NodeList.from( [ div, span, p ].map(nodeName = document.createElement(nodeName)) ); ...But the arraylike or iterable might not have a .map() method of its own, which will cause issues if I'm in a JS-target transpilation scenario... (function( root ) { root.converter = function( ctor, iterable, map ) { return this[ ctor ].from( iterable, map ); }.bind(root); }( this )); (just assume that |this| is the global object ;) ) - instead of limiting to Array, .from-map is now limited to iterables Where do you see this documented? Array.from (and any subclass) accepts both array-likes and iterables. (it would work for Set, which is really OrderedSet, but it wouldn't work for WeakMap) This discussion is irrelevant to 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 My point was that map is far more widely useful, not limited to Array (Array.prototype.map), and not limited to Array construction from Iterables (Array.prototype.from with second parameter). Consider map on event emitters, on promises, on exceptions, on generators, .. I don't have an alternative solution that would cover all use cases in ES uniformly, because the existing solutions in other languages do not translate directly. However, I wanted to ring a warning bell that adding a different partial solution for every new use case is not going to scale well (especially with things being so difficult to change once they are in ES), and misses potential for writing generic library code. Can you show an example of this? If I have to write different code, depending on whether I need to map over a constructed Array, an Array under construction, an Array or an Int32Array, a generator, a promise, etc., then that will result in duplicate code instead of generic code. 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. Implementations can fuse .from().map() as well as .map().from(); .from() is a static method, so .map().from() won't work. if you want access to the unconverted elements, you want to map over the iterable, not the resulting Array (again, with loop fusion in the implementation): SubArray.from( iterable.map( val = f(val) ) ) The from-map-function does map over the iterable, not the resulting Array (or SubArray, or NodeList or whatever) (See NodeList example above) Of course, since map isn't part of a standard Array-independent interface, I have to write that as a generator expression (not sure whether I'm up to date on their syntax) instead of a map. SubArray.from( (for ( elem of iterable ) in f(elem) ) ) So... you'd get a SubArray whose elements where the result of `(for ( elem of iterable ) in f(elem) )`, but that's the same as: SubArray.from( iterable, f ); ...Which is much nicer to read. Rick ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Array subclassing, .map and iterables (Re: Jan 30 TC39 Meeting Notes)
On Feb 10, 2013, at 1:33 AM, Claus Reinke wrote: Thanks for the explanations and additional details. Let me first try to rephrase, to see whether I've understood your reasoning: The problem comes from the partial integration of types in ES, specifically having typed arrays but no easy way to express and control the types of the functions mapped over them. ECMAScript has various kinds of values and objects that are distinguished in various ways. However, these variations of kind are quite unlike the concept of type as used in PL theory and statically typed languages. For that reason I generally try to avoid use of the word type in the context of ES (although sometimes it is unavoidable, eg TypedArrays) because it can lead to thinking that reflects static typing experience rather than dynamic language experiences that are often more appropriate for ES. And your solution is to fix Array.map to being type-preserving, and to use an auxiliary map in Container.from instead of Array.map when type changing mappings have to be expressed. Using for type parameters, = for function types, and suppressing a few details (such as prototype, this, index parameter), we can write the types of the two groups of operations as ArrayElem.map :(Elem = Elem) =ArrayElem ContainerElem2.from :IterableElem1 =(Elem1 = Elem2) =ContainerElem2 where the ES5 Array is seen as ArrayAny (so arbitrary mappings are still supported at that level), and ArrayInt32, etc are written as type-level constants Int32Array, for lack of type-level constructor syntax (the parameterized Interface Iterable is also inexpressible). Like I said above. This is a type theory view of the word. Static type systems require some sort parameterization or genercity in order to be sufficiently expressive. Few programmers actually understand the subtleties of the type systems. Dynamic languages typically don't use such type systems and achieve equivalent (arguably greater) expressiveness using different approaches. We don't want to turn ES into Java or C#. Since ES cannot guarantee that the mappings have the expected types, an implicit conversion of the mapped elements to the expected element type will be enforced (probably with a type check to avoid unexpected conversions?). Right, dynamic languages typically use dynamic constraint checks or dynamic coercions when specific kinds of objects are required. So int32Array.map( f ) will be read as roughly int32Array.map( (elem) = Number( f(elem) ) ) and Int32Array.from( iterable, f ) as Int32Array.from( iterable, (elem) = Number( f(elem) ) ) Do I have this right, so far? yes, except that the Number coercion takes place at a much deeper layer -- If is part of the [[Put]] operation that actually stores values into the Int32Array 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. It is this additional mechanism which I'm after. In typed languages, it is long-established practice to put the additional parameters at the type level and to hang the interface on the type-level constructor, and I wonder how much of that could be translated for use in ES. There is also plenty of dynamic language experience for the pattern I proposed. For example, withAll: is pretty much Smalltalk's equivalent of ES for. The conceptual model of this is not that the for method is being parameterized by the programmer. Instead, the model is that the programmer has chosen a specific kind of collection object that will be populated using for and it is the responsibility of that object to impose what ever constraints upon its elements as are appropriate for it. For instance, being able to specify an overloaded map function was the motivating example for introducing type constructor classes in Haskell A system of constructor classes: overloading and implicithigher-order polymorphism Mark P. Jones,In FPCA '93: Conference on Functional Programming Languagesand Computer Architecture, Copenhagen, Denmark, June 1993. http://web.cecs.pdx.edu/~mpj/pubs/fpca93.html Right, but this is all starting rom a static typing perspective rather than dynamic typing. 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
Re: Array subclassing, .map and iterables (Re: Jan 30 TC39 Meeting Notes)
On Feb 10, 2013, at 3:40 AM, Herby Vojčík wrote: Allen Wirfs-Brock wrote: On Feb 9, 2013, at 3:01 PM, Herby Vojčík wrote: ... I think the best would be (it is dead simple): - to _always_ use Array in map result In your previous post you said: 1. .map should work for Array subclasses, preserving class are you changing that position? That's probably not me who said it :-) oops, sorry for the mis-attribution Also, there is another subtlety that is on slide 25 of the deck I present. Existing ES6 code may create objects that inherit from Array.prototype. When running on an ES6 implementation uses of Array.prototype.map (etc.) in such code can't change their behavior. I am not TC39 member, so I did not see that slide, but if you say .map always produces Array, it does not change the behaviour. Could you elaborate more, please? All the presentation decks from the meeting are publicly available at http://wiki.ecmascript.org/doku.php?id=meetings:meeting_jan_29_2013 the short form: In ES5, today you can use Array.prototype as the [[Prototype]] of non-Array objects or apply the Array.prototype methods to non-arrays. The behaviors they get are non-necessarily what you would expect if you would really subclass Array. We need to preserve the existing behaviors for these existing use cases but would like to provide the more reasonable behavior when class extends Array is used to create a subclass. Array.prototype.concat is the most problematic of the existing methods in this regard. ... Another issue, is that some array-like classes instances must have their size fixed at allocation. This is the case for all the TypedArrays. For iterators automatically derived from most arrays, we can make the size information available. But for a generator, there is no way to know how many iterations it will make without actually running it. For specification purposes, we may specify the Hm. Yes, this is problem, then. from method as accumulating the element values into a List, allocatininge most TypedArray uses) that the double copy can be optimized away. That means that in the usual case the size must be available at the beginning which precludes using a generator expression as the usual case pattern. Yes, then the second point is not-starter. But I still think it is simpler to specify .map returning array, always. You know you get an Array, you don't have to worry about magic of Set, Map etc., if you wish to have it in other kind of container, use Container.from(...). But wouldn't you want: var my16bitVector = Uint16Array.from([1,2,3,4,5,6]); ... var y = my16bitVector.map(v=v+1) someExternalAPI(y); to create a Uint16Array for y? It seems like the most common use cases want to produce the same kind of elements. Mapping to a different or broader kind of element is also common, but I think less common. So it's the case that should take to more to express. Allen ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Array subclassing, .map and iterables (Re: Jan 30 TC39 Meeting Notes)
On Sun, Feb 10, 2013 at 3:04 PM, Tab Atkins Jr. jackalm...@gmail.comwrote: On Sun, Feb 10, 2013 at 11:18 AM, Rick Waldron waldron.r...@gmail.com wrote: (Assuming a future where the DOM's NodeList inherits from Array) How would you produce a NodeList from an arbitrary array of strings? NodeList.from( [ div, span, p ], nodeName = document.createElement(nodeName) ); Because... NodeList.from( strings ); Would try to make a NodeList, but with items that of a type that it disallows, meaning: NodeList.from( strings ).map( nodeName = document.createElement(nodeName) ); Would call .map() on an empty NodeList, since the string value items had been rejected. Of course, you might argue that I could just call it like: NodeList.from( [ div, span, p ].map(nodeName = document.createElement(nodeName)) ); ...But the arraylike or iterable might not have a .map() method of its own, which will cause issues if I'm in a JS-target transpilation scenario... From what I've seen in the examples in this topic so far, it looks like Array#from takes a second optional argument, which is a function that you map the iterable elements through first, before giving them to the constructor. Yes, and I said just as much (not same words) further down the thread. So, you would write: NodeList.from( [div, span, p], (nodeName)=document.createElement(nodeName) ); This is exactly the same as the very first solution I offered: (Assuming a future where the DOM's NodeList inherits from Array) How would you produce a NodeList from an arbitrary array of strings? NodeList.from( [ div, span, p ], nodeName = document.createElement(nodeName) ); Rick ~TJ ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Array subclassing, .map and iterables (Re: Jan 30 TC39 Meeting Notes)
One of the problems with map is that the return type of the function might be different. With some pseudo syntax for types function f(x : T) : V { ... } var a : Array.T = ... var b = a.map(f); b is actually of type Array.V and not of Array.T This becomes an issue with typed arrays at least. On Sat, Feb 9, 2013 at 5:16 AM, Claus Reinke claus.rei...@talk21.com 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 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, toseparate structure class -supporting map- from elementclass -being mapped) Instead, the accepted approach -if I understood it correctly- focuses on conversion and iterables: 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 - 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) - 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 With a general solution to the issue, I would expect to write SubArray.from( iterable ).map( val = val ) instanceof SubArray while also getting new Mapable().map( val = val ) instanceof 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? ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss -- erik ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Array subclassing, .map and iterables (Re: Jan 30 TC39 Meeting Notes)
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, toseparate structure class -supporting map- from elementclass -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'twork 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 ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Array subclassing, .map and iterables (Re: Jan 30 TC39 Meeting Notes)
Allen Wirfs-Brock wrote: 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 useArrayClass.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. You chose one default, but I think it was not the simplest one. It is good to see that map is often transforming types, so the same type may not be the best default (filter is another story). I think the best would be (it is dead simple): - to _always_ use Array in map result - to leave Array.from (as well as Map.from, V.from etc.) as is, generator comprehension does the mapping for you if you wish one. So, the examples would be V.from(x for x in new V); // you say you want the results in V intArray.map(v=v.toString()); // collect them in default Array Herby Allen ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Array subclassing, .map and iterables (Re: Jan 30 TC39 Meeting Notes)
On Feb 9, 2013, at 3:01 PM, Herby Vojčík wrote: Allen Wirfs-Brock wrote: ethods. 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 useArrayClass.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. You chose one default, but I think it was not the simplest one. It is good to see that map is often transforming types, so the same type may not be the best default (filter is another story). I think the best would be (it is dead simple): - to _always_ use Array in map result In your previous post you said: 1. .map should work for Array subclasses, preserving class are you changing that position? Also, there is another subtlety that is on slide 25 of the deck I present. Existing ES6 code may create objects that inherit from Array.prototype. When running on an ES6 implementation uses of Array.prototype.map (etc.) in such code can't change their behavior. So, the selection of the result class can't be based solely on the [[Prototype]] inheritance chain. - to leave Array.from (as well as Map.from, V.from etc.) as is, generator comprehension does the mapping for you if you wish one. So, the examples would be V.from(x for x in new V); // you say you want the results in V I don't understand? This produces the same result as new V; but with an extra V allocation, creation of a generator, etc. Another issue, is that some array-like classes instances must have their size fixed at allocation. This is the case for all the TypedArrays. For iterators automatically derived from most arrays, we can make the size information available. But for a generator, there is no way to know how many iterations it will make without actually running it. For specification purposes, we may specify the from method as accumulating the element values into a List, allocatininge most TypedArray uses) that the double copy can be optimized away. That means that in the usual case the size must be available at the beginning which precludes using a generator expression as the usual case pattern. Allen ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss