Array comprehensions with Spread operator

# monolithed (10 years ago)
let x = [0, 1, 2];
let y = [3, 4, 5];


// Expected
[ for (i of [x, y]) ...i ];


// Reality
Array.prototype.concat(...[ for (x of [x, y]) i ]);


// Result
[0, 1, 2, 3, 4, 5]

Is there any discussion on this subject?

# Jeremy Martin (10 years ago)

Why not just [...x, ...y]?

# Tab Atkins Jr. (10 years ago)

On Wed, Apr 15, 2015 at 9:23 AM, monolithed <monolithed at gmail.com> wrote:

Is there any discussion on this subject?

+1. Right now you can insert 1 (default) or 0 (with a filter) elements per iteration, but you can't insert 2+. This lack in Python has forced me to unwrap several comprehensions into explicit loops, which is annoying.

The general solution to this is monadic, which has been inconclusively discussed in the past, but the use of the spread operator for this case is very compelling, imo. It feels natural.

# Tab Atkins Jr. (10 years ago)

On Wed, Apr 15, 2015 at 9:31 AM, Jeremy Martin <jmar777 at gmail.com> wrote:

Why not just [...x, ...y]?

Obviously that's a solution to the trivial example that monolithed provided, but it's not a solution to the more general problem he's alluding to, where you're doing a comprehension and want to insert two or more elements into the result in a single iteration.

# Jeremy Martin (10 years ago)

Thanks, I gathered so after your response. This is why 99% of the time I wait for at least one other person to reply first, and why I should wait the remaining 1%... :)

# Axel Rauschmayer (10 years ago)

It’s important to keep in mind that there is no official version of array comprehensions, at the moment. So that is something to keep in mind whenever they are added to the language.

I’d probably implement flatMap() and use it if I ever needed to do something like this.

# Mark S. Miller (10 years ago)

Dave Herman did an excellent presentation at one of the TC39 meetings that convinced us all to drop comprehension syntax from ES6. I remember it surprised us all including, earlier Dave, which led to his presentation. Anyone have a link?

The arguments that I remember as most significant are a) When you look as how much syntactic convenience comprehensions provide above explicit calls to higher-order operations (assuming we have .map, .filter, as well as the currently absent .flatMap) and arrow functions, the answer is not much. b) When your comprehensions involve only those ho operations, fine. But as soon as you try to mix in some other ho operation, such as e.g., a reduce, if you started with a comprehension you're gonna create a mess. OTOH, if you were starting with code explicitly calling ho operations, then there's nothing confusing or unnatural mixing in some others.

IMO, #a was necessary to convince me. YAGNI. Given #a, #b was sufficient.

Dave, if I've misrepresented you in any way, please correct. Thanks.

# Axel Rauschmayer (10 years ago)

Right, map() et al. plus arrow functions come pretty close to the syntactic elegance of comprehensions.

# liorean (10 years ago)

On 15 April 2015 at 18:36, Jeremy Martin <jmar777 at gmail.com> wrote:

Thanks, I gathered so after your response. This is why 99% of the time I wait for at least one other person to reply first, and why I should wait the remaining 1%... :)

Heck no! You asking that question clarified the problem statement and highlit that the example wasn't the perfect example code. Always good to get assumptions checked and explanations of why alternative solutions to specific use cases aren't what the issue is about.

For the record, what I can see, the code in the example is broken in another way as well, the «i» in the reality example is not the variable name used in the for-of.

Really, for these cases, I'd probably do something like a chained reduce, somewhat like this:

> let x=[0,1,2],y=[3,4,5],z=[6,7,8];
> let flatten=(p,c)=>([...p,c]);
> let add=(p,c)=>((+p)+(+c));
> let concat=(p,c)=>(''+p+c);
> let multireduce=(f,r)=>(a,...b)=>(a === void 0)?r:multireduce(f,a.reduce(f,r))(...b);
> multireduce(flatten,[])(x,y,z);
[0, 1, 2, 3, 4, 5, 6, 7, 8]
> multireduce(add,0)(x,y,z);
36
> multireduce(concat,'')(x,y,z);
"012345678"

you could even edit that «multireduce» so that the no-initval variants work, though I'm not bored enough to continue this exercise any longer.

# monolithed (10 years ago)

@ liorean,

For the record, what I can see, the code in the example is broken in another way as well, the «i» in the reality example is not the variable name used in the for-of.

You're right the second example is broken. I can not edit my messages, sorry.

Array.prototype.concat(...[ for (i of [x, y]) i ]);

@Mark S. Miller,

Dave Herman did an excellent presentation at one of the TC39 meetings that convinced us all to drop comprehension syntax from ES6. I remember it surprised us all including, earlier Dave, which led to his presentation. Anyone have a link?

The arguments that I remember as most significant are a) When you look as how much syntactic convenience comprehensions provide above explicit calls to higher-order operations (assuming we have .map, .filter, as well as the currently absent .flatMap) and arrow functions, the answer is not much. b) When your comprehensions involve only those ho operations, fine. But as soon as you try to mix in some other ho operation, such as e.g., a reduce, if you started with a comprehension you're gonna create a mess. OTOH, if you were starting with code explicitly calling ho operations, then there's nothing confusing or unnatural mixing in some others.

I can not agree with this approach, many languages have this functionality

For example:

var files = function (data) {
    let result = [];

    for (let path of data) {
       result.push(...fs.readdirSync(path));
    }

    return result;
}

I believe that this code is awkward and not as beautiful as the possible alternatives:

var files = function (data) {
   return [ for (path of data) ...fs.readdirSync(path) ];
}

or

var files = function (data) {
   return data.map(path => ...fs.readdirSync(path));
}
# Mark S. Miller (10 years ago)

On Wed, Apr 15, 2015 at 1:11 PM, monolithed <monolithed at gmail.com> wrote:

I can not agree with this approach, many languages have this functionality

Which is why we were all surprised.

For example:

var files = function (data) {
let result = [];

for (let path of data) {
result.push(...fs.readdirSync(path));
}

return result;
}

I believe that this code is awkward and not as beautiful as the possible alternatives:

var files = function (data) {
   return [ for (path of data) ...fs.readdirSync(path) ];
}

or

var files = function (data) {
   return data.map(path => ...fs.readdirSync(path));
}

or, if we had flatMap

var files = function (data) {
   return data.flatMap(path => fs.readdirSync(path));
}

which looks as good as the comprehension and is clearer -- especially as a starting point for refactorings.

So should we add flatMap? Yeah, probably.

# Rick Waldron (10 years ago)

On Wed, Apr 15, 2015 at 2:27 PM Mark S. Miller <erights at google.com> wrote:

Dave Herman did an excellent presentation at one of the TC39 meetings that convinced us all to drop comprehension syntax from ES6. I remember it surprised us all including, earlier Dave, which led to his presentation. Anyone have a link?

Yes.

Here are the notes from the discussion: rwaldron/tc39-notes/blob/c61f48cea5f2339a1ec65ca89827c8cff170779b/es6/2014-06/jun-5.md#generator-comprehensions-slides-plz

And here is the slide deck: speakerdeck.com/dherman/a-better-future-for-comprehensions