Array comprehensions shorter syntax (?)
2011/5/29 Dmitry A. Soshnikov <dmitry.soshnikov at gmail.com>
Hi,
Don't get this proposal as a bikesheding, just an idea in case if arrow functions will win the block-functions.
What about to make a sugar for Array comprehensions based also on arrow syntax? The same as in Erlang:
let data = [1, 2, 3, 4, 5];
let squares = [x * x | x <- data, x > 3]; // [16, 25]
Basically you are proposing bring in Haskell's syntax for comprehensions
- Of course it's assumed that we can destructure elements in such array comprehensions:
let users = [ {name: "Alex", age: 31}, {name: "John", age: 25}, {name: "Mark", age: 33} ];
let names = [name | {name, age} <- users, age > 30]; // ["Alex", "Mark"]
This form of located flat pattern matching is good.
Jose.
On 29.05.2011 16:18, Jose Antonio Perez wrote:
2011/5/29 Dmitry A. Soshnikov <dmitry.soshnikov at gmail.com <mailto:dmitry.soshnikov at gmail.com>>
Hi, Don't get this proposal as a bikesheding, just an idea in case if arrow functions will win the block-functions. What about to make a sugar for Array comprehensions based also on arrow syntax? The same as in Erlang: let data = [1, 2, 3, 4, 5]; let squares = [x * x | x <- data, x > 3]; // [16, 25]
Basically you are proposing bring in Haskell's syntax for comprehensions
Yeah, here I found a list of other syntax, including math root from sets theory: en.wikipedia.org/wiki/List_comprehension And taking into account to accept arrow functions, I think this Erlang's/Haskell's <- notation fits nice and avoids the long for-loop and if-statement constructions. Besides, the ability to traverse directly over values without specifying it explicitly via generator IMO is also a good thing.
3. Of course it's assumed that we can destructure elements in such array comprehensions: let users = [ {name: "Alex", age: 31}, {name: "John", age: 25}, {name: "Mark", age: 33} ]; let names = [name | {name, age} <- users, age > 30]; // ["Alex", "Mark"]
This form of located flat pattern matching is good.
It should follow from the shorter notation of destructuring, and yeah, I also think it's good.
Dmitry.
An alternative syntax, which I tend to prefer, is a LINQ-like syntax. Please note that LINQ don’t produce an array, but an enumerable. It means that if the result is never used, the query is never executed (and if only the first result is used, the query only executed itself to that point, and not further) let users = [ .... ]
let names = ( foreach user in users select user.name if user.age > 30 );
let newClientsSpendings = ( foreach user in users let spendings = getSpendingsById(user.id) select spendings if (user.isNewClient && spendings.length!=0) ).merge()
But I don’t know if we really need something special (ie: a new syntax) for that kind of things. Using a short function notation + paren-free loops could do the trick pretty well : let newClientsSpendings = @{ foreach user in users { if !user.isNewClient: continue; let spendings = getSpendingsById(user.id); if spendings.length!=0: yield spendings; } }().merge();
For your “squares” sample, that gives : let squares = @{ foreach x in data { if x<3 : yield x*x } }(); It’s longer, but it’s aslo more extensible... and it don’t introduce anything new to ES Harmony.
Please note that your syntax has to be rejected because we can’t get intellisense working since the variable is declared after the expression.
Best , François
From: Jose Antonio Perez Sent: Sunday, May 29, 2011 2:18 PM To: es-discuss at mozilla.org Subject: Re: Array comprehensions shorter syntax (?) 2011/5/29 Dmitry A. Soshnikov <dmitry.soshnikov at gmail.com>
Hi,
Don't get this proposal as a bikesheding, just an idea in case if arrow functions will win the block-functions.
What about to make a sugar for Array comprehensions based also on arrow syntax? The same as in Erlang:
let data = [1, 2, 3, 4, 5];
let squares = [x * x | x <- data, x > 3]; // [16, 25]
Basically you are proposing bring in Haskell's syntax for comprehensions
- Of course it's assumed that we can destructure elements in such array comprehensions:
let users = [ {name: "Alex", age: 31}, {name: "John", age: 25}, {name: "Mark", age: 33} ];
let names = [name | {name, age} <- users, age > 30]; // ["Alex", "Mark"]
This form of located flat pattern matching is good.
Jose.
On 29.05.2011 17:45, François REMY wrote:
An alternative syntax, which I tend to prefer, is a LINQ-like syntax.
As I noted from the beginning I wouldn't like to turn the topic into bikesheding with all possible syntax constructions for comprehensions (once again, there many interesting in many languages: en.wikipedia.org/wiki/List_comprehension); the idea was to adopt arrow symbol for that and only if arrow functions will be accepted. And the main thing is to write it shorted, after all we already have normal powerful list comprehensions borrowed from Python.
Please note that LINQ don’t produce an array, but an enumerable. It means that if the result is never used, the query is never executed (and if only the first result is used, the query only executed itself to that point, and not further)
let users = [ .... ] let names = ( foreach user in users select user.name if user.age > 30 ); let newClientsSpendings = ( foreach user in users let spendings = getSpendingsById(user.id) select spendings if (user.isNewClient && spendings.length!=0) ).merge()
Yeah, and ES also supports them. It's called a generator expression; in this proposal it would look like:
let squares = (x * x | x < data, x > 5);
i.e. parens instead of brackets. In this way we create a generator
object and behind the scene call its next
method.
But I don’t know if we really need something special (ie: a new syntax) for that kind of things. Using a short function notation + paren-free loops could do the trick pretty well :
Perhaps, I don't cancel it. Let's see:
// full notation let squares = [x * x for (x in values(data)) if (x > 5)]
// paren-free let squares = [x * x for x in values(data) if x > 5]
// set-builder notation let squares = [x * x | x <- data, x > 5 ]
Yes, I like paren-free notation as well. So, it's really just a proposal, it can be accepted or not, depending on needs and complexity of implementation. Moreover, it's syntactically incomparable.
let newClientsSpendings = @{ foreach user in users { if !user.isNewClient: continue; let spendings = getSpendingsById(user.id); if spendings.length!=0: yield spendings; } }().merge();
For your “squares” sample, that gives :
let squares = @{ foreach x in data { if x<3 : yield x*x } }();
It’s longer,
That's it, exactly. We always looking for a shorter sugar. Though, the main thing that the sugar shouldn't be cryptic at the same time. Probably Erlang's list comprehensions are cryptic for someone, but again, taking into account arrow-functions, seems arrow-comprehensions aren't so cryptic.
but it’s aslo more extensible... and it don’t introduce anything new to ES Harmony.
As well as current array-comprehensions; though, paren-free are better IMO.
Dmitry.
2011/5/29 Dmitry A. Soshnikov <dmitry.soshnikov at gmail.com>
That's it, exactly. We always looking for a shorter sugar. Though, the main
thing that the sugar shouldn't be cryptic at the same time. Probably Erlang's list comprehensions are cryptic for someone, but again, taking into account arrow-functions, seems arrow-comprehensions aren't so cryptic.
I think that your proposal based in Haskell's syntax is natural,clear and easy for the user. A EBNF grammar for it:
ListComprehension : '[' Expression '|' IterableOrFilter (,IterableOrFilter)+ ']' IterableOrFilter: Id '<-' ArrayOrGenerator | BooleanFilter
The problem is the LL(1) conflict with Array Literal, but that doesn't matter much, right? ;)
Jose.
Errata: + must be *
ListComprehension : '[' Expression '|' IterableOrFilter (,IterableOrFilter)* ']' IterableOrFilter: Id '<-' ArrayOrGenerator | BooleanFilter
Jose.
On May 29, 2011, at 7:17 AM, Jose Antonio Perez wrote:
2011/5/29 Dmitry A. Soshnikov <dmitry.soshnikov at gmail.com>
That's it, exactly. We always looking for a shorter sugar. Though, the main thing that the sugar shouldn't be cryptic at the same time. Probably Erlang's list comprehensions are cryptic for someone, but again, taking into account arrow-functions, seems arrow-comprehensions aren't so cryptic.
I think that your proposal based in Haskell's syntax is natural,clear and easy for the user. A EBNF grammar for it:
ListComprehension : '[' Expression '|' IterableOrFilter (,IterableOrFilter)+ ']' IterableOrFilter: Id '<-' ArrayOrGenerator | BooleanFilter
The problem is the LL(1) conflict with Array Literal, but that doesn't matter much, right? ;)
No, it's a big incompatibility that could result in no early error, rather a shift in meaning.
We're not doing those without a really good reason. We don't have one here.
People seem to forget that | is an operator in the language. In strawman:block_lambda_revival I worked around the lesser problem of parameter default values by requiring any default value using bitwise-or to be parenthesized:
blam = {|x = (y | z)| x*x};
But block-lambdas do not use [] where | may be an operator in a single element initialiser expression, so there's no further incompatibility.
Not so with square brackets.
Array comprehensions using for-in or the replacement (paren-free, probably "for-of") Harmony syntax are in for ES.next.
On 29.05.2011 23:29, Brendan Eich wrote:
On May 29, 2011, at 7:17 AM, Jose Antonio Perez wrote:
2011/5/29 Dmitry A. Soshnikov <dmitry.soshnikov at gmail.com <mailto:dmitry.soshnikov at gmail.com>>
That's it, exactly. We always looking for a shorter sugar. Though, the main thing that the sugar shouldn't be cryptic at the same time. Probably Erlang's list comprehensions are cryptic for someone, but again, taking into account arrow-functions, seems arrow-comprehensions aren't so cryptic.
I think that your proposal based in Haskell's syntax is natural,clear and easy for the user. A EBNF grammar for it:
ListComprehension : '[' Expression '|' IterableOrFilter (,IterableOrFilter)+ ']' IterableOrFilter: Id '<-' ArrayOrGenerator | BooleanFilter
The problem is the LL(1) conflict with Array Literal, but that doesn't matter much, right? ;)
No, it's a big incompatibility that could result in no early error, rather a shift in meaning.
We're not doing those without a really good reason. We don't have one here.
OK; it has been just proposed.
Basically I think paren-free "for-in + if" scheme is fine, just though it can be more sugared with the arrow. But, there's no a big need, I agree.
P.S.: though, btw, IIRC, you said the same when an year ago I proposed arrow functions or Ruby's blocks and they were refused because of grammar reasons; today we want them to standardize ;) I mean, perhaps what seems not so needed and complicated to implement at first glace, later can become interesting and useful.
Dmitry.
On May 29, 2011, at 12:52 PM, Dmitry A. Soshnikov wrote:
P.S.: though, btw, IIRC, you said the same when an year ago I proposed arrow functions or Ruby's blocks and they were refused because of grammar reasons; today we want them to standardize ;) I mean, perhaps what seems not so needed and complicated to implement at first glace, later can become interesting and useful.
Block-lambdas face an uphill battle on several grounds, semantic as well as syntactic.
Arrow functions are "just syntax", the only issues are the ones listed on the strawman: ArrowFormalParameters vs. AssignmentExpression ambiguity in LR(k), and the remaining block vs. object literal disambiguation problem.
Neither is in ES.next. I'd say arrow functions are closer to getting to Harmony, with some on the committee wanting a "late save" for ES.next if we can get the grammar issues resolved.
On 05/29/11 07:00, Dmitry A. Soshnikov wrote:
Yeah, and ES also supports them. It's called a generator expression; in this proposal it would look like:
let squares = (x * x | x < data, x > 5);
Ahem, that's already a parenthesized comma expression with operands
x * x | x < data
and
x > 5.
Remember that | is a perfectly fine binary operator.
Waldemar
PS. Block lambdas don't suffer from this problem because neither | nor || is a valid unary prefix operator, so they can be made syntactically unambiguous.
On 01.06.2011 3:06, Waldemar Horwat wrote:
On 05/29/11 07:00, Dmitry A. Soshnikov wrote:
Yeah, and ES also supports them. It's called a generator expression; in this proposal it would look like:
let squares = (x * x | x < data, x > 5);
Ahem, that's already a parenthesized comma expression with operands
Yes, true (haha, this one is even a correct ES3 expression :D, and not
only with the typo < data
, but with the arrow: <- data
). Though, I
just was excited with arrow functions so proposed Erlang's list
comprehensions (Erlang also uses arrows for functions btw, though,
that's said, it uses two pipes [X*X || X <- Data, X > 5]). Regarding
comma, I though, perhaps it would be possible to treat it correctly in this context -- well, no means no.
P.S.: another question I have -- is it worth and makes sense to raise a topic on considering/standardizing the pattern matching (Dave's proposal)? strawman:pattern_matching Brendan mentioned on Twitter that it's "too late" (?), but IMO this proposal is much more interesting and needed for the lang than e.g. WeakMaps (which of course are also useful stuff, but not so powerful addition as pattern-matching would).
Dmitry.
P.S.: another question I have -- is it worth and makes sense to raise a topic on considering/standardizing the pattern matching (Dave's proposal)? strawman:pattern_matching Brendan mentioned on Twitter that it's "too late" (?), but IMO this proposal is much more interesting and needed for the lang than e.g. WeakMaps (which of course are also useful stuff, but not so powerful addition as pattern-matching would).
Not really. I want to see a pattern matching form for JS as much as anyone (and I did propose it) but it hasn't had enough time to get refined and promoted to the next stage. I only first proposed it a couple months ago, so it needs more time to mature.
We have a very large set of features in official Harmony status for ES.next, and we are now beginning the refinement phase -- the meeting last week was the cutoff for proposals to get promoted to Harmony status. But don't despair! We aren't going to stop working on the next round of proposals. We'll keep the pipeline full with strawmen for ES.next.next even as we refine the ES.next proposals. As Brendan said at the last meeting, TC39 is superscalar. ;-)
And BTW, comparing apples and oranges like pattern matching and weak maps is not really meaningful. In fact, if either one is more powerful it's weak maps; there's no way to simulate them in ES5, whereas pattern matching is just syntactic convenience... albeit totally awesome convenience. :)
On 01.06.2011 10:57, David Herman wrote:
P.S.: another question I have -- is it worth and makes sense to raise a topic on considering/standardizing the pattern matching (Dave's proposal)? strawman:pattern_matching Brendan mentioned on Twitter that it's "too late" (?), but IMO this proposal is much more interesting and needed for the lang than e.g. WeakMaps (which of course are also useful stuff, but not so powerful addition as pattern-matching would). Not really. I want to see a pattern matching form for JS as much as anyone (and I did propose it) but it hasn't had enough time to get refined and promoted to the next stage. I only first proposed it a couple months ago, so it needs more time to mature.
I understand, and actually I mentioned/proposed them here also an year ago and said that it would be great to have p-matching in JS.
We have a very large set of features in official Harmony status for ES.next, and we are now beginning the refinement phase -- the meeting last week was the cutoff for proposals to get promoted to Harmony status. But don't despair! We aren't going to stop working on the next round of proposals. We'll keep the pipeline full with strawmen for ES.next.next even as we refine the ES.next proposals. As Brendan said at the last meeting, TC39 is superscalar. ;-)
Yeah, glad to hear it.
And BTW, comparing apples and oranges like pattern matching and weak maps is not really meaningful. In fact, if either one is more powerful it's weak maps; there's no way to simulate them in ES5, whereas pattern matching is just syntactic convenience... albeit totally awesome convenience. :)
Ah, come on, of course I didn't compare them apples-to-apples. Just said that it's more likely that some elegant and powerful syntactic construction/sugar will be used more often than use-cases with WeakMaps and it turns out that much more time is given to WeakMaps than to p-matching.
Dmitry.
On Jun 1, 2011, at 12:21 AM, Dmitry A. Soshnikov wrote:
Ah, come on, of course I didn't compare them apples-to-apples. Just said that it's more likely that some elegant and powerful syntactic construction/sugar will be used more often than use-cases with WeakMaps and it turns out that much more time is given to WeakMaps than to p-matching.
Actually, you wrote "IMO this proposal is much more interesting and needed for the lang than e.g. WeakMaps", where "needed" sets up a false dilemma. There's "needed" and then there is "wanted". WeakMaps are needed in cases where you can't simulate them with existing facilities. Pattern-matching is wanted and you could argue "need" because of cost of implementing a matching library and calling functions instead of writing statements, but that is "just syntax".
As to which would is interesting or which would be used more, you're arguably right -- but that doesn't mean we face an either/or, or that proposals were done out of order...
Don't get this proposal as a bikesheding, just an idea in case if arrow functions will win the block-functions.
What about to make a sugar for Array comprehensions based also on arrow syntax? The same as in Erlang:
let data = [1, 2, 3, 4, 5];
let squares = [x * x | x <- data, x > 3]; // [16, 25]
Which basically is the:
let squares = [x * x for (x in values(data)) if (x > 3)]; // [16, 25]
A semantic (important) difference is that in the later case we use values(...) generator function to get the values. In case of shorter syntax, the array is assumed to be traversed over values by default.
P.S.:
S = { 2 · x | x N }
Erlang: Squares = [X * X || X <- Data, X > 3];
let users = [ {name: "Alex", age: 31}, {name: "John", age: 25}, {name: "Mark", age: 33} ];
let names = [name | {name, age} <- users, age > 30]; // ["Alex", "Mark"]
3.1. ...which can lead to real pattern-matching Dave's proposal to be included ;) "switch-on-steroids" is very powerful thing.
Dmitry.