A reprieve for ES6 comprehensions

# Andy Wingo (6 years ago)

dherman has argued that comprehensions should be removed from the ES6 draft:

speakerdeck.com/dherman/a-better-future-for-comprehensions

The argument is that the existing comprehension mechanism is incompatible with future extensions, notably parallelism.

I am not sure I buy this argument. (Disclaimer: I did the work to adapt SpiderMonkey's comprehensions to ES6 syntax/semantics, and have a colleague that was working on doing the same in V8. This does give me some familiarity with the topic, but may blind me to alternatives.)

My reasoning is (1) that the difference between array comprehensions -- [for x of y z] -- and generator comprehensions -- (for x of y z) -- is essential, not incidental. (2) A unified syntax seems to me to be incompatible with the ES6 external iterator design. Finally, (3) the current draft syntax both hangs very well with the rest of ES6, and it is not incompatible with combinator-style extension.

1: Essential differences

Array comprehensions are eager. Generator comprehensions are lazy. Array comprehensions desugar into a do expression à la ES7. Generator comprehensions desugar into an IIGFE (immediately-invoked generator function expression) -- sorta, anyway. (Generator function desugaring is not exact due to arguments/this/etc scoping and properties on the generator function.)

This is an essential difference in the use of the iterator, and so it makes sense to distinguish these at the source level as well.

2: Refactoring?

David brings up the argument that with parallel comprehensions, we then would have three ways of iterating over a sequence, and that is reason to see if we can refactor. This is a good point. However I don't see it working out, because of (1). The only way I could see it working is if comprehensions became internal iterators: for example, the body of the comprehension would be wrapped in an arrow function or something and that function would get invoked in the different contexts. This is possible to optimize of course -- anything is possible -- but it is going to really be a dog compared to what is currently in SpiderMonkey (for example). You are relying on the compiler to inline a whole stack of functions. There are many hazards here to getting good perf, and we'd be baking that choice into the language.

3: Comprehensions match ES6

Comprehensions match ES6 very well as it is, without considering the possible addition of parallelism. They even match up well if we add the other dimension of asynchronous comprehensions -- i.e. [for x of await y z], (for x of await y z). I don't think it's unreasonable to expect PJS to also extend the syntax in this way -- we end up with 2³ squares in our "matrix" -- eager vs lazy, sync vs async, parallel vs not -- and that seems fine to me.

Finally the idea that iterators should have a combinator API like y.if(pred).butNot(bar) or whatever -- that is totally a doable thing now with the proposed addition of a common Iterator prototype, and it meshes well both with for-of (which no one has proposed removing, and the same arguments apply) and with comprehensions as they are in the draft.

In summary, I think Dave already did a good job with comprehensions and they should stay in :)

,

Andy

# Andy Wingo (6 years ago)

A final point while I am thinking about it:

On Fri 06 Jun 2014 13:57, Andy Wingo <wingo at igalia.com> writes:

1: Essential differences

Array comprehensions are eager. Generator comprehensions are lazy.

This difference shows up here, for example:

  Q.async(function*(){
    return [for (x of y) yield x]
  })()

You can yield in the body of an ES6 array comprehension, but not in the body of a generator comprehension. This isn't something you can do with an internal iterator design.

# C. Scott Ananian (6 years ago)

On Fri, Jun 6, 2014 at 7:57 AM, Andy Wingo <wingo at igalia.com> wrote:

Comprehensions match ES6 very well as it is, without considering the possible addition of parallelism. They even match up well if we add the other dimension of asynchronous comprehensions -- i.e. [for x of await y z], (for x of await y z). I don't think it's unreasonable to expect PJS to also extend the syntax in this way -- we end up with 2³ squares in our "matrix" -- eager vs lazy, sync vs async, parallel vs not -- and that seems fine to me.

Not that another alternative is transactional parallelism, where await ends up marking atomicity boundaries. Separate iterations of [for x of await y z] can execute in parallel, protected by a transaction mechanism (STM/HTM/hybrid/etc) to ensure that the semantics match a serial execution. This is in many ways a better match for JavaScript's existing concurrency model -- which programmers seem to prefer over an explicit locks+threads model.

So, for one, I wouldn't want to put the PJS cart before the JS horse.

# Tab Atkins Jr. (6 years ago)

On Fri, Jun 6, 2014 at 1:57 PM, Andy Wingo <wingo at igalia.com> wrote:

1: Essential differences

Array comprehensions are eager. Generator comprehensions are lazy. Array comprehensions desugar into a do expression à la ES7. Generator comprehensions desugar into an IIGFE (immediately-invoked generator function expression) -- sorta, anyway. (Generator function desugaring is not exact due to arguments/this/etc scoping and properties on the generator function.)

This is an essential difference in the use of the iterator, and so it makes sense to distinguish these at the source level as well.

I'm not sure I understand. Is this an argument against Python's use of similar syntaxes as well? That is, do you also disagree with Python's high similarity between array comprehensions and generator comprehensions?

# Brendan Eich (6 years ago)

No, the problem is there's no way to do a generator comprehension in (what we saw of) the new syntax.

# Tab Atkins Jr. (6 years ago)

Ah, getting context from just the slide deck clearly puts me at a disadvantage. Am I to understand that the three slides in a row that have a "for()...for()...if()...{x, y}" syntax are the proposed syntax?

# Andy Wingo (6 years ago)

On Sat 07 Jun 2014 21:28, "Tab Atkins Jr." <jackalmage at gmail.com> writes:

do you also disagree with Python's high similarity between array comprehensions and generator comprehensions?

It seems that we have lost context on both sides, both in David's slides and in my note ;) One of David's thoughts was that somehow the syntax could and should be unified, but we don't know how to do that right now, and so we should put off comprehensions until later.

I was suggesting that actually they can't be unified, and that comprehensions are syntactically fine as they are, and compatible with extensions that are under discussion. But David knows the tradeoffs better than I do.

Andy