On Jul 30, 2010, at 1:38 PM, Dmitry A. Soshnikov wrote:

> Another thing to mention regarding array comprehensions is /pattern matching/ 
> (in less common, but related to JS, case -- /destructing assignment/). 
> Currently, it's implemented in JS 1.7 in simple for/each-in loops:
> 
> for each ({a: x} in [{a: 10}, {a: 20}, {a: 30}]) {
>  alert(x); // 10, 20, 30
> }
> 
> Yes, it can be useful to pattern match {a: x} extracting a value of the "a" 
> property into the "x" variable. Array comprehensions of JS 1.7 in contrast 
> with a loop do not have such sugar:
> 
> let a = [x for each ({a: x} in [{a: 10}, {a: 20}, {a: 30}]) if (x > 2)]; // 
> SyntaxError

You don't need each to make that work in JS1.7:

js> let a = [x.a for each (x in [{a: 10}, {a: 20}, {a: 30}]) if (x.a > 2)];
js> a
[10, 20, 30]

Notice that you can use each in JS1.7 after for (E4X was in JS1.6 and up).

But you are quite right that we did not allow the same left-hand sides of 'in' 
in comprehensions as we did in equivalent loops:

js> let a = []; for each ({a: x} in [{a: 10}, {a: 20}, {a: 30}]) if (x > 2) 
a[a.length] = x; a
[10, 20, 30]

That is just a flaw in JS1.7, possibly even not a design flaw but an 
implementation bug (I honestly don't remember).

For Harmony, we do not propose to standardize |for each|. Instead, the 
iteration and array comprehensions proposals for Harmony (see the wiki) propose 
that programmers choose keys, values, items (properties), or other iteration 
protocols by saying what they mean more precisely on the right-hand side of 
'in':

for (k in keys(o)) ...
for (v in values(o)) ...
for ([k, v] in properties(o)) ... // Python's "items"

This seems better in TC39 members' views than adding ambiguous 'each' as a 
contextual keyword.


> Also using pattern matching, it's useful sometimes to filter needed values of 
> an array in the pattern matching parameter itself, but not using filter 
> section (actually, it's hard to use filter section to filter exactly values 
> which are not of the needed structure). For example (code on Erlang with its 
> list comprehensions):
> 
> List = [{1,2}, skip, {3,4}],
> 
> FilteredList = [X + Y || {X, Y} <- List].
> 
> Result is: [3, 7]. Pattern matching filtered atom `skip' and took only {X, Y} 
> structures (1st and 3rd elements in list) extracting values into the X and Y 
> variables.
> 
> This is useful feature, I was needed it on practice. In contrast lists:map + 
> lists:filter (that's desugared list comprehensions) cannot handle this case, 
> because for the `skip' atom will be `bad match' error and we can't map a list 
> the same elegant as with list comprehension:
> 
> List = [{1,2}, skip, {3,4}],
> lists:map(fun({X, Y}) -> X + Y end, List).  % bad_match error for the second 
> element - `skip' atom
> 
> Syntactically JavaScript has similar construction, but semantically result 
> differs:
> 
> for each ({a: x, b: y} in [{a: 10, b: 20}, "skip", {a: 30, b: 40}]) {
>  alert(x, y); // 10, 20 | undefined, undefined | 30, 40
> }

Syntax aside, destructuring in JS is not irrefutable match. It is simply 
shorthand for assigning (and declaring) variables whose names are supplied in 
the "value" position in array and object initialisers, where the assigned 
values for these variables come from property values named by the keys in the 
corresponding "key" position. Failure in the sense of pulling undefined out of 
a non-existing property *is* an option:

js> let [x, y] = {not_an_array: 42};
js> x
js> y

and of course if you dig deeper, you can get failure dereferencing undefined:

js> let [x, [y, z]] = {nor_here: 99};
typein:20: TypeError: (void 0) is undefined

(SpiderMonkey uses (void 0) not undefined since prior to ES5, the global 
property named undefined was writable and could be spoofed.)


> String "skip" isn't pattern matched, but the object is created with 
> `undefined' values for x and y. And again for array comprehensions this shows 
> SyntaxError (that, for consistency with for/each-in loop should not).

I agree that some fairly common JS use-cases want irrefutable match. Dave 
Herman pointed out how the SpiderMonkey and Rhino extended catch syntax, 
guarded catches, is a kind of matching:

try {
   throw random_exception_generator();
} catch (e if typeof e == "number") {
   ...
} catch (e if typeof e == "string") {
   ...
} catch {
  // default case, if you forget it e will be rethrown for you
}

This was proposed for ES3 but not accepted. It has the advantage cited by the 
comment in the default catch clause.

Perhaps there's a generalization of such guards, which could re-use the 
initialiser-derived pattern syntax from destructuring, and which would provide 
irrefutable match as a primitive with good compositionality.

/be
_______________________________________________
es-discuss mailing list
[email protected]
https://mail.mozilla.org/listinfo/es-discuss

Reply via email to