Discussion:
Map: filter/map and more
Dmitry Soshnikov
2014-10-01 18:50:39 UTC
Permalink
Hi,

(Maps are awesome!)

1. Transforming iteration methods

We're currently polyfillying the `Map`, and got some questions form devs.
One of them is about transforming iteration methods, like `map` and
`filter`.

Unfortunately I missed that part of the spec when it was approved, so can
someone please remind/clarify -- was it an intended decision not to hap
Map#map, Map#filter? I can see only Map#forEach in the spec. Are maps
"immutable"? -- That's fine, the `map` and `filter` return a new map.

2. Declarative syntax

The other thing to note: currently maps a lack of a nice declarative
syntax. This one came from the use-case for maps for dynamic (computed)
property names.

Previously, we had to allocate an empty object, and then, in the imperative
style, append needed props:

```
var requestData = {};
requestData[Names.ID] = id;
requestData[Names.EMAIL] = email;
requestData.needsReload = true;
...
new Request(...)
.setData(requestData)
.send();
```

With computed properties of object initialisers it's much simpler and
convenient:

```
new Request(...)
.setData({
[Names.ID]: id,
[Names.EMAIL]: email,
needsReload: true,
})
.send();
```

Then thing is: if we'd like to use maps for such use-case, it brings us
back to that inconvenient imperative style of assignments (even worse,
since you have to repeat that `.set(...)` constantly):

```
var requestData = new Map();
requestData.set(Names.ID, id);
requestData.set(Names.EMAIL, email);
requestData.set('needsReload', id);
...
```

Yes, we provide the iterable option for the constructor, and it can be
rewritten as (and this can even be inlined):

```
var requestData = new Map([
[Names.ID, id],
[Names.EMAIL, email],
['needsReload', id]
]);
```

However, obviously, it's too many arrays allocation for such a simple use
case.

Will it make sense having a nice declarative syntax like:

```
new Map({
[Names.ID]: id,
[Names.EMAIL]: email,
needsReload: true,
})
```

It can even be done via a simple helper method that transforms this object
literal with computed props to the same iterable array of array, but this,
unfortunately, doesn't work with all cases like "objects as keys".

I don't have the exact idea yet of how such a syntax can look like, but it
seems would be nice to have.

(unless, the use-case is not for maps, and we should use simple objects
here as was shown above)

Dmitry
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20141001/1e2900b8/attachment-0001.html>
Rick Waldron
2014-10-01 18:57:50 UTC
Permalink
On Wed, Oct 1, 2014 at 2:50 PM, Dmitry Soshnikov <dmitry.soshnikov at gmail.com
Post by Dmitry Soshnikov
Hi,
(Maps are awesome!)
1. Transforming iteration methods
We're currently polyfillying the `Map`, and got some questions form devs.
One of them is about transforming iteration methods, like `map` and
`filter`.
Unfortunately I missed that part of the spec when it was approved, so can
someone please remind/clarify -- was it an intended decision not to hap
Map#map, Map#filter? I can see only Map#forEach in the spec. Are maps
"immutable"? -- That's fine, the `map` and `filter` return a new map.
Only with regard to time. I expect there will be substantial additions to
Map and Set in ES7 (as long as the work is done, of course).
Post by Dmitry Soshnikov
2. Declarative syntax
The other thing to note: currently maps a lack of a nice declarative
syntax. This one came from the use-case for maps for dynamic (computed)
property names.
Previously, we had to allocate an empty object, and then, in the
```
var requestData = {};
requestData[Names.ID] = id;
requestData[Names.EMAIL] = email;
requestData.needsReload = true;
...
new Request(...)
.setData(requestData)
.send();
```
With computed properties of object initialisers it's much simpler and
```
new Request(...)
.setData({
[Names.ID]: id,
[Names.EMAIL]: email,
needsReload: true,
})
.send();
```
Then thing is: if we'd like to use maps for such use-case, it brings us
back to that inconvenient imperative style of assignments (even worse,
```
var requestData = new Map();
requestData.set(Names.ID, id);
requestData.set(Names.EMAIL, email);
requestData.set('needsReload', id);
...
```
Yes, we provide the iterable option for the constructor, and it can be
```
var requestData = new Map([
[Names.ID, id],
[Names.EMAIL, email],
['needsReload', id]
]);
```
However, obviously, it's too many arrays allocation for such a simple use
case.
```
new Map({
[Names.ID]: id,
[Names.EMAIL]: email,
needsReload: true,
})
```
This doesn't work because Maps allow objects as keys.

Rick
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20141001/7bf37de5/attachment.html>
Dmitry Soshnikov
2014-10-01 19:07:59 UTC
Permalink
On Wed, Oct 1, 2014 at 11:57 AM, Rick Waldron <waldron.rick at gmail.com>
Post by Rick Waldron
On Wed, Oct 1, 2014 at 2:50 PM, Dmitry Soshnikov <
Post by Dmitry Soshnikov
Hi,
(Maps are awesome!)
1. Transforming iteration methods
We're currently polyfillying the `Map`, and got some questions form devs.
One of them is about transforming iteration methods, like `map` and
`filter`.
Unfortunately I missed that part of the spec when it was approved, so can
someone please remind/clarify -- was it an intended decision not to hap
Map#map, Map#filter? I can see only Map#forEach in the spec. Are maps
"immutable"? -- That's fine, the `map` and `filter` return a new map.
Only with regard to time. I expect there will be substantial additions to
Map and Set in ES7 (as long as the work is done, of course).
Hm, sounds like two copy-pasting algorithms from the same `Array#map`,
`Array#filter`, or just tweaking the `Map#forEach` (I might be missing
something).
Post by Rick Waldron
Post by Dmitry Soshnikov
2. Declarative syntax
The other thing to note: currently maps a lack of a nice declarative
syntax. This one came from the use-case for maps for dynamic (computed)
property names.
Previously, we had to allocate an empty object, and then, in the
```
var requestData = {};
requestData[Names.ID] = id;
requestData[Names.EMAIL] = email;
requestData.needsReload = true;
...
new Request(...)
.setData(requestData)
.send();
```
With computed properties of object initialisers it's much simpler and
```
new Request(...)
.setData({
[Names.ID]: id,
[Names.EMAIL]: email,
needsReload: true,
})
.send();
```
Then thing is: if we'd like to use maps for such use-case, it brings us
back to that inconvenient imperative style of assignments (even worse,
```
var requestData = new Map();
requestData.set(Names.ID, id);
requestData.set(Names.EMAIL, email);
requestData.set('needsReload', id);
...
```
Yes, we provide the iterable option for the constructor, and it can be
```
var requestData = new Map([
[Names.ID, id],
[Names.EMAIL, email],
['needsReload', id]
]);
```
However, obviously, it's too many arrays allocation for such a simple use
case.
```
new Map({
[Names.ID]: id,
[Names.EMAIL]: email,
needsReload: true,
})
```
This doesn't work because Maps allow objects as keys.
Yes, I said it myself above. That's the thing -- I'd like to thing about
some special syntax maybe. Don't know yet, probably map as a construct for
declarative cases like:

```
// Declarative
Map {
[foo]: 1,
bar: 2,
}

// Imperative (via constructor)
new Map([ // brrr..
[foo, 1],
['bar', 2]
])
```

Dmitry
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20141001/c3561398/attachment.html>
Axel Rauschmayer
2014-10-01 19:28:57 UTC
Permalink
Post by Dmitry Soshnikov
```
var requestData = new Map();
requestData.set(Names.ID, id);
requestData.set(Names.EMAIL, email);
requestData.set('needsReload', id);
...
```
Note that you can chain:

```js
var requestData = new Map()
.set(Names.ID, id)
.set(Names.EMAIL, email)
.set('needsReload', id);
```

Not too bad, IMO.
--
Dr. Axel Rauschmayer
axel at rauschma.de
rauschma.de



-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20141001/2ee6ef04/attachment-0001.html>
Jeremy Martin
2014-10-01 19:51:58 UTC
Permalink
Not sure if this is sufficient motivation to accelerate the timeline for
adding suitable parallels from Array.prototype, but it may be worth
recalling what developers did last time there were obvious "gaps" in what a
native prototype provided. Array#contains, anyone?
Post by Dmitry Soshnikov
Then thing is: if we'd like to use maps for such use-case, it brings us
back to that inconvenient imperative style of assignments (even worse,
```
var requestData = new Map();
requestData.set(Names.ID, id);
requestData.set(Names.EMAIL, email);
requestData.set('needsReload', id);
...
```
```js
var requestData = new Map()
.set(Names.ID, id)
.set(Names.EMAIL, email)
.set('needsReload', id);
```
Not too bad, IMO.
--
Dr. Axel Rauschmayer
axel at rauschma.de
rauschma.de
_______________________________________________
es-discuss mailing list
es-discuss at mozilla.org
https://mail.mozilla.org/listinfo/es-discuss
--
Jeremy Martin
661.312.3853
http://devsmash.com
@jmar777
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20141001/05c641ab/attachment.html>
Rick Waldron
2014-10-01 19:59:41 UTC
Permalink
Post by Jeremy Martin
Not sure if this is sufficient motivation to accelerate the timeline
It's not about "motivation", it's about realistic time constraints. TC39
has already had to push out 6 months and that was not taken well by both
the community and by Ecma. Further delay is not an option.
Post by Jeremy Martin
for adding suitable parallels from Array.prototype, but it may be worth
recalling what developers did last time there were obvious "gaps" in what a
native prototype provided. Array#contains, anyone?
As a community we'll simply have to reject the use of code that directly
modifies built-ins. This is also not a very strong argument in the ES6
world where built-ins can be subclassed.


Rick
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20141001/1a13df39/attachment.html>
Dmitry Soshnikov
2014-10-01 20:26:22 UTC
Permalink
On Wed, Oct 1, 2014 at 12:59 PM, Rick Waldron <waldron.rick at gmail.com>
Post by Rick Waldron
Post by Jeremy Martin
Not sure if this is sufficient motivation to accelerate the timeline
It's not about "motivation", it's about realistic time constraints. TC39
has already had to push out 6 months and that was not taken well by both
the community and by Ecma. Further delay is not an option.
Just out of curiosity: what's the realistic "out of time" issue here?
Actually, I think having a ready and working algorithm draft on github gist
will help discussing this faster and in realistic time frames (I've
should've done before starting this thread; otherwise, these are "too
abstract time-stoppers" for me -- unless you know specific big issues that
will be hard to implement).
Post by Rick Waldron
Post by Jeremy Martin
for adding suitable parallels from Array.prototype, but it may be worth
recalling what developers did last time there were obvious "gaps" in what a
native prototype provided. Array#contains, anyone?
As a community we'll simply have to reject the use of code that directly
modifies built-ins. This is also not a very strong argument in the ES6
world where built-ins can be subclassed.
Actually the case with "borken" `Array#contains` which is so actively
discussed, is a good example. I think very likely `Map#map`, and
`Map#filter` will be added manually if not in ES6.

Dmitry
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20141001/9547172b/attachment-0001.html>
Rick Waldron
2014-10-01 20:38:43 UTC
Permalink
On Wed, Oct 1, 2014 at 4:26 PM, Dmitry Soshnikov <dmitry.soshnikov at gmail.com
Post by Dmitry Soshnikov
On Wed, Oct 1, 2014 at 12:59 PM, Rick Waldron <waldron.rick at gmail.com>
Post by Rick Waldron
Post by Jeremy Martin
Not sure if this is sufficient motivation to accelerate the timeline
It's not about "motivation", it's about realistic time constraints. TC39
has already had to push out 6 months and that was not taken well by both
the community and by Ecma. Further delay is not an option.
Just out of curiosity: what's the realistic "out of time" issue here?
Actually, I think having a ready and working algorithm draft on github gist
will help discussing this faster and in realistic time frames (I've
should've done before starting this thread; otherwise, these are "too
abstract time-stoppers" for me -- unless you know specific big issues that
will be hard to implement).
If this was really pressing, why wasn't it on any meeting agendas in the
last year?

The spec must be essentially finished by the next meeting (the last meeting
in 2014). Finalizing the Loader details, super class instantiation (you
might recall that was an issue recently), RegExp work in Annex B (may
require lexical grammar changes), revising, reviewing, etc. (I know there
is a lot more here)... All of this takes precedence over API surface
additions, whether complete or not. Additionally, the committee is trying
establish a new process for all new features. Domenic could've tried to
push Array.prototype.contains into ES6, but he's following the process.
https://github.com/tc39/ecma262
Post by Dmitry Soshnikov
Post by Rick Waldron
Post by Jeremy Martin
for adding suitable parallels from Array.prototype, but it may be worth
recalling what developers did last time there were obvious "gaps" in what a
native prototype provided. Array#contains, anyone?
As a community we'll simply have to reject the use of code that directly
modifies built-ins. This is also not a very strong argument in the ES6
world where built-ins can be subclassed.
Actually the case with "borken" `Array#contains` which is so actively
discussed, is a good example. I think very likely `Map#map`, and
`Map#filter` will be added manually if not in ES6.
See my response, I doubt that a one year wait until ES7 will really ruin
the chances for map and filter methods. In the meantime, write the spec,
get it added to https://github.com/tc39/ecma262 and enforce the semantics
on all userland implementations. Time will fly, I promise.

Rick
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20141001/93539352/attachment-0001.html>
Dmitry Soshnikov
2014-10-01 22:45:24 UTC
Permalink
Post by Rick Waldron
On Wed, Oct 1, 2014 at 4:26 PM, Dmitry Soshnikov <
Post by Dmitry Soshnikov
On Wed, Oct 1, 2014 at 12:59 PM, Rick Waldron <waldron.rick at gmail.com>
Post by Rick Waldron
Post by Jeremy Martin
Not sure if this is sufficient motivation to accelerate the timeline
It's not about "motivation", it's about realistic time constraints. TC39
has already had to push out 6 months and that was not taken well by both
the community and by Ecma. Further delay is not an option.
Just out of curiosity: what's the realistic "out of time" issue here?
Actually, I think having a ready and working algorithm draft on github gist
will help discussing this faster and in realistic time frames (I've
should've done before starting this thread; otherwise, these are "too
abstract time-stoppers" for me -- unless you know specific big issues that
will be hard to implement).
If this was really pressing, why wasn't it on any meeting agendas in the
last year?
I'm sorry, as mentioned at the beginning of this thread, I unfortunately
missed this part of the spec when the decisions were made. And since we
just recently started to polyfill the `Map` in our codebase following the
spec, I was surprised that neither `map`, nor `filter` are actually in the
spec. I thought there were some real big issues, and wanted to clarify
them. If it's only "out of time", I'd of course propose it for the agenda.
Post by Rick Waldron
The spec must be essentially finished by the next meeting (the last
meeting in 2014). Finalizing the Loader details, super class instantiation
(you might recall that was an issue recently), RegExp work in Annex B (may
require lexical grammar changes), revising, reviewing, etc. (I know there
is a lot more here)... All of this takes precedence over API surface
additions, whether complete or not. Additionally, the committee is trying
establish a new process for all new features. Domenic could've tried to
push Array.prototype.contains into ES6, but he's following the process.
https://github.com/tc39/ecma262
Yeah, absolutely agree that these are higher-pri.
Post by Rick Waldron
Post by Dmitry Soshnikov
Post by Rick Waldron
Post by Jeremy Martin
for adding suitable parallels from Array.prototype, but it may be worth
recalling what developers did last time there were obvious "gaps" in what a
native prototype provided. Array#contains, anyone?
As a community we'll simply have to reject the use of code that directly
modifies built-ins. This is also not a very strong argument in the ES6
world where built-ins can be subclassed.
Actually the case with "borken" `Array#contains` which is so actively
discussed, is a good example. I think very likely `Map#map`, and
`Map#filter` will be added manually if not in ES6.
See my response, I doubt that a one year wait until ES7 will really ruin
the chances for map and filter methods. In the meantime, write the spec,
get it added to https://github.com/tc39/ecma262 and enforce the semantics
on all userland implementations. Time will fly, I promise.
Sure, at first glance the algorithms are trivial enough as posted above.

Dmitry
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20141001/7b56a8b2/attachment-0001.html>
Dmitry Soshnikov
2014-10-01 22:33:14 UTC
Permalink
On Wed, Oct 1, 2014 at 1:26 PM, Dmitry Soshnikov <dmitry.soshnikov at gmail.com
Post by Dmitry Soshnikov
On Wed, Oct 1, 2014 at 12:59 PM, Rick Waldron <waldron.rick at gmail.com>
Post by Rick Waldron
Post by Jeremy Martin
Not sure if this is sufficient motivation to accelerate the timeline
It's not about "motivation", it's about realistic time constraints. TC39
has already had to push out 6 months and that was not taken well by both
the community and by Ecma. Further delay is not an option.
Just out of curiosity: what's the realistic "out of time" issue here?
Actually, I think having a ready and working algorithm draft on github gist
will help discussing this faster and in realistic time frames (I've
should've done before starting this thread; otherwise, these are "too
abstract time-stoppers" for me -- unless you know specific big issues that
will be hard to implement).
OK, FWIW:

- map: https://gist.github.com/DmitrySoshnikov/a218700746b2d7a7d2c8
- filter: https://gist.github.com/DmitrySoshnikov/82b46f4674acee5cedc2

Does it look like a vital algorithm? And if no one sees big/any issue with,
can it potentially be considered for inclusion? I'm not gonna push it of
course taking into account higher-pri things, however, if it seems to
everyone trivial -- why not.

(Otherwise, at least it can be a source for monkey-patching the
`Map.prototype` if it's only for ES7).

Dmitry
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20141001/73444bfc/attachment.html>
Rick Waldron
2014-10-02 19:00:49 UTC
Permalink
On Wed, Oct 1, 2014 at 6:33 PM, Dmitry Soshnikov <dmitry.soshnikov at gmail.com
Post by Dmitry Soshnikov
On Wed, Oct 1, 2014 at 1:26 PM, Dmitry Soshnikov <
Post by Dmitry Soshnikov
On Wed, Oct 1, 2014 at 12:59 PM, Rick Waldron <waldron.rick at gmail.com>
Post by Rick Waldron
Post by Jeremy Martin
Not sure if this is sufficient motivation to accelerate the timeline
It's not about "motivation", it's about realistic time constraints. TC39
has already had to push out 6 months and that was not taken well by both
the community and by Ecma. Further delay is not an option.
Just out of curiosity: what's the realistic "out of time" issue here?
Actually, I think having a ready and working algorithm draft on github gist
will help discussing this faster and in realistic time frames (I've
should've done before starting this thread; otherwise, these are "too
abstract time-stoppers" for me -- unless you know specific big issues that
will be hard to implement).
- map: https://gist.github.com/DmitrySoshnikov/a218700746b2d7a7d2c8
- filter: https://gist.github.com/DmitrySoshnikov/82b46f4674acee5cedc2
Please make a PR to https://github.com/tc39/ecma262

Rick
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20141002/f69f4c10/attachment.html>
Dmitry Soshnikov
2014-10-02 19:28:26 UTC
Permalink
On Thu, Oct 2, 2014 at 12:00 PM, Rick Waldron <waldron.rick at gmail.com>
Post by Rick Waldron
On Wed, Oct 1, 2014 at 6:33 PM, Dmitry Soshnikov <
Post by Dmitry Soshnikov
On Wed, Oct 1, 2014 at 1:26 PM, Dmitry Soshnikov <
Post by Dmitry Soshnikov
On Wed, Oct 1, 2014 at 12:59 PM, Rick Waldron <waldron.rick at gmail.com>
Post by Rick Waldron
Post by Jeremy Martin
Not sure if this is sufficient motivation to accelerate the timeline
It's not about "motivation", it's about realistic time constraints.
TC39 has already had to push out 6 months and that was not taken well by
both the community and by Ecma. Further delay is not an option.
Just out of curiosity: what's the realistic "out of time" issue here?
Actually, I think having a ready and working algorithm draft on github gist
will help discussing this faster and in realistic time frames (I've
should've done before starting this thread; otherwise, these are "too
abstract time-stoppers" for me -- unless you know specific big issues that
will be hard to implement).
- map: https://gist.github.com/DmitrySoshnikov/a218700746b2d7a7d2c8
- filter: https://gist.github.com/DmitrySoshnikov/82b46f4674acee5cedc2
Please make a PR to https://github.com/tc39/ecma262
Thanks, done. https://github.com/tc39/ecma262/pull/13

Dmitry
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20141002/ffd590f1/attachment.html>
Domenic Denicola
2014-10-05 00:52:52 UTC
Permalink
From: es-discuss [mailto:es-discuss-bounces at mozilla.org] On Behalf Of Dmitry Soshnikov
Post by Dmitry Soshnikov
Thanks, done. https://github.com/tc39/ecma262/pull/13
This seems less useful than adding %IteratorPrototype%.map and %IteratorPrototype.filter. It also clarifies some of the confusion about whether you are changing the values, keys, or entries (since you would need to specify explicitly).

Unless I'm missing something (quite possible!), I would prefer not to add new methods to Map and Set when they could be added to %IteratorPrototype%.
Domenic Denicola
2014-10-05 00:58:04 UTC
Permalink
From: es-discuss [mailto:es-discuss-bounces at mozilla.org] On Behalf Of Domenic Denicola
Post by Domenic Denicola
Unless I'm missing something (quite possible!), I would prefer not to add new methods to Map and Set when they could be added to %IteratorPrototype%.
Yeah, I'm missing something. It's the difference in code between:

```js
var newMap = oldMap.map(([k, v]) => [k + 1, v + 1]);
```

versus

```js
var newMap = new Map(oldMap.entries().map((([k, v]) => [k + 1, v + 1]);
```

I think I still prefer avoiding every iterable subclass adding its own map/filter/etc. in favor of people using the compositional base primitives, but at least I see the argument now.
Dmitry Soshnikov
2014-10-05 05:03:52 UTC
Permalink
On Sat, Oct 4, 2014 at 5:58 PM, Domenic Denicola <
Post by Domenic Denicola
From: es-discuss [mailto:es-discuss-bounces at mozilla.org] On Behalf Of Domenic Denicola
Post by Domenic Denicola
Unless I'm missing something (quite possible!), I would prefer not to
add new methods to Map and Set when they could be added to
%IteratorPrototype%.
```js
var newMap = oldMap.map(([k, v]) => [k + 1, v + 1]);
```
versus
```js
var newMap = new Map(oldMap.entries().map((([k, v]) => [k + 1, v + 1]);
```
I think I still prefer avoiding every iterable subclass adding its own
map/filter/etc. in favor of people using the compositional base primitives,
but at least I see the argument now.
We already have `Map#forEach`, and correlation with `Array#forEach ->
`Array#map` makes this constancy intuitive.

The only thing which Rick mentioned, is that `Map#map` should probably
return a transformed `[key, value]` pair, that allows updating the key as
well. However, need to think about it, since as mentioned on the diff [1],
usually people transform only values, and to force them always return an
array may be annoying (the key is not transformed in this case), and less
performant in terms useless array allocation.

This case I'd probably make as `Map#mapWithKey`, however there other
questions arise like, should the returned pair replace the existing, or
create a new one, if the key is different?

```
({a => 10}).mapWithKey((k, v) => ['b', 20]); // {b => 20} or {a => 20, b =>
20} ?
```

But this is for further discussion.

Dmitry
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20141004/c2460935/attachment.html>
Brendan Eich
2014-10-05 05:37:21 UTC
Permalink
Post by Domenic Denicola
From: es-discuss [mailto:es-discuss-bounces at mozilla.org] On Behalf Of Domenic Denicola
- show quoted text -
Post by Domenic Denicola
Unless I'm missing something (quite possible!), I would prefer not to add new methods to Map and Set when they could be added to %IteratorPrototype%.
```js
var newMap = oldMap.map(([k, v]) => [k+ 1, v+ 1]);
```
versus
```js
var newMap = new Map(oldMap.entries().map((([k, v]) => [k+ 1, v+ 1]);
But entries returns an iterator, and if we define %IteratorPrototype% to
create a new instance of the right class given its |this| parameter, in
this case Map and not MapIterator, then no need for the new Map() wrapper.
Post by Domenic Denicola
I think I still prefer avoiding every iterable subclass adding its own map/filter/etc. in favor of people using the compositional base primitives, but at least I see the argument now.
I point out in https://www.w3.org/Bugs/Public/show_bug.cgi?id=26973#c3
that the lack of Map.prototype.{map,filter,...} doesn't bite when you
use for-of, but of course will be missed when you want to call
someMap.filter directly. Which is reasonable to do!

/be
Domenic Denicola
2014-10-05 23:53:46 UTC
Permalink
From: Brendan Eich [mailto:brendan at mozilla.org]
But entries returns an iterator, and if we define %IteratorPrototype% to create a new instance of the right class given its |this| parameter, in this case Map and not MapIterator, then no need for the new Map() wrapper.
This seems like it'd involve too much machinery and give unexpected results.

I'd expect `instance.map(...)` to return another one of `instance`. That is, given a MapIterator `i`, I'd expect `i.map(...)` to be another MapIterator, and not a Map like you are proposing.

Giving a MapIterator also has the advantage of preserving laziness. I like how in
```js
var newMap = new Map(oldMap.entries().map((([k, v]) => [k + 1, v+ 1]);
```
The Map constructor call gives a clear delineation of where the iterator is being consumed, and the laziness is flattened down. Whereas if `.entries().map(...)` gave a Map instance straightaway, the option of keeping things lazy would be gone.
Brendan Eich
2014-10-06 03:44:47 UTC
Permalink
Post by Domenic Denicola
From: Brendan Eich [mailto:brendan at mozilla.org]
But entries returns an iterator, and if we define %IteratorPrototype% to create a new instance of the right class given its|this| parameter, in this case Map and not MapIterator, then no need for the new Map() wrapper.
This seems like it'd involve too much machinery and give unexpected results.
I'd expect `instance.map(...)` to return another one of `instance`. That is, given a MapIterator `i`, I'd expect `i.map(...)` to be another MapIterator, and not a Map like you are proposing.
Right, I was smoking something bad. The uniformity you describe is
clearly the right solution.

This meansMap.prototype.mapetc. -- or %ItereablePrototype%.map if not
Array.prototype.map etc. -- are the eager collection-generic companions.

/be
Brendan Eich
2014-10-06 16:37:04 UTC
Permalink
Post by Brendan Eich
This meansMap.prototype.mapetc. -- or %ItereablePrototype%.map if not
Array.prototype.map etc. -- are the eager collection-generic companions.
Bear with me, trying to develop a half-thought. To say it with code:

```js
Object.defineProperty(%IteratorPrototype%, 'map', {
value: function (fun) {
return function* (iter) {
for (let [key, val] of iter) {
yield fun(val, key);
}
}(this.clone());
},
configurable: true,
enumerable: false,
writable: true
});
```

Note the this.clone() call. This is novel, and required to avoid
exhausting the receiver iterator.

Rather than %IterablePrototype%, which is too generic a name (it does
not imply a key/value map), we need something such as %MaplikePrototype%
or %CollectionPrototype%:

```js
Object.defineProperty(%CollectionPrototype%, 'map', {
value: function (fun) {
let result = new this.constructor();
for (let [key, val] of this) {
result.set(key, val);
}
return result;
},
configurable: true,
enumerable: false,
writable: true
});
```

The Collection protocol thus consists of at least .constructor,
@@iterator, .set. (Is there a better way to clone? Don't want a new
protocol where an old one will suffice!) This Collection protocol would
include Map and maplikes but leaves out Set, Array, Object -- appropriately.

We could just use FP-style map, filter, etc. APIs, but as argued among
TC39ers, OOP wins in JS: (a) it matches built-ins; (b) it composes left
to right, not inside out.

This argument implies a %CollectionPrototype% object full of
protocol-generic methods, to avoid rewriting collection methods all over
the place (wherever there is a maplike), or borrowing Map.prototype.*
references by hand and monkeypatching them into maplike prototypes.

Unless I'm mistaken, the OOP-ness also implies other stuff, like a way
to clone an iterator.

I hope this makes sense. Comments welcome.

/be
Brendan Eich
2014-10-06 16:55:42 UTC
Permalink
Post by Brendan Eich
```js
Object.defineProperty(%CollectionPrototype%, 'map', {
value: function (fun) {
let result = new this.constructor();
for (let [key, val] of this) {
result.set(key, val);
```js
result.set(key, fun(val, key));
```

I call the mapfun in these examples with (val, key) -- could use the
e-i-c convention from the Array extras but I was in a hurry (wherefore
this bug).

/be
Domenic Denicola
2014-10-06 17:02:31 UTC
Permalink
Note the this.clone() call. This is novel, and required to avoid exhausting the receiver iterator.
This is noteworthy and worth keeping in mind regardless of the rest of the discussion. I think I would be fine having %IterablePrototype%.map exhaust the iterator. I would `.clone()` manually myself if I didn't want that. But in general anytime I loop through the iterator---either directly with `for`-`of`, or indirectly with `.map` and friends---I would expect exhaustion.
We could just use FP-style map, filter, etc. APIs, but as argued among TC39ers, OOP wins in JS: (a) it matches built-ins; (b) it composes left to right, not inside out.
I am still hoping [the bind operator][1] gives us a good middle ground. E.g.

```js
import { map } from "js/iterables";

var newMap = oldMap::map(([k, v]) => [k + 1, v + 1]);
```

Here I guess `map` would look something like

```js
function map(...args) {
return new this.constructor(this.entries().map(...args));
}
```

(Note that I use `new this.constructor(entriesIterable)` instead of your `var m = new this.constructor()` plus `m.set(...)`. Unclear which is better.)

[1]: http://wiki.ecmascript.org/doku.php?id=strawman:bind_operator
Comments welcome.
My first thought is that it seems a little far out there given that we're talking about the net benefit being reducing

```js
var newMap = new Map(oldMap.entries().map((([k, v]) => [k + 1, v + 1]);
```

to

```js
var newMap = oldMap.map(([k, v]) => [k + 1, v + 1]);
```

My second thought is that we might also want to consider approaching this from the perspective of traits, see e.g. [an earlier discussion about Set][2].

But, collections do need some love, so it's good that we're thinking about this.

[2]: https://esdiscuss.org/topic/overriding-map-etc-with-get-set-hooks#content-54
Brendan Eich
2014-10-06 17:17:40 UTC
Permalink
Post by Domenic Denicola
Note the this.clone() call. This is novel, and required to avoid exhausting the receiver iterator.
This is noteworthy and worth keeping in mind regardless of the rest of the discussion. I think I would be fine having %IterablePrototype%.map exhaust the iterator. I would `.clone()` manually myself if I didn't want that. But in general anytime I loop through the iterator---either directly with `for`-`of`, or indirectly with `.map` and friends---I would expect exhaustion.
For iteration, sure. For map or other functional (pure?!) APIs? Yikes.

If in ES7 we build up the duality between iterables and observables, add
for-on, pave the way toward FRP, then `map` as a protocol (part) should
be pure with respect to its receiver, ISTM.
Post by Domenic Denicola
We could just use FP-style map, filter, etc. APIs, but as argued among TC39ers, OOP wins in JS: (a) it matches built-ins; (b) it composes left to right, not inside out.
I am still hoping [the bind operator][1] gives us a good middle ground. E.g.
```js
import { map } from "js/iterables";
var newMap = oldMap::map(([k, v]) => [k+ 1, v+ 1]);
```
Here I guess `map` would look something like
```js
function map(...args) {
return new this.constructor(this.entries().map(...args));
}
```
(Note that I use `new this.constructor(entriesIterable)` instead of your `var m = new this.constructor()` plus `m.set(...)`. Unclear which is better.)
More elaborate constructor protocol, but if it's part of maplike, may be ok.
Post by Domenic Denicola
[1]:http://wiki.ecmascript.org/doku.php?id=strawman:bind_operator
Problem is :: vs. just dot, and now you have two problems. I'm actually
good with FP inside-out, could learn to love :: if we add it, but the
main objection IMHO is two-problems. OOP means just dot.
Post by Domenic Denicola
Comments welcome.
My first thought is that it seems a little far out there given that we're talking about the net benefit being reducing
```js
var newMap = new Map(oldMap.entries().map((([k, v]) => [k+ 1, v+ 1]);
```
to
```js
var newMap = oldMap.map(([k, v]) => [k+ 1, v+ 1]);
```
Fair, just trying to develop a thought on es-discuss. I'm not sure we
should do anything in TC39 about this. I want future underscore.js
libraries to tread those cowpaths we ultimately pave.

Also the ES7 iterable/observable duality and the DOM maplike demands
combine to push for paving sooner. We should be minimalists as you
suggest, though.
Post by Domenic Denicola
My second thought is that we might also want to consider approaching this from the perspective of traits, see e.g. [an earlier discussion about Set][2].
Traits are great -- we lost a live, championed proposal at some point on
the road to ES6. Who will revive?

/be
Domenic Denicola
2014-10-06 18:34:23 UTC
Permalink
From: Brendan Eich [mailto:brendan at mozilla.org]
Post by Brendan Eich
For iteration, sure. For map or other functional (pure?!) APIs? Yikes.
If in ES7 we build up the duality between iterables and observables, add for-on, pave the way toward FRP, then `map` as a protocol (part) should be pure with respect to its receiver, ISTM.
This is iterables vs. iterators IMO. Iterators are inherently stateful and I'd expect anything that uses them to consume them, even something named "map". Iterables of course would not be.
Jason Orendorff
2014-10-06 18:43:53 UTC
Permalink
On Mon, Oct 6, 2014 at 1:34 PM, Domenic Denicola
Post by Domenic Denicola
This is iterables vs. iterators IMO. Iterators are inherently stateful and I'd expect anything that uses them to consume them, even something named "map". Iterables of course would not be.
I agree. It's inherent in the python-like design we're using for iteration.

It is not so jarring in practice, even for people with the classical
training to recognize what a farce it is. :) Many uses of itertools in
python, though blatantly stateful in implementation and operation,
still "feel" functional, because the iterator being consumed is a
temporary, something like mydict.iterkeys(). The mutation isn't
observable if there's no other reference to the iterator object.

-j
Brendan Eich
2014-10-06 21:21:44 UTC
Permalink
Good to known (and I had heard this via Python community channels
already). Still feels wrong, given "name polymorphism" on `map`. Perhaps
we just plow ahead.

Still seems like someone (at some layer) will want iter.clone(). Vouch?

/be
OnMon, Oct 6, 2014 at 1:34 PM, Domenic Denicola
Post by Domenic Denicola
This is iterables vs. iterators IMO. Iterators are inherently stateful and I'd expect anything that uses them to consume them, even something named "map". Iterables of course would not be.
I agree. It's inherent in the python-like design we're using for iteration.
It is not so jarring in practice, even for people with the classical
training to recognize what a farce it is.:) Many uses of itertools in
python, though blatantly stateful in implementation and operation,
still "feel" functional, because the iterator being consumed is a
temporary, something like mydict.iterkeys(). The mutation isn't
observable if there's no other reference to the iterator object.
-j
Kevin Smith
2014-10-07 15:51:01 UTC
Permalink
Post by Domenic Denicola
[1]:http://wiki.ecmascript.org/doku.php?id=strawman:bind_operator
Problem is :: vs. just dot, and now you have two problems. I'm actually
good with FP inside-out, could learn to love :: if we add it, but the main
objection IMHO is two-problems. OOP means just dot.
That reminds me:

https://esdiscuss.org/topic/merging-bind-syntax-with-relationships

I know, OOP wins and all, but the downside is that it puts all the burden
onto TC39 for defining a complete iterator API. The double-colon is a bit
nasty-looking, though... Maybe we should consider single-arrow?

For example:

import { slice, forEach } from "my-userland-iterator-lib";

gimmeIterator()->slice(10, 20)->forEach(x => console.log(x));

Or for private fields:

import { makePrivate } from "private-fields";

const _x = makePrivate();
const _y = makePrivate();

class Point {
constructor(x, y) {
this->_x = x;
this->_y = y;
}
}


In any case, I think a merged relationships (i.e. private fields) + binding
mechanism would be a *huge* win for the language.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20141007/42cd1f76/attachment-0001.html>
Andrea Giammarchi
2014-10-07 16:12:19 UTC
Permalink
Coming form other languages, double colons is used as a "static" thing,
although we don't need (?) this convention in ES6 classes, and I agree it
looks ugly anyway.

I wonder if here `:>` would work instead, because I'd personally find any
other usage of the thin arrow `->` confusing *

Regards

* I personally see `->` as a shortcut for `function`
Post by Kevin Smith
Post by Domenic Denicola
[1]:http://wiki.ecmascript.org/doku.php?id=strawman:bind_operator
Problem is :: vs. just dot, and now you have two problems. I'm actually
good with FP inside-out, could learn to love :: if we add it, but the main
objection IMHO is two-problems. OOP means just dot.
https://esdiscuss.org/topic/merging-bind-syntax-with-relationships
I know, OOP wins and all, but the downside is that it puts all the burden
onto TC39 for defining a complete iterator API. The double-colon is a bit
nasty-looking, though... Maybe we should consider single-arrow?
import { slice, forEach } from "my-userland-iterator-lib";
gimmeIterator()->slice(10, 20)->forEach(x => console.log(x));
import { makePrivate } from "private-fields";
const _x = makePrivate();
const _y = makePrivate();
class Point {
constructor(x, y) {
this->_x = x;
this->_y = y;
}
}
In any case, I think a merged relationships (i.e. private fields) +
binding mechanism would be a *huge* win for the language.
_______________________________________________
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/20141007/ed75f024/attachment.html>
Kevin Smith
2014-10-07 17:23:49 UTC
Permalink
Post by Andrea Giammarchi
* I personally see `->` as a shortcut for `function`
I'm a C sympathizer, so I don't see it the quite the same, but I
understand. Regardless of the token, I feel like a generalized ref_get/set
mechanism would be great.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20141007/8a508178/attachment.html>
Mark S. Miller
2014-10-07 17:42:23 UTC
Permalink
Post by Kevin Smith
Post by Domenic Denicola
[1]:http://wiki.ecmascript.org/doku.php?id=strawman:bind_operator
Problem is :: vs. just dot, and now you have two problems. I'm actually
good with FP inside-out, could learn to love :: if we add it, but the main
objection IMHO is two-problems. OOP means just dot.
https://esdiscuss.org/topic/merging-bind-syntax-with-relationships
Hi Kevin,

Somehow I had missed that post until now. Interesting! For the sake of
clarity while examining alternatives, let's assume for the moment the
following infix operators:

infix "::" for bind alone as in
http://wiki.ecmascript.org/doku.php?id=strawman:bind_operator

infix "@" for relationships alone, as in
http://wiki.ecmascript.org/doku.php?id=strawman:relationships

infix "->" for bind+relationships as you propose here.


My question is: If we have "->", how much need would there still be for
"::" or "@"? If "->" can subsume the practical need for either or both of
the others, that would be interesting.

We should postpone any further bikeshedding on which token to use for which
meaning until we understand better which ones we actually need.
Post by Kevin Smith
I know, OOP wins and all, but the downside is that it puts all the burden
onto TC39 for defining a complete iterator API. The double-colon is a bit
nasty-looking, though... Maybe we should consider single-arrow?
import { slice, forEach } from "my-userland-iterator-lib";
gimmeIterator()->slice(10, 20)->forEach(x => console.log(x));
import { makePrivate } from "private-fields";
const _x = makePrivate();
const _y = makePrivate();
class Point {
constructor(x, y) {
this->_x = x;
this->_y = y;
}
}
In any case, I think a merged relationships (i.e. private fields) +
binding mechanism would be a *huge* win for the language.
_______________________________________________
es-discuss mailing list
es-discuss at mozilla.org
https://mail.mozilla.org/listinfo/es-discuss
--
Cheers,
--MarkM
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20141007/1dff4ad1/attachment.html>
Kevin Smith
2014-10-07 17:50:09 UTC
Permalink
Post by Mark S. Miller
My question is: If we have "->", how much need would there still be for
the others, that would be interesting.
That was the idea, but it's been a while since I went through that
exercise. I need to review.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20141007/31635761/attachment-0001.html>
Dmitry Soshnikov
2014-10-08 21:49:58 UTC
Permalink
Post by Brendan Eich
Post by Brendan Eich
This meansMap.prototype.mapetc. -- or %ItereablePrototype%.map if not
Array.prototype.map etc. -- are the eager collection-generic companions.
```js
Object.defineProperty(%IteratorPrototype%, 'map', {
value: function (fun) {
return function* (iter) {
for (let [key, val] of iter) {
yield fun(val, key);
}
}(this.clone());
},
configurable: true,
enumerable: false,
writable: true
});
```
Note the this.clone() call. This is novel, and required to avoid
exhausting the receiver iterator.
Rather than %IterablePrototype%, which is too generic a name (it does not
imply a key/value map), we need something such as %MaplikePrototype% or
```js
Object.defineProperty(%CollectionPrototype%, 'map', {
value: function (fun) {
let result = new this.constructor();
for (let [key, val] of this) {
result.set(key, val);
}
return result;
},
configurable: true,
enumerable: false,
writable: true
});
```
The Collection protocol thus consists of at least .constructor,
@@iterator, .set. (Is there a better way to clone? Don't want a new
protocol where an old one will suffice!) This Collection protocol would
include Map and maplikes but leaves out Set, Array, Object -- appropriately.
As long as it can be generic enough for `Map`, `Set` and .. (what else?) --
I actually like this one. Can try specifying it in a separate diff and will
get back to you.
Post by Brendan Eich
We could just use FP-style map, filter, etc. APIs, but as argued among
TC39ers, OOP wins in JS: (a) it matches built-ins; (b) it composes left to
right, not inside out.
This argument implies a %CollectionPrototype% object full of
protocol-generic methods, to avoid rewriting collection methods all over
the place (wherever there is a maplike), or borrowing Map.prototype.*
references by hand and monkeypatching them into maplike prototypes.
Yeah.
Post by Brendan Eich
Unless I'm mistaken, the OOP-ness also implies other stuff, like a way to
clone an iterator.
I hope this makes sense. Comments welcome.
Yep, thanks!

Dmitry
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20141008/289ad72f/attachment-0001.html>
Brendan Eich
2014-10-08 22:09:22 UTC
Permalink
Post by Brendan Eich
The Collection protocol thus consists of at least .constructor,
@@iterator, .set. (Is there a better way to clone? Don't want a
new protocol where an old one will suffice!) This Collection
protocol would include Map and maplikes but leaves out Set, Array,
Object -- appropriately.
As long as it can be generic enough for `Map`, `Set`
Set methods include add, whereas Map has set. Set lacks a get method,
whereas Map of course has one. So not the same protocol.

/be
Dmitry Soshnikov
2014-10-09 03:05:23 UTC
Permalink
Post by Brendan Eich
Post by Brendan Eich
The Collection protocol thus consists of at least .constructor,
@@iterator, .set. (Is there a better way to clone? Don't want a
new protocol where an old one will suffice!) This Collection
protocol would include Map and maplikes but leaves out Set, Array,
Object -- appropriately.
As long as it can be generic enough for `Map`, `Set`
Set methods include add, whereas Map has set. Set lacks a get method,
whereas Map of course has one. So not the same protocol.
I see, yeah, it's doable, and potentially they can be handled via abstract
operations with checking kind of a collection (`IsSet`, `IsMap`), and
setting needed entries (`AddEntry` would cover `set` for maps, and `add`
for Sets), and `GetEntry` would return needed thing since sets are just
backed by maps. However, this seems not a big win in terms of constant
runtime checks for this, and probably having a separate algorithms are
better (even if some parts are repeated).

If you want to have just an explicit protocol/interface for "MapLike"s and
"SetLikes" (i.e. with any explicit `set` method, and stuff which can be
implemented at user-level by any object), it's possible. Although, this
interface implementation can also be just replaced with sub-classing of the
`Map`, and the same algorithm works. It's just a difference b/w duck-typing
(the protocol, that any object may implement regardless its hierarchy), or
the inheritance. Will think about it, and maybe will come up with a spec.

Dmitry
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20141008/45afc851/attachment.html>
Dmitry Soshnikov
2014-10-05 06:16:04 UTC
Permalink
On Sat, Oct 4, 2014 at 10:03 PM, Dmitry Soshnikov <
Post by Dmitry Soshnikov
On Sat, Oct 4, 2014 at 5:58 PM, Domenic Denicola <
Post by Domenic Denicola
From: es-discuss [mailto:es-discuss-bounces at mozilla.org] On Behalf Of Domenic Denicola
Post by Domenic Denicola
Unless I'm missing something (quite possible!), I would prefer not to
add new methods to Map and Set when they could be added to
%IteratorPrototype%.
```js
var newMap = oldMap.map(([k, v]) => [k + 1, v + 1]);
```
versus
```js
var newMap = new Map(oldMap.entries().map((([k, v]) => [k + 1, v + 1]);
```
I think I still prefer avoiding every iterable subclass adding its own
map/filter/etc. in favor of people using the compositional base primitives,
but at least I see the argument now.
We already have `Map#forEach`, and correlation with `Array#forEach ->
`Array#map` makes this constancy intuitive.
The only thing which Rick mentioned, is that `Map#map` should probably
return a transformed `[key, value]` pair, that allows updating the key as
well. However, need to think about it, since as mentioned on the diff [1],
usually people transform only values, and to force them always return an
array may be annoying (the key is not transformed in this case), and less
performant in terms useless array allocation.
This case I'd probably make as `Map#mapWithKey`, however there other
questions arise like, should the returned pair replace the existing, or
create a new one, if the key is different?
```
({a => 10}).mapWithKey((k, v) => ['b', 20]); // {b => 20} or {a => 20, b
=> 20} ?
```
Whoops, nevermind actually, since we don't mutate the original map (the
`this`), but rather return a new one, it should always be just `{b => 20}`,
and `a` key is ignored in the transformed map.

Dmitry
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20141004/1b7e4abd/attachment-0001.html>
Tab Atkins Jr.
2014-10-07 17:01:15 UTC
Permalink
On Sat, Oct 4, 2014 at 10:03 PM, Dmitry Soshnikov
Post by Dmitry Soshnikov
This case I'd probably make as `Map#mapWithKey`, however there other
questions arise like, should the returned pair replace the existing, or
create a new one, if the key is different?
```
({a => 10}).mapWithKey((k, v) => ['b', 20]); // {b => 20} or {a => 20, b =>
20} ?
```
But this is for further discussion.
Mapping produces a new object, so the answer to that is luckily trivial.

~TJ
Dmitry Soshnikov
2014-10-08 00:40:41 UTC
Permalink
On Sat, Oct 4, 2014 at 10:03 PM, Dmitry Soshnikov <
Post by Dmitry Soshnikov
On Sat, Oct 4, 2014 at 5:58 PM, Domenic Denicola <
Post by Domenic Denicola
From: es-discuss [mailto:es-discuss-bounces at mozilla.org] On Behalf Of Domenic Denicola
Post by Domenic Denicola
Unless I'm missing something (quite possible!), I would prefer not to
add new methods to Map and Set when they could be added to
%IteratorPrototype%.
```js
var newMap = oldMap.map(([k, v]) => [k + 1, v + 1]);
```
versus
```js
var newMap = new Map(oldMap.entries().map((([k, v]) => [k + 1, v + 1]);
```
I think I still prefer avoiding every iterable subclass adding its own
map/filter/etc. in favor of people using the compositional base primitives,
but at least I see the argument now.
We already have `Map#forEach`, and correlation with `Array#forEach ->
`Array#map` makes this constancy intuitive.
The only thing which Rick mentioned, is that `Map#map` should probably
return a transformed `[key, value]` pair, that allows updating the key as
well. However, need to think about it, since as mentioned on the diff [1],
usually people transform only values, and to force them always return an
array may be annoying (the key is not transformed in this case), and less
performant in terms useless array allocation.
This case I'd probably make as `Map#mapWithKey`, however there other
questions arise like, should the returned pair replace the existing, or
create a new one, if the key is different?
```
({a => 10}).mapWithKey((k, v) => ['b', 20]); // {b => 20} or {a => 20, b
=> 20} ?
```
But this is for further discussion.
I think it's definitely worth discussing the generic collection prototype,
however at the meantime, an updated version of the mapping function, here:
https://gist.github.com/DmitrySoshnikov/a218700746b2d7a7d2c8

- Moved the mapping to the abstract `MapTransform`
- Reused it for `Map#map` and `Map#mapEntries` (which updates the keys as
well)

Dmitry
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20141007/fd1b3dc3/attachment.html>
Rick Waldron
2014-10-01 20:26:02 UTC
Permalink
Post by Jeremy Martin
Not sure if this is sufficient motivation to accelerate the timeline for
adding suitable parallels from Array.prototype, but it may be worth
recalling what developers did last time there were obvious "gaps" in what a
native prototype provided. Array#contains, anyone?
Also, if there are written proposals, the userland extensions (if they
really _must_ be) can follow those proposals.

Rick
Post by Jeremy Martin
Post by Dmitry Soshnikov
Then thing is: if we'd like to use maps for such use-case, it brings us
back to that inconvenient imperative style of assignments (even worse,
```
var requestData = new Map();
requestData.set(Names.ID, id);
requestData.set(Names.EMAIL, email);
requestData.set('needsReload', id);
...
```
```js
var requestData = new Map()
.set(Names.ID, id)
.set(Names.EMAIL, email)
.set('needsReload', id);
```
Not too bad, IMO.
--
Dr. Axel Rauschmayer
axel at rauschma.de
rauschma.de
_______________________________________________
es-discuss mailing list
es-discuss at mozilla.org
https://mail.mozilla.org/listinfo/es-discuss
--
Jeremy Martin
661.312.3853
http://devsmash.com
@jmar777
_______________________________________________
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/20141001/07a73181/attachment.html>
Dmitry Soshnikov
2014-10-01 22:35:44 UTC
Permalink
Post by Dmitry Soshnikov
Then thing is: if we'd like to use maps for such use-case, it brings us
back to that inconvenient imperative style of assignments (even worse,
```
var requestData = new Map();
requestData.set(Names.ID, id);
requestData.set(Names.EMAIL, email);
requestData.set('needsReload', id);
...
```
```js
var requestData = new Map()
.set(Names.ID, id)
.set(Names.EMAIL, email)
.set('needsReload', id);
```
Not too bad, IMO.
Not ideal either. Usually langs provide nice declarative syntax for such
things. E.g. we have[1] the same in the HACK language, and use it well
everyday when need a map.

But this part is of course not for ES6, hope ES7-ish.

[1] http://docs.hhvm.com/manual/en/hack.collections.map.php
Dmitry
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20141001/2b2f1ff7/attachment.html>
Brendan Eich
2014-10-01 23:51:15 UTC
Permalink
Post by Dmitry Soshnikov
Not ideal either. Usually langs provide nice declarative syntax for
such things. E.g. we have[1] the same in the HACK language, and use it
well everyday when need a map.
But this part is of course not for ES6, hope ES7-ish.
[1] http://docs.hhvm.com/manual/en/hack.collections.map.php
We could definitely have Map and Set literals:

const map = {1 => "one", "two" => true, false => "three"};

const set = {<1, "two", false>};

If you still buy Harmony of My Dreams, prefix # before { to get
immutable value-type forms.

I don't mind reusing => in initialiser context where : would go, but
perhaps someone sees a problem I don't. The Set literal hack of {< and
Post by Dmitry Soshnikov
} seems necessary given object initialiser property assignment
shorthand syntax ({x, y} for {x:x, y:y}). Some kind of hack is required,
yet losing { and } as outermost bracketing characters for Set seems
worse than any digraph or token-pair alternative.

/be
Dmitry Soshnikov
2014-10-02 03:33:55 UTC
Permalink
Post by Brendan Eich
Post by Dmitry Soshnikov
Not ideal either. Usually langs provide nice declarative syntax for such
things. E.g. we have[1] the same in the HACK language, and use it well
everyday when need a map.
But this part is of course not for ES6, hope ES7-ish.
[1] http://docs.hhvm.com/manual/en/hack.collections.map.php
const map = {1 => "one", "two" => true, false => "three"};
const set = {<1, "two", false>};
Yeah, something like this (I actually like these two forms); later for ES7.
Post by Brendan Eich
If you still buy Harmony of My Dreams, prefix # before { to get immutable
value-type forms.
Yep, why not, can be (re)considered as well I guess.

Dmitry
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20141001/a42f9c8f/attachment.html>
Axel Rauschmayer
2014-10-02 03:47:55 UTC
Permalink
Post by Brendan Eich
const map = {1 => "one", "two" => true, false => "three"};
Would that be tricky to parse if keys can be arbitrary expressions (incl. array literals)?
Post by Brendan Eich
const set = {<1, "two", false>};
If you still buy Harmony of My Dreams, prefix # before { to get immutable value-type forms.
I don't mind reusing => in initialiser context where : would go, but perhaps someone sees a problem I don't.
Using something other than a colon seems a good idea, to make it clear that any kind of value can be used as keys.

Another possibility:

```js
const map = {: 1 => "one", "two" => true, false => "three" :};
const set = {. 1, "two", false .};
```
--
Dr. Axel Rauschmayer
axel at rauschma.de
rauschma.de



-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20141002/3fc83438/attachment.html>
Brendan Eich
2014-10-02 04:38:09 UTC
Permalink
Post by Axel Rauschmayer
Post by Brendan Eich
const map = {1 => "one", "two" => true, false => "three"};
Would that be tricky to parse if keys can be arbitrary expressions (incl. array literals)?
The issue would be arrow function expression keys:

const map = {x => x*x => "square"};

But such a key could not be found without capturing the reference:

let f;
const map = {f = x => x*x => "square"};
let g = map.get(f);
assertEq(g, f);

It's all parseable but an eyesore; but also unlikely/contrived.
Post by Axel Rauschmayer
Post by Brendan Eich
const set = {<1, "two", false>};
If you still buy Harmony of My Dreams, prefix # before { to get
immutable value-type forms.
I don't mind reusing => in initialiser context where : would go, but
perhaps someone sees a problem I don't.
Using something other than a colon seems a good idea, to make it clear
that any kind of value can be used as keys.
```js
const map = {: 1 => "one", "two" => true, false => "three" :};
const set = {. 1, "two", false .};
```
Not sure what's best, but . is visually light, also used in the language
in ways independent from Sets.

I don't see the need for extra delimiters for map literals -- why tax
two chars each literal just for parallelism w.r.t. set literals?

/be
Axel Rauschmayer
2014-10-01 20:12:34 UTC
Permalink
Post by Dmitry Soshnikov
1. Transforming iteration methods
We're currently polyfillying the `Map`, and got some questions form devs. One of them is about transforming iteration methods, like `map` and `filter`.
Unfortunately I missed that part of the spec when it was approved, so can someone please remind/clarify -- was it an intended decision not to hap Map#map, Map#filter? I can see only Map#forEach in the spec. Are maps "immutable"? -- That's fine, the `map` and `filter` return a new map.
FWIW: I?ll probably use the following work-arounds. I don?t expect performance to be an issue for my applications; it?d be interesting to hear if it becomes a problem for someone.

```js
let map2 = new Map(map1.entries().filter((key, value) => key >= 0));
let map2 = new Map(map1.entries().map((key, value) => [key * 2, value * 2]));
```
--
Dr. Axel Rauschmayer
axel at rauschma.de
rauschma.de



-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20141001/f35d0cc4/attachment.html>
Axel Rauschmayer
2014-10-01 20:17:12 UTC
Permalink
Post by Axel Rauschmayer
Post by Dmitry Soshnikov
1. Transforming iteration methods
We're currently polyfillying the `Map`, and got some questions form devs. One of them is about transforming iteration methods, like `map` and `filter`.
Unfortunately I missed that part of the spec when it was approved, so can someone please remind/clarify -- was it an intended decision not to hap Map#map, Map#filter? I can see only Map#forEach in the spec. Are maps "immutable"? -- That's fine, the `map` and `filter` return a new map.
FWIW: I?ll probably use the following work-arounds. I don?t expect performance to be an issue for my applications; it?d be interesting to hear if it becomes a problem for someone.
```js
let map2 = new Map(map1.entries().filter((key, value) => key >= 0));
let map2 = new Map(map1.entries().map((key, value) => [key * 2, value * 2]));
```
Almost. Correct versions:

```js
let map2 = new Map(map1.entries().filter(([key, value]) => key >= 0));
let map2 = new Map(map1.entries().map(([key, value]) => [key * 2, value * 2]));
```
--
Dr. Axel Rauschmayer
axel at rauschma.de
rauschma.de



-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20141001/ea71b258/attachment.html>
Rick Waldron
2014-10-01 20:27:28 UTC
Permalink
Post by Dmitry Soshnikov
1. Transforming iteration methods
We're currently polyfillying the `Map`, and got some questions form devs.
One of them is about transforming iteration methods, like `map` and
`filter`.
Unfortunately I missed that part of the spec when it was approved, so can
someone please remind/clarify -- was it an intended decision not to hap
Map#map, Map#filter? I can see only Map#forEach in the spec. Are maps
"immutable"? -- That's fine, the `map` and `filter` return a new map.
FWIW: I?ll probably use the following work-arounds. I don?t expect
performance to be an issue for my applications; it?d be interesting to hear
if it becomes a problem for someone.
```js
let map2 = new Map(map1.entries().filter((key, value) => key >= 0));
let map2 = new Map(map1.entries().map((key, value) => [key * 2, value * 2]));
entries() returns an iterator, not an array.
Rick
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20141001/933522da/attachment.html>
Axel Rauschmayer
2014-10-01 20:38:52 UTC
Permalink
Ah, thanks! Then I?d wrap the result of `entries()` in `Array.from()`. In ES7, we?ll hopefully be able to use comprehensions or iterator helper functions.
Post by Dmitry Soshnikov
Post by Axel Rauschmayer
Post by Dmitry Soshnikov
1. Transforming iteration methods
We're currently polyfillying the `Map`, and got some questions form devs. One of them is about transforming iteration methods, like `map` and `filter`.
Unfortunately I missed that part of the spec when it was approved, so can someone please remind/clarify -- was it an intended decision not to hap Map#map, Map#filter? I can see only Map#forEach in the spec. Are maps "immutable"? -- That's fine, the `map` and `filter` return a new map.
FWIW: I?ll probably use the following work-arounds. I don?t expect performance to be an issue for my applications; it?d be interesting to hear if it becomes a problem for someone.
```js
let map2 = new Map(map1.entries().filter((key, value) => key >= 0));
let map2 = new Map(map1.entries().map((key, value) => [key * 2, value * 2]));
entries() returns an iterator, not an array.
Rick
--
Dr. Axel Rauschmayer
axel at rauschma.de
rauschma.de



-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20141001/d609305a/attachment.html>
Rick Waldron
2014-10-01 20:48:32 UTC
Permalink
Post by Axel Rauschmayer
Ah, thanks! Then I?d wrap the result of `entries()` in `Array.from()`. In
ES7, we?ll hopefully be able to use comprehensions or iterator helper
functions.
Don't need to do that either.

let map1 = new Map([[-1, -1], [0, 0], [1, 1], [2, 2]]);
let map2 = new Map([...map1].filter(([key, value]) => key >= 0));
let map3 = new Map([...map1].map(([key, value]) => [key * 2, value * 2]));

console.log([...map2]); // [[0, 0], [1, 1], [2, 2]]
console.log([...map3]); // [[-2, -2], [0, 0], [2, 2], [4, 4]]


Rick
Post by Axel Rauschmayer
Post by Dmitry Soshnikov
1. Transforming iteration methods
We're currently polyfillying the `Map`, and got some questions form devs.
One of them is about transforming iteration methods, like `map` and
`filter`.
Unfortunately I missed that part of the spec when it was approved, so can
someone please remind/clarify -- was it an intended decision not to hap
Map#map, Map#filter? I can see only Map#forEach in the spec. Are maps
"immutable"? -- That's fine, the `map` and `filter` return a new map.
FWIW: I?ll probably use the following work-arounds. I don?t expect
performance to be an issue for my applications; it?d be interesting to hear
if it becomes a problem for someone.
```js
let map2 = new Map(map1.entries().filter((key, value) => key >= 0));
let map2 = new Map(map1.entries().map((key, value) => [key * 2, value * 2]));
entries() returns an iterator, not an array.
Rick
--
Dr. Axel Rauschmayer
axel at rauschma.de
rauschma.de
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20141001/542b1fbc/attachment.html>
Kevin Smith
2014-10-01 20:41:13 UTC
Permalink
Post by Axel Rauschmayer
```js
let map2 = new Map(map1.entries().filter((key, value) => key >= 0));
let map2 = new Map(map1.entries().map((key, value) => [key * 2, value * 2]));
entries() returns an iterator, not an array.
let map2 = new Map([...map1].map(([k, v]) => [k, v * 2])
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20141001/f5672ec9/attachment.html>
Brendan Eich
2014-10-01 20:42:31 UTC
Permalink
Post by Dmitry Soshnikov
entries() returns an iterator, not an array.
let map2 = new Map([...map1].map(([k, v]) => [k, v * 2])
Sweet!

/be
Rick Waldron
2014-10-01 20:50:46 UTC
Permalink
Post by Kevin Smith
Post by Axel Rauschmayer
```js
let map2 = new Map(map1.entries().filter((key, value) => key >= 0));
let map2 = new Map(map1.entries().map((key, value) => [key * 2, value * 2]));
entries() returns an iterator, not an array.
let map2 = new Map([...map1].map(([k, v]) => [k, v * 2])
Sorry for the almost-dup, didn't see this while I was typing.


Rick
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20141001/52e344d0/attachment.html>
Continue reading on narkive:
Loading...