Introduction, comprehensions beyond arrays

Tab Atkins Jr. jackalmage at gmail.com
Sat May 11 22:34:16 PDT 2013


On Sat, May 11, 2013 at 1:32 PM, Mike Stay <metaweta at gmail.com> wrote:
> On Fri, May 10, 2013 at 2:34 PM, Brendan Eich <brendan at mozilla.com> wrote:
>> So, just to be clear, now the issue isn't that comprehensions make arrays,
>> it's that iteration isn't X. What is X? Quiz rule: don't say "Monad" or
>> "Monadic" in your answer. :-|
>
> Actually, if we could provide a different flatten operation, that
> would suffice, since the iterator could simply provide a single value
> of the appropriate class.  For example, given an iterator that
> produced integers, we could do matrix multiplication if flatten summed
> all the elements of the inner lists instead of concatenating the inner
> lists.
>
> If
>    [for (x of foo) for (y of bar(x)) for (z of baz(x,y)) expr(x, y, z)]
> iterated as usual over foo, bar(x) and baz(x,y) but used the flatten()
> method of bar(x) on the innermost list of lists and the flatten()
> method of foo on the outermost list of lists, that would do everything
> I was hoping for.
>
> In particular, the expression
>
> [ for (w of obj)
>   for (x of foo(w))
>   for (y of bar(w,x))
>   for (z of baz(w,x,y))
>   if (cond(w,x,y,z))
>   expr(w,x,y,z)
> ]
>
> would translate as
>
> let resultObj = [];
> let onceObj = obj;
> for (let w of onceObj) {
>     let resultFoo = [];
>     let onceFoo = foo(w);
>     for (let x of onceFoo) {
>         let resultBar = [];
>         let onceBar = bar(w,x);
>         for (let y of onceBar) {
>             let resultBaz = [];
>             let onceBaz = baz(w,x,y);
>             for (let z of onceBaz) {
>                 if (cond(w,x,y,z)) {
>                     resultBaz.push(expr(w,x,y,z));
>                 }
>             }
>             resultBar.push(resultBaz);
>         }
>         resultBar = (onceBar.flatten || [].flatten).call(resultBar);
>         resultFoo.push(resultBar);
>     }
>     resultFoo = (onceFoo.flatten || [].flatten).call(resultFoo);
>     resultObj.push(resultFoo);
> }
> resultObj = (onceObj.flatten || [].flatten).call(resultObj);
>
> No change to the syntax, no change to the iterator semantics, and a
> compatible change to the flattening semantics (assuming the existence
> of Array.prototype.flatten).

In addition to what Brendan said about the cost of actually generating
nested arrays in the common case, I don't think this desugaring does
what you want - if you have only a single for-of, you'll just get an
array back out, without a chance to call the custom flatten on it.
You can't handle this generically, either, because the innermost
for-of is handled as a map rather than a flatMap. You really do need
to go monadic all the way, and as Brendan said, that's too much cost
for the common case of Arrays.

Let's just add do-expressions, or something similar, in the future.
We can leave list comprehensions to their array specialization for
now.

~TJ


More information about the es-discuss mailing list