(x) => {foo: bar}
In the implementations I checked, this is actually allowed, but it's
parsed as a label instead of what you may expect at first glance (an object).
For it to be a concise body, you need to change it to let f = (x) => ({foo: bar});
. Otherwise, it's like a regular function body.
this seems like a footgun and has tripped people up in the wild twitter.com/jankrems/status/544645776518184960
See
strawman:block_vs_object_literal
This could be done, but not for ES6. Sorry I didn't push this harder, earlier.
What do you think the chances of this are in ES7+? That is, how backward-compatible is this change? The linked strawman doesn't seem to touch on () => { foo: bar }
as a back-compat hazard. Do you feel hopeful?
Domenic Denicola wrote:
What do you think the chances of this are in ES7+? That is, how backward-compatible is this change? The linked strawman doesn't seem to touch on
() => { foo: bar }
as a back-compat hazard.
The strawman discusses compatibility explicitly:
strawman:block_vs_object_literal#compatibility
"""
This proposal is mostly backward-compatible. In particular, the “stray label” problem whereby
javascript:foo()
migrates from URL contexts (links, |src| attribute values, the browser’s address toolbar) into script content, but not at the start of a block, continues to work. Note that such a “label” is not used by the statement or expression to which it is affixed.
Useless labels are thus allowed other than at the start of a block (immediately after the |{| that starts the block).
A block in JS today, or a block-lambda strawman:block_lambda_revival
if that extension is supported, may be prefixed by a label and actually use that label, e.g. via a |break| targeting that label. The grammar changes above support such a label-using block(-lambda).
"""
Do you feel hopeful?
Reasonably.
How often is label-after-block-opening-left-curly used and actually useful? Empirical question, where did Google codesearch go? :-/. Arrows won't change answer much, my guess, by the time ES7 is going.
The strawman changes to the grammar are still ambiguous :(
() => {}
-> noop or Object or SyntaxError? If SyntaxError, ...why?
I think hacking around this would not get rid of the footgun, but would just make it more complicated to understand the footgun, personally.
Caitlin Potter wrote:
The strawman changes to the grammar are still ambiguous :(
() => {}
-> noop or Object or SyntaxError? If SyntaxError, ...why?
No, the strawman addresses this: "An empty pair of braces |{}| other than at start of /Statement/ is an /ObjectLiteral/." The grammar is unambiguous. Notice the meta-? in the first production RHS below, and the non-empty-producing nature of the second:
Block: { UnlabeledStatementFirstList? } { WellLabeledStatement StatementList? }
UnlabeledStatementFirstList: UnlabeledStatement UnlabeledStatementFirstList Statement
The final piece of the puzzle is here:
We retain the |[lookahead ∉ {{, function}]| restriction in /ExpressionStatement/. At the start of a statement, |{| can be the start of a block only, never an object literal.
I think hacking around this would not get rid of the footgun, but would just make it more complicated to understand the footgun, personally.
You mean replace the footgun with a smaller one, maybe one so small it doesn't matter. Saying that the proposal doesn't get rid of the footgun means it still remains impossible to write x => {p: x} and not get what
Frankie and others want: an arrow returning an object. But the proposal does fix that footgun.
How often do you really want an empty object instead of a block with no statements?
That's good news, thanks for the strawman link. Is it correct to assume this with that proposal?
var f = (x) => (y) => {x, y};
f(1)(2) // => {x: 1, y: 2};
I think hacking around this would not get rid of the footgun, but would just make it more complicated to understand the footgun, personally.
My gut reaction is to agree - the current rule, while it takes some trivial learning, is easy to understand and communicate and is reflected well in other parts of the language. Also, additions to object literal syntax might make this more...weird:
x => { [abc](def = function() { huh() }) { blahblahblah } };
"But it's an object literal, obviously!"
Frankie Bagnardi wrote:
That's good news, thanks for the strawman link. Is it correct to assume this with that proposal?
var f = (x) => (y) => {x, y}; f(1)(2) // => {x: 1, y: 2};
Good point, the strawman does not take object literal shorthand into account and indeed parses {x, y} in an expression context as a block expression, not an object literal. More work needed, it was only straw :-P.
To clarify something Caitlin raised: the goal is to enable block expressions everywhere, and unify arrow function body on an expression nonterminal, eiminating ConciseBody (in ES6) or the two production right-parts in
ArrowFunctionExpression : ArrowFormalParameters => [lookahead ∉ { "{" }] AssignmentExpression ArrowFormalParameters => Block
from
Kevin Smith wrote:
I think hacking around this would not get rid of the footgun, but would just make it more complicated to understand the footgun, personally.
My gut reaction is to agree - the current rule, while it takes some trivial learning, is easy to understand and communicate and is reflected well in other parts of the language. Also, additions to object literal syntax might make this more...weird:
x => { [abc](def = function() { huh() }) { blahblahblah } };
"But it's an object literal, obviously!"
Yes, there's always a trade-off, some futures are foreclosed by syntax changes of this sort.
Is it worth it? Hard to say, crystal ball service not answering the phone ;-). Still, the motivation for that strawman I wrote in 2011 lives on.
You mean replace the footgun with a smaller one, maybe one so small it
doesn't matter. Saying that the proposal doesn't get rid of the footgun means it still remains impossible to write x => {p: x} and not get what
Frankie and others want: an arrow returning an object. But the proposal does fix that footgun.
How often do you really want an empty object instead of a block with no
statements?
Okay, good point about the (lack of) ambiguity in terms of how it's interpreted, but what I'm talking about is how it looks like it should work. It looks like a function body and an object literal, and it doesn't matter which one is used more frequently, because it's going to bite people one way or the other. Rules about what sorts of statements are allowed as the first statement in a block is also going to bite people some times.
Because it bites fewer people, there will be fewer people who have dealt with it and are able to explain it, so it becomes one of those "gotchas" of the language.
This is my personal opinion, but I think the "wrap object literals in parentheses for concise bodies" is much simpler to explain to people than more complicated rules. Simple is good :>
Also, I did some analysis on this issue way back when. In the codebases that I looked at the percentage of "bound this functions" which simply returned an object literal were quite low.
(See the "%Object Literals" column)
docs.google.com/spreadsheet/ccc?key=0Aro5yQ2fa01xdENxUzBuNXczb21vUWVUX0tyVmNKTUE#gid=0, docs.google.com/spreadsheet/ccc?key=0Aro5yQ2fa01xdEJySWxhZ1VoZ0VaWTdldXp4NUtJd3c#gid=0
(from esdiscuss.org/topic/btf-measurements)
Of course, actually having arrow functions will change the situation to some degree, but the current tradeoff made sense in light of that evidence.
In addition, I don't think Traceur supports that syntax yet. I have had to do this instead:
let f = x => { return {foo: bar}; };
/ /
\ /ark
Object Computing, Inc.
\ /olkmann
/
Brendan Eich wrote:
Kevin Smith wrote:
I think hacking around this would not get rid of the footgun, but would just make it more complicated to understand the footgun, personally.
My gut reaction is to agree - the current rule, while it takes some trivial learning, is easy to understand and communicate and is reflected well in other parts of the language. Also, additions to object literal syntax might make this more...weird:
x => { [abc](def = function() { huh() }) { blahblahblah } };
"But it's an object literal, obviously!"
Yes, there's always a trade-off, some futures are foreclosed by syntax changes of this sort.
To say a bit more, your example as written above unambiguously shows an object literal body, not a block expression body. It's complex, but there's no valid parse that would interpret the stuff after => as a
block. But let's tweak it a bit:
x => { [abc](def = function() { huh() })
{ blahblahblah } };
Now ASI applies and this parses as a block or an object literal. The arrow body is ctually valid ES1!
The future-hostility of strawman:block_vs_object_literal must be traded off against the complexity burden of every-more elaborate object literal extension syntax. At some point we can stop extending object literals. Are we there yet?
If so, then the {x, y} shorthand problem Frankie raised remains. One brute-force way out is to ban leading comma expression statements in UnlabeledStatement: ExpressionStatement, which could be done. I'm still thinking about this, not super-worried.
Given ES6 arrows as drafted and implemented, and as about to be finalized, the main problem that I foresee with adding block_vs_object_literal in ES7 is how () = {} makes an empty-block body. That's why I asked
"How often do you really want an empty object instead of a block with no statements?"
a few messages back. Serious question!
One scenario where arrow functions are likely to return objects is in the map function of arrays and the pipe function for streams. For example:
[1,2,3] .map(x => {value: x, double:2*x}) .filter(x => x.double < 5)
Marius Gundersen
On 5 Jan 2015 21:16, "Kevin Smith" <zenparsing at gmail.com> wrote:
Also, I did some analysis on this issue way back when. In the codebases
that I looked at the percentage of "bound this functions" which simply returned an object literal were quite low.
(See the "%Object Literals" column)
docs.google.com/spreadsheet/ccc?key=0Aro5yQ2fa01xdENxUzBuNXczb21vUWVUX0tyVmNKTUE#gid=0
docs.google.com/spreadsheet/ccc?key=0Aro5yQ2fa01xdEJySWxhZ1VoZ0VaWTdldXp4NUtJd3c#gid=0
(from esdiscuss.org/topic/btf-measurements)
Of course, actually having arrow functions will change the situation to
some degree, but the current tradeoff made sense in light of that evidence.
On Mon, Jan 5, 2015 at 3:06 PM, Brendan Eich <brendan at mozilla.org> wrote:
Kevin Smith wrote:
I think hacking around this would not get rid of the footgun, but would just make it more complicated to understand the footgun, personally.
My gut reaction is to agree - the current rule, while it takes some
trivial learning, is easy to understand and communicate and is reflected well in other parts of the language. Also, additions to object literal syntax might make this more...weird:
x => { [abc](def = function() { huh() }) { blahblahblah } };
"But it's an object literal, obviously!"
Yes, there's always a trade-off, some futures are foreclosed by syntax
changes of this sort.
Is it worth it? Hard to say, crystal ball service not answering the
phone ;-). Still, the motivation for that strawman I wrote in 2011 lives on.
Caitlin Potter wrote:
You mean replace the footgun with a smaller one, maybe one so small it doesn't matter. Saying that the proposal doesn't get rid of the footgun means it still remains impossible to write x => {p: x} and not get what Frankie and others want: an arrow returning an object. But the proposal does fix that footgun.
How often do you really want an empty object instead of a block with no statements?
Okay, good point about the (lack of) ambiguity in terms of how it's interpreted, but what I'm talking about is how it looks like it should work. It looks like a function body and an object literal, and it doesn't matter which one is used more frequently, because it's going to bite people one way or the other.
True. Can't share { as I did in JS and avoid this. Ship sailed long ago.
Rules about what sorts of statements are allowed as the first statement in a block is also going to bite people some times.
Probably not useless labels at the front. Seriously, the change to allow labels only at top level or else in front of any label-using statement (and no other) is almost perfectly backward compatible. Stray and unused labels are latent bugs, even if just minor left-over or misplace noise.
Because it bites fewer people, there will be fewer people who have dealt with it and are able to explain it, so it becomes one of those "gotchas" of the language.
Yes, a smaller footgun -- not the same one that started this thread. I agree it's vexing to have a footgun left, but if it differs in quality and frequency of misfires, maybe it's worthwhile. Hard to assess.
I also agree KISS favors what we all agreed to do, which was (a) take arrows into ES6; (b) not pursue block_vs_object_literal after I wrote it up. But we could revisit (b) and that seems worth some es-discuss overhead.
This is my personal opinion, but I think the "wrap object literals in parentheses for concise bodies" is much simpler to explain to people than more complicated rules. Simple is good :>
Your advice is good as far as it goes; it seems not to reach enough people to avoid a real-world speedbump, from the root message in this thread.
If the speedbump were caught at compile-time, no big. That it results in runtime behavior differing from the DWIM intent of the programmer means latent bugs. Perhaps JSLint, ESHint, etc., will be updated to catch the bad thing in the field, and that will suffice.
My apologies. I meant to say that Traceur doesn't support returning a literal object from an arrow function with syntax like this:
let f = x => ({foo: bar});
However, I just tested it again and it works fine. I could swear this didn't work a few months ago. Maybe it was fixed recently. I'm happy!
Mark Volkmann wrote:
My apologies. I meant to say that Traceur doesn't support returning a literal object from an arrow function with syntax like this:
let f = x => ({foo: bar});
However, I just tested it again and it works fine. I could swear this didn't work a few months ago. Maybe it was fixed recently. I'm happy!
Cool -- you are supporting Caitlin's thesis that we can pedagogue around the footgun. I hope so.
Still leaves strawman:block_vs_object_literal as an option for ES7. Comments welcome.
Unless
x => {foo: bar}
and
x => {}
both parse as object literals, I'm against your proposal. Either un-paren'd braces are consistently a block, or they're consistently an object literal. Python has had major pains with "i before e except after c" rules to make the language nicer to use, and it's easier to tell people "always add the parens".
A third option is to make x => {foo: bar} a syntax error to steer people
away from an accidental footgun, while still making {} a block, and keeping the consistency of "parens around object literals".
Jasper St. Pierre wrote:
Unless
x => {foo: bar}
and
x => {}
both parse as object literals, I'm against your proposal. Either un-paren'd braces are consistently a block, or they're consistently an object literal. Python has had major pains with "i before e except after c" rules to make the language nicer to use, and it's easier to tell people "always add the parens".
Fair.
A third option is to make x => {foo: bar} a syntax error to steer people away from an accidental footgun, while still making {} a block, and keeping the consistency of "parens around object literals".
Just about too late for ES6, though. Eep!
Strongly negative against the proposal. I believe that charisma of a language comes from its simplicity. The proposal seems to add too many syntax rules to ES. btw it is not easy to implement such a syntax into practice. I'm afraid there will be more syntax refinement, which actually make the parser to scan source code twice (or adjust the AST afterward, but currently traceur reparse the code when refine the destructuring assignment). I don't feel it worthwhile.
Though I am strongly negative, but there actually is such an implementation. The REPL of node will parse {a:1} as object literal while {a:1;} as block.
An HTML attachment was scrubbed... URL: esdiscuss/attachments/20150106/66238b7a/attachment
For better or worse, C# avoids this ambiguity through the use of the new
operator:
f = x => y => new { x, y };
Of course, C# does not support creating an object literal (nee. "anonymous type") without new
, but using new
here would be generally unambiguous in ES7+. Although its legal to write new {}
in ES today, it currently results in a runtime exception.
Ron
Sent from my Windows Phone
Once again...forgot to change the subject. (I get the digest...can't you tell?) sigh
From: Alex Kocharin <alex at kocharin.ru> To: Gary Guo <nbdd0121 at hotmail.com>, "brendan at mozilla.org" <brendan at mozilla.org> Cc: "es-discuss at mozilla.org" <es-discuss at mozilla.org> Date: Tue, 06 Jan 2015 06:59:52 +0300 Subject: Re: (x) => {foo: bar}
06.01.2015, 06:38, "Gary Guo" <nbdd0121 at hotmail.com>:
Though I am strongly negative, but there actually is such an implementation. The REPL of node will parse {a:1} as object literal while {a:1;} as block.
Node.js REPL wraps all the statements in parentheses. Therefore
{a:1;}
becomes({a:1;})
which is a syntax error.
Not in my REPL (node 0.10.33).
$ node
> {a:1;}
1
> {a:1}
{ a: 1 }
What the Node.js REPL effectively appears to do, AFAICT, is some sort
of evented try-catch. Though, @Gary, I agree 100% with what you said
about being very against it. It would require a lot more context to
figure out if something returns an object, as {a, b, c}
is
technically both a valid block and object (using shorthand
properties). The problems would then become non-obvious and difficult
to diagnose in practice.
Nope. {a:1;} will work in REPL of Node.js. In source code of repl.js, it will wrap input with parentheses first, and if there is a syntax error, it then evaluate the code without parentheses. You can test {a:1} and {a:1;}, both of them works; the former one returns {a:1} while the latter one returns 1.
From: alex at kocharin.ru To: nbdd0121 at hotmail.com; brendan at mozilla.org CC: es-discuss at mozilla.org Subject: Re: (x) => {foo: bar}
Date: Tue, 6 Jan 2015 06:59:52 +0300
06.01.2015, 06:38, "Gary Guo" <nbdd0121 at hotmail.com>: Though I am strongly negative, but there actually is such an implementation. The REPL of node will parse {a:1} as object literal while {a:1;} as block. Node.js REPL wraps all the statements in parentheses. Therefore {a:1;}
becomes ({a:1;})
which is a syntax error.
On Mon, Jan 5, 2015 at 6:06 PM, Brendan Eich <brendan at mozilla.org> wrote:
Jasper St. Pierre wrote:
Unless
x => {foo: bar}
and
x => {}
both parse as object literals, I'm against your proposal. Either un-paren'd braces are consistently a block, or they're consistently an object literal. Python has had major pains with "i before e except after c" rules to make the language nicer to use, and it's easier to tell people "always add the parens".
Fair.
A third option is to make x => {foo: bar} a syntax error to steer people
away from an accidental footgun, while still making {} a block, and keeping the consistency of "parens around object literals".
Just about too late for ES6, though. Eep!
Is it though? ISTM there's consensus that this is a footgun -- isn't that a
kind of spec bug? If it's too late to properly vet a change which narrowly
excludes from arrow bodies any LabeledStatement
that isn't a
WellLabeledStatement
, would it be feasible to simply exclude any
LabeledStatement
for now? I doubt there's much code in the wild that
would be affected by this change -- certainly not yet. That'd buy plenty of
time to add back proper support for WellLabeledStatement
arrow bodies in
es7.
Implicitly returning object literals would still require paren wrapping, of course -- and in light of the empty object / empty block ambiguity this seems inevitable. And we'll get a fail-fast compile error for the common case (non-empty objects) to would help educate and remind us all of the distinction.
On Tue Jan 06 2015 at 2:18:47 AM Isiah Meadows <impinball at gmail.com> wrote:
From: Alex Kocharin <alex at kocharin.ru> To: Gary Guo <nbdd0121 at hotmail.com>, "brendan at mozilla.org" < brendan at mozilla.org> Cc: "es-discuss at mozilla.org" <es-discuss at mozilla.org> Date: Tue, 06 Jan 2015 06:59:52 +0300 Subject: Re: (x) => {foo: bar}
06.01.2015, 06:38, "Gary Guo" <nbdd0121 at hotmail.com>:
Though I am strongly negative, but there actually is such an implementation. The REPL of node will parse {a:1} as object literal while {a:1;} as block.
Node.js REPL wraps all the statements in parentheses. Therefore
{a:1;}
becomes({a:1;})
which is a syntax error.Not in my REPL (node 0.10.33).
$ node > {a:1;} 1 > {a:1} { a: 1 }
What the Node.js REPL effectively appears to do, AFAICT, is some sort of evented try-catch.
No, that's not what it does. Alex was close, but not exactly right. The REPL will test input and wrap if necessary: joyent/node/blob/master/lib/repl.js#L266
Dean Landolt wrote:
ISTM there's consensus that this is a footgun -- isn't that a kind of spec bug?
No, it's a trade-off, and recent comments in this thread argue against complicating the grammar and real-world parsers, and in favor of teaching people to parenthesize. Nowhere near consensus on what to do about the trade-off. Lots of footguns, more than feet, out there in design space.
Sorry, sent too soon.
Dean Landolt wrote:
If it's too late to properly vet a change which narrowly excludes from arrow bodies any
LabeledStatement
that isn't aWellLabeledStatement
, would it be feasible to simply exclude anyLabeledStatement
for now? I doubt there's much code in the wild that would be affected by this change -- certainly not yet. That'd buy plenty of time to add back proper support forWellLabeledStatement
arrow bodies in es7.
The ES6 grammar has not been refactored per my old strawman. It has no WellLabeledStatement non-terminal.
For ES6, we would need another production parameter to FunctionBody, FunctionStatementList, and StatementList (at least) by which to restrict a ConciseBody : { FunctionBody } such that the FunctionBody : FunctionStatementList (via FunctionStatementList : StatementList[opt], StatementList StatementListItem, etc.) doesn't start with a LabelledStatement.
Cc'ing Allen and Waldemar for their thoughts.
I don't see how wellLabeledStatement will protect against object literals with shorthand properties, like (x, y, z) => {x, y, z}.
The solution to the block vs object problem today is to wrap the object in parenthesis, but not the block. The only alternative that is easy for humans to understand and not ambiguous for machines is to wrap or prefix the block, but not the object, for example with do:
x => do { let a = 5; return a*x; }
AFAIK do expressions are not in ES6, but might be in ES7.
Marius Gundersen
Okay: is this a valid statement/expression? I didn't think so, but I may be wrong.
({ foo(); bar(); })
Marius Gundersen wrote:
I don't see how wellLabeledStatement will protect against object literals with shorthand properties, like (x, y, z) => {x, y, z}.
The solution to the block vs object problem today is to wrap the object in parenthesis, but not the block. The only alternative that is easy for humans to understand and not ambiguous for machines is to wrap or prefix the block, but not the object, for example with do:
x => do { let a = 5; return a*x; }
AFAIK do expressions are not in ES6, but might be in ES7.
do-expressions are in good shape for ES7, stage 2 or better IIRC. I can't find a record of this at the moment, but perhaps arossberg has a link.
On Tue, Jan 6, 2015 at 3:57 PM, Marius Gundersen <gundersen at gmail.com>
wrote:
I don't see how wellLabeledStatement will protect against object literals with shorthand properties, like (x, y, z) => {x, y, z}.
It would "protect" against them by disallowing them without the paren wrapping. A fail-fast SyntaxError beats the hell out of trying to diagnose an almost imperceptible runtime bug.
The solution to the block vs object problem today is to wrap the object in parenthesis, but not the block. The only alternative that is easy for humans to understand and not ambiguous for machines is to wrap or prefix the block, but not the object, for example with do:
x => do { let a = 5; return a*x; }
AFAIK do expressions are not in ES6, but might be in ES7.
I don't follow -- this isn't a labeled statement. How would it be any different from this?
x => { let a = 5; return a*x; }
AFAIK nobody's proposed forbidding this form. How would do-expressions help
here? Sure, IIUC do-expressions would be a good solution for labeled
blocks, when they're available. But by es7 there's also plenty of time to
refactor the spec to allow some variant of /be's proposed
WellLabeledStatement
grammar, which would allow well-formed labeled
statements w/o the do prefix.
Just to reiterate, I'm arguing that paren-wrapping should *still *be required around object literal arrow bodies, now and forever. Anything else would be impossibly confusing. But I can't see a reason to allow an (accidentally) unwrapped object literal to parse. If it's not a "well-labeled statement", it's surely a mistake.
Marius Gundersen
Dean Landolt wrote:
On Tue, Jan 6, 2015 at 3:57 PM, Marius Gundersen <gundersen at gmail.com <mailto:gundersen at gmail.com>> wrote:
I don't see how wellLabeledStatement will protect against object literals with shorthand properties, like (x, y, z) => {x, y, z}.
It would "protect" against them by disallowing them without the paren wrapping. A fail-fast SyntaxError beats the hell out of trying to diagnose an almost imperceptible runtime bug.
No, Marius has a point here: there's no LABEL: before anything in {x, y, z}.
On Tue Jan 06 2015 at 4:53:05 PM Isiah Meadows <impinball at gmail.com> wrote:
Okay: is this a valid statement/expression? I didn't think so, but I may be wrong.
({ foo(); bar(); })
That's not valid for any grammar in up to and including ES6.
To make it valid, pick one, but not both:
- Remove the parens and it's valid block syntax.
- Remove everything inside the curlies, except for the word
foo
and it's valid object literal syntax, containing an identifier reference for some binding namedfoo
(property short hand).
Can anyone explain what to expect as a result of the following expression?
var fn = () => ({ a, b } = { a: 0, b: 1 });
var fn = function () { var a = 0, b = 1;
return { a: a, b: b };
};
or
- (traceur-compiler)
var fn = function () { var tmp;
return (tmp = {a: 0, b: 1}, a = tmp.a, b = tmp.b, tmp);
};
There're global variables in the second case
var fn = () => ({ a, b } = { a: 0, b: 1 });
The expression ({ a, b } = { a: 0, b: 1 })
is a destructuring
assignment. If there are lexical bindings for a
and b
it will assign
to those. Otherwise, in strict mode it will fail with a ReferenceError.
In sloppy mode, it will assign to the global.
traceur is correct.
I found that even without shorthand, the object literal can still be ambiguous with block.
}}
shall it be interpreted as()=>({ method(){ // method body }})
or
()=>{ method(); { // block statement }}```
This is certainly an ambiguity that cannot be resolved by any transformation, or refinement of syntax. (so does the shorthand {x,y,z})
On Jan 6, 2015, at 5:34 PM, Gary Guo wrote:
I found that even without shorthand, the object literal can still be ambiguous with block.
Yes, but not in the context of a ConciseBody of an ArrowFunction.
The grammar for ConciseBody (people.mozilla.org/~jorendorff/es6-draft.html#sec-arrow-function-definitions ) ensures that there is no ambiguity. If a ConciseBody starts with a { then it must parse as either a well-formed bracketed FunctionBody or as a syntax error.
There is no guessing. { means statements follow rather than an expression.
Allen Wirfs-Brock wrote:
On Jan 6, 2015, at 5:34 PM, Gary Guo wrote:
I found that even without shorthand, the object literal can still be ambiguous with block.
Yes, but not in the context of a ConciseBody of an ArrowFunction.
The grammar for ConciseBody (people.mozilla.org/~jorendorff/es6-draft.html#sec-arrow-function-definitions, people.mozilla.org/~jorendorff/es6-draft.html#sec-arrow-function-definitions ) ensures that there is no ambiguity. If a ConciseBody starts with a { then it must parse as either a well-formed bracketed FunctionBody or as a syntax error.
There is no guessing. { means statements follow rather than an expression.
Indeed. Perhaps Gary meant less formal ambiguity, as Caitlin did earlier in thread -- in which case we could future-proof or perhaps just help people by proscribing { L: 42 } from being parsed as a ConciseBody. I'm curious to know what you think about this idea. It seems best done with parametric productions, but I haven't worked it out yet.
On Jan 7, 2015, at 1:23 PM, Brendan Eich wrote:
Allen Wirfs-Brock wrote:
On Jan 6, 2015, at 5:34 PM, Gary Guo wrote:
I found that even without shorthand, the object literal can still be ambiguous with block.
Yes, but not in the context of a ConciseBody of an ArrowFunction.
The grammar for ConciseBody (people.mozilla.org/~jorendorff/es6-draft.html#sec-arrow-function-definitions, people.mozilla.org/~jorendorff/es6-draft.html#sec-arrow-function-definitions ) ensures that there is no ambiguity. If a ConciseBody starts with a { then it must parse as either a well-formed bracketed FunctionBody or as a syntax error.
There is no guessing. { means statements follow rather than an expression.
Indeed. Perhaps Gary meant less formal ambiguity, as Caitlin did earlier in thread -- in which case we could future-proof or perhaps just help people by proscribing { L: 42 } from being parsed as a ConciseBody. I'm curious to know what you think about this idea. It seems best done with parametric productions, but I haven't worked it out yet.
Certainly is plausible. I suspect there might be a couple places in the ES grammar where we could replace lookahead restrictions with parameterization. I'm less sure whether there is a reason to prefer one over the other.
ObjectLiteral is already doing double duty covering ObjectAssignmentPattern. so this would be even more complexity in this area of the grammar.
Finally, I'm sort of on the side that this may not be so good for programmers. The rule, "if it starts with a { its a function body" is simple to teach and remember. Adding "except if the first thing is a labeled expression statement" (or whatever) is more difficult to explain and remember. More likely then not, all that many programmers would take away is "there are some special cases here that I don't really remember so I need to be really careful". I'm not sure that is an improvement over the status quo.
Allen Wirfs-Brock wrote:
ObjectLiteral is already doing double duty covering ObjectAssignmentPattern. so this would be even more complexity in this area of the grammar.
Yeah, the complexity counter-argument is strong.
Finally, I'm sort of on the side that this may not be so good for programmers. The rule, "if it starts with a { its a function body" is simple to teach and remember. Adding "except if the first thing is a labeled expression statement" (or whatever) is more difficult to explain and remember. More likely then not, all that many programmers would take away is "there are some special cases here that I don't really remember so I need to be really careful". I'm not sure that is an improvement over the status quo.
Me either, as noted up-thread. The option to prohibit a label at start of body wouldn't commit us to doing block_vs_object_literal, though. It would just carve out the future option. Still, no point in that work if we think we will never take the option.
Even when we want to throw early errors for object-literal-like block, I consider my argument still valid.
()=>{ method() { }}
Should we throw an early error? Of course not, but it might be a potential error; out task is not to warn on object-literal-like block; instead, maybe simply we can just disallow improperly labelled statement in arrow function body.
Brendan Eich wrote:
Mark Volkmann wrote:
My apologies. I meant to say that Traceur doesn't support returning a literal object from an arrow function with syntax like this:
let f = x => ({foo: bar});
However, I just tested it again and it works fine. I could swear this didn't work a few months ago. Maybe it was fixed recently. I'm happy!
Cool -- you are supporting Caitlin's thesis that we can pedagogue around the footgun. I hope so.
Still leaves strawman:block_vs_object_literal as an option for ES7. Comments welcome.
A crazy idea:
(a, b) { body } (a, b) => expr /* always expr context */
the only problem I quickly see is to distiguish (a) { body }, but IMHO there is the same lookahead need for distiguishing (a) => something today.
Marius Gundersen wrote:
I don't see how wellLabeledStatement will protect against object literals with shorthand properties, like (x, y, z) => {x, y, z}.
The solution to the block vs object problem today is to wrap the object in parenthesis, but not the block. The only alternative that is easy for humans to understand and not ambiguous for machines is to wrap or prefix the block, but not the object, for example with do:
x => do { let a = 5; return a*x; }
Yeah, this is good.
Even if do expression won't get in, if one could clearly tell the intent (block vs. expression), it would make things easier to work with.
=> expr / => do block is less ambiguous then => expr / [nothing] block I
posted earlier.
let f = (x) => {foo: bar};
In the implementations I checked, this is actually allowed, but it's parsed as a label instead of what you may expect at first glance (an object).
Is there any reason this is allowed? If there's no reason other than to match
function(){}
, this should be a syntax error, in my opinion.A potentially easier and wider reaching solution here would be to restrict labels in strict mode to demand a possible break/continue, else it's a syntax error. The only area I'd be concerned about compatibility is low level generated JavaScript.
Thoughts?