Comprehensions, Where Art Thou?

Andy Wingo wingo at igalia.com
Mon Aug 11 06:49:05 PDT 2014


On Mon 11 Aug 2014 15:03, Kevin Smith <zenparsing at gmail.com> writes:

> I still don't quite understand the motivation for removing
> comprehensions from ES6. They are a well-loved feature of Python and I
> have every reason to believe that they would be well-liked in
> Javascript. For simplicity, we could certainly leave off array
> comprehensions, while keeping generator comprehensions, since you can
> build a list from a comprehension using spread syntax:
>
> var list = [...(for x of iterable if somePredicate(x)];
>
> Can someone explain more clearly the motivation for removing this
> feature?

I can give it a go ;)

We can start with generator comprehensions, which are a specific way of
constructing lazy sequences.  It's a syntactic feature, which has a cost
on the language level -- makes you wonder, can this be done without
syntax?

Turns out, yes: you can do it by constructing a graph of combinators,
with normal method syntax.  So you can do:

  var sequence = iterable.lazy().if(somePredicate)

where lazy and if might be:

  Iterable.prototype.lazy = function* () {
    for (var x of this) yield x;
  }
  Iterable.prototype.if = function* (pred) {
    for (var x of this) if pred(x) yield x;
  }

The neat thing about this is that it's extensible -- it privileges the
users to the same degree as the language designers.  If there's a new
pipeline-like operator, it can be polyfilled on Iterator.prototype.

It also allows for look-alike APIs for other kinds of traversal: for
example, the same syntax could map over parallel arrays in parallel.

There is also an argument that a more combinator-like syntax can better
support pushing values into iterators, a concept described as
"Observables" rather than "Iterables", but I don't remember the details
and can't describe it adequately.  The end point of all these arguments
is that a more combinator-like, linq-like API is as expressive and more
extensible than a hard-coded syntactic facility.

I love _array_ comprehensions -- they are light-weight both from a
performance and syntactic point of view.  You can quickly build up short
arrays, and at least in SpiderMonkey you get a lovely ionified loop.
Generator comprehensions were unlikely to be as light-weight from a perf
perspective, but the use of () instead of [] was a cute pun, and could
have gently pushed people in the lazy direction.  But then, if
combinators are better for the lazy case, then the case for eager
traversal is less strong -- and so we end up in current situation of
punting on both of them for now.

In many ways it's too bad -- Array.map is usually much worse than a
for-of and Array.push, which is what an array comprehension is, and
using combinators means you can't yield in the comprehended expression
("[for (x of y) ... yield ...]").  But the extensibility benefit of
combinators on Iterator.prototype will probably make deferal of
comprehensions a net win for many programmers.

Andy


More information about the es-discuss mailing list