Make comma at the end of line optional
Yes, ASI has been such a great success, let's extend it to commas.
There are [no LineTerminator here] rules in the ES grammar, in order to prevent the most pernicious trap when using ASI:
return // implicit semicolon here
a = b
Those [no LineTerminator here] rules have not been included in constructs that don’t need them. As a result:
const object = {
get // no implicit comma here
y: 2
z: x // no implicit comma here
in: "foo"
}
So, no, it’s not a good idea.
Sorry, but I don't see any problems with example you provide:
const object = { get // not a complete declaration statement - so no implicit comma here y: 2 // continuation of a previous line
z: x // complete declaration statement and next line is not an operator - implicit comma here in: "foo" }
2017-09-12 18:53 GMT+03:00 Claude Pache <claude.pache at gmail.com>:
What benefit does this give the language?
Time saving:
- Automatically handle accidentally omitted commas, if used
- Saves having to add them in the first place
const object = { get // not a complete declaration statement - so no implicit comma here y: 2 // continuation of a previous line }
The get
declaration actually is a valid declaration (see shorthand
property names here:
developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#New_notations_in_ECMAScript_2015
).
Le 12 sept. 2017 à 18:00, Алексей <agat00 at gmail.com> a écrit :
Sorry, but I don't see any problems with example you provide:
const object = { get // not a complete declaration statement - so no implicit comma here y: 2 // continuation of a previous line
z: x // complete declaration statement and next line is not an operator - implicit comma here in: "foo" }
This is not how ASI works. A implicit semicolon is not added when a declaration would be complete, but when the next token would produce a syntax error.
I don't think ASI matters in relation to this proposal
I think the only place I see as a current inconsistency is with class definitions vs object definitions. It probably should have been looped into the object shorthand definition:
class MyClass {
prop = 123
constructor() {}
method() {}
}
vs
const myObj = {
prop: 123,
constructor(){},
method(){},
};
Also, to wit on the class-fields proposal and this issue: tc39/proposal-class-fields#7
Punctuation isn't noise.
Of course not:
var arr = [
1, 2, 3
4
];
If JS was the kind of language where a line break definitively ended a statement, that'd be a different story - but it's not.
I'm not sure how your text editor is set up, but you may or may not be able to tell soft-wrapping apart from an actual EOL character if you have long lines.
I don't like the idea of making whitespace of any kind significant, which is why I would agree with Jordan.
Le 12 sept. 2017 à 18:57, Алексей <agat00 at gmail.com> a écrit :
Don't you think that line break is a strong punctuation by itself?
It could have been. Unfortunately, in JS, it is not.
Automatic semi-colon insertion (ASI) could be seen as an attempt to have their cake and eat it too. Or, as a compromise between the two options by making some line breaks significant (e.g., the one after return
).
But in general, line breaks are not significant enough to allow to introduce ASI-like rules after the fact, that would work reliably enough.
BTW, the title of the thread, “Make comma at the end of line optional”, reveals a probable misunderstanding of the feature. In JS, semicolons are not “optional”, they are “automatically inserted”. That makes a difference when parsing:
a = b
+c
I prefer to describe semicolons as so required that the engine inserts them for you if you forget ;-)
The reason why we have a discussions about whether to rely on ASI or insert them manually is because ASI has problems (return is one of them). And they can't be fixed because fixes are not backward compatible. Based on this experience it would be possible to determine the rules that would not have that gaps or their kind.
2017-09-12 20:32 GMT+03:00 Claude Pache <claude.pache at gmail.com>:
You should have a reason to write it like this. And if you have than
var arr = [
1, 2, 3
4
];
would probably be better than
var arr = [
1, 2, 3,
4
];
And even if you don't - you always have an option to insert a coma at the end on the line
But in most cases dropping of a comma at the end would not reduce the readability but only remove the duplicated (by a line break) punctuation
Would you agree that
var a = {
x: 1
y: 2
}
is not reading worse than
var a = {
x: 1,
y: 2
}
?
2017-09-12 20:10 GMT+03:00 Jordan Harband <ljharb at gmail.com>:
In terms of style, and in terms of version control, either of these are fine:
const obj = {
x: 10
y: 23
};
// or
const obj2 = {
x: 10,
y: 23,
};
The mixed case is the most obnoxious one:
const bad = {
x: 12,
y: 7
}
However, as pointed out, there is ambiguity with:
const obj = {
get
x: 10
y
z: 24
get
myMethod() {
}
}
I just don't see optional comma in a collection as a useful feature for the
language -- "whitespace matters" always makes me uncomfortable, especially
since there are different standards for line breaks across machines
(looking at you \r\n
), whereas the ,
token, which is already used to
delineate arguments, is suited exactly for this job.
The one thing I still have a gripe over is the class definition syntax --
where neither ,
or ;
are used at the end of method expressions.
*> The reason why we have a discussions about whether to rely on ASI or
insert them manually is because ASI has problems (return is one of them). *
This seems to come from the perspective that ASI was a poorly specified feature, as opposed to a recovery mechanism for poorly terminated statements.
That being said, even with ASI completely removed from the discussion, your proposal has some significant barriers to overcome -- most notably the lack of backwards compatibility.
Examples have already been provided where your proposal would change the semantics of *already *valid code. E.g.,
const get = "foo";
const obj = { get y: 2 };
console.log(obj.get);
// under current rules: undefined
// under your new proposal: "foo"
If you introduce new rules to resolve that ambiguity or to preserve backwards compatibility, you've now created a situation similar to ASI, where developers have to remember a list of exceptions before they elide a comma.
Even in the absence of backwards compatibility concerns, you need to provide a persuasive argument that the cost-benefit ratio justifies the effort, and judging from initial feedback, I think that will be an uphill battle.
On Sep 12, 2017, at 11:22 AM, dante federici <c.dante.federici at gmail.com> wrote:
The one thing I still have a gripe over is the class definition syntax -- where neither
,
or;
are used at the end of method expressions.
Look at the grammar.
;
is not part of the concise method production, but it is still perfectly fine to insert a a ;
at the immediate end of a concise method. This is exactly like function declarations. A ‘;` is not a required part of a function declaration, but you can put one there if you want.
function f() {};
class C {
m() {};
n() {};
} //you can put a ; here, too if you want
Apologies, I was butchering the property getter syntax. With the correct syntax, the backwards compatibility concern remains, however:
const get = "foo";
const obj = {
get
y() { return 2 }
};
console.log(obj.get);
// current: `undefined`
// under this proposal: `"foo"`
You can try it yourself here: es6console.com/j7hy49k4
On the margin: currently that's syntax error. The only ambiguity that I see is shorthand for variable called get followed by method.
I mean, it's the general case of the "get" and "set" when defining a method: www.ecma-international.org/ecma-262/6.0/#sec-method-definitions-runtime-semantics-propertydefinitionevaluation
That being said, there's a lot of "you just shouldn't do that" in
javascript. Looking at you, undefined
not being a reserved word.
Syntax aside, a question that hasn't been sufficiently answered is what value does this actually add other than "I don't want to type ,"? Arguments for "easier to read code" I would absolutely disagree with, since it may be easier for one person, but not another. Giving a "line break matters" is a terrible answer, since that would break a ton of backwards compatibility.
Think of it from a different way: if there would be no ',' how would you react on the idea of adding it? Peaty sour every one would decide that would be a complete nonsense. On the other side there is a discussion about possibility of make it optional where different people have different opinions. And no, it wouldn't break a tone of code - at list for now we came to only one quite exotic case with "get" and "set". But yes, the fact that it exists making it impossible. At list in such form
2017-09-12 23:09 GMT+03:00 dante federici <c.dante.federici at gmail.com>:
On Tue, Sep 12, 2017 at 1:49 PM, Алексей <agat00 at gmail.com> wrote:
Think of it from a different way: if there would be no ',' how would you react on the idea of adding it? Peaty sour every one would decide that would be a complete nonsense.
This sort of hypothetical isn't useful; you're not proposing switching over to solely comma-less, you're proposing a mixture of comma and comma-less being allowed. That has very different ergonomics than either all-comma or all-comma-less.
The hypothetical comma-less language would also have made many different syntax decisions over the years to accommodate that, which current JS has not made. This causes the sorts of problems that Claude/etc have pointed out.
I tried out "automatic comma insertion" on some real-world code examples; in my opinion it decreases code readability.It solves the problem of needing to change two lines when adding to the end of a list, but that already has many solutions (the awful comma-first style, allowing trailing commas, just deal with it, syntax-aware diff).
On Tuesday, September 12, 2017, 3:09:51 PM CDT, dante federici <c.dante.federici at gmail.com> wrote:
I mean, it's the general case of the "get" and "set" when defining a method:www.ecma-international.org/ecma-262/6.0/#sec-method-definitions-runtime-semantics-propertydefinitionevaluation
That being said, there's a lot of "you just shouldn't do that" in javascript. Looking at you, undefined
not being a reserved word.
Syntax aside, a question that hasn't been sufficiently answered is what value does this actually add other than "I don't want to type ,"? Arguments for "easier to read code" I would absolutely disagree with, since it may be easier for one person, but not another. Giving a "line break matters" is a terrible answer, since that would break a ton of backwards compatibility._______________________________________________
es-discuss mailing list
es-discuss at mozilla.org, mail.mozilla.org/listinfo/es
I would take commas over a mixture a thousand times over; I'd do the same with semicolons - it's not the presence or absence of these tokens that causes a problem, it's the ambiguity.
Introducing the same horrific ambiguity around semicolons, for commas, does not sound like a good idea.
Okay what would be the cons to allowing semi colons in place of commas in object literals?
I have an aversion to dangling commas. They're like,
I can totally feel the, English break with, Commas especially, as a part of speech; Semicolons do a great job of delineating sentences and statements; But I have a special heart for separating bits from bytes.
I guess a return is a period?
Either way --- I think my opinions are:
- end of statement style is a linting concern
- there's no benefit from comma optional => lint + fix can warn or make right in the exact get/set case
- ASI is... a stopgap
- srsly go to coffeescript or sugarjs if you want to type less (P.S. don't)
Can't get
be relegated to a reserved/keyword, like let
, yield
and
await
were? Just curious about that kind of process & decision?...
I can assure you that will likely never happen, because it's a pretty
obvious identifier to use in more generic or high-context scenarios.
(Think: get(foo, bar)
, and I've done that plenty of times.)
Very interesting point. Is there another way to get this optional comma proposal through while being backwards compatible? I really like the idea.
If it worked like ASI, then surely it would allow the multi-line get
case?:
{
get
x() //valid syntax after "get", so no comma inserted
}
Can someone remind me of the problem doing it this way, if any? (I'm not sure it has been mentioned yet)
On 9/13/17 9:05 AM, Naveen Chawla wrote:
Can someone remind me of the problem doing it this way, if any?
You mean apart from all the existing footguns ASI has?
*> Can someone remind me of the problem doing it this way, if any? (I'm not
sure it has been mentioned yet)*
The problem is in how this proposal has currently been structured:
*> [...] if line is a complete statement and next line is not an operator
than consider it as complete argument (field, element) declaration.*
In the provided example...
{
get
x() //valid syntax after "get", so no comma inserted
}
... the get
line is a valid, complete shorthand property declaration, so
a comma would be inserted, and thus changing the semantics of already valid
code.
As is the case with virtually all potential syntax ambiguities, this can be worked around through exceptions and extra rules regarding when commas are/aren't inserted, but that tends this proposal towards the same idiosyncratic footguns that come with ASI.
This is obviously a subjective analysis, but this proposal seems to introduce too much wtfjs material in exchange for too little in time/character savings.
I'm not really familiar with ASI but they key factor mentioned in this discussion is this (by Claude Pache):
*A implicit semicolon is not added when a declaration would be complete, but when the next token would produce a syntax error. *
By this behaviour (a modification to the initial "complete statement produces comma" version of this proposal), everything would work perfectly, no?
The multi-line get would not produce a comma, and hence the scheme is backwards compatible, right?
Please provide a counter-example if I have missed something.
As for the benefit, the time savings in not having to debug accidentally omitted commas and not having to add them in the first place are, I think, an improvement. And of course those who want to continue using commas everywhere, can:
function doStuff(
x
y
z
){
}
const
x = 5
y = 6
z = 7
Great to hear those counter-examples as I don't know enough about ASI, and the related subject, to picture the pitfalls (sorry for my ignorance on this). Also it would be good for reference on this proposal...
Quick side note regarding multiple variable declarations: both versions of this proposal (OP's newline-based proposal and the ASI-inspired version) result in code breakage:
const
x = 5
y = 6
z = 7
Under existing ASI rules, this is currently equivalent to:
const x = 5;
// y and z are global
y = 6;
z = 7;
If we use newline based comma insertion (or give ASI-style comma insertion precedence over semicolon insertion), then this proposal would result in the following equivalent:
const
x = 5,
// y and z are now lexically scoped constants
y = 6,
z = 7;
Unless I'm missing something, both of those scenarios definitely preclude multiple variable declarations from this proposal.
That being said, the ASI-inspired semantics seems like it could have more legs in other contexts, although I would personally argue that too little is offered in exchange for the cognitive overhead of a new rule with ASI-style exceptions attached to it (i.e.., "comma before newlines, except after get, and set, variable declarations, ...").
Yes, this is the reason why I didn't mention the variable declaration in initial proposal - it is 100% valid syntax in current implementation (and 100% relative error in strict mode)
But the design problems of ASI are incomparable with a special case of get and set
2017-09-13 17:55 GMT+03:00 Jeremy Martin <jmar777 at gmail.com>:
On 9/13/17 9:57 AM, Naveen Chawla wrote:
By this behaviour (a modification to the initial "complete statement produces comma" version of this proposal), everything would work perfectly, no?
If by "perfectly" you mean "have hard-to-predict somewhat nonlocal behavior that makes any code relying on this a hard-to-read footgun", then the answer might be "yes". For pretty much any other definition of "perfectly", I'm fairly sure the answer is "no".
Great to hear those counter-examples as I don't know enough about ASI,
Still in the context of ASI, here are some examples of why ASI is a bad idea:
-
What does this return?
function f() { return 5; }
-
What does this alert?
var str = "hello"; var x = str [x].forEach(() => alert(x))
Now back to automatic comma insertion... In your example:
function doStuff( x y z ){ }
if someone changes doStuff to take an array as the second arg and you modify the call as:
function doStuff( x [y] z ){ }
suddenly you need to insert a comma after the "x" to preserve the right semantics, no? This is not terribly intuitive or obvious. It gets even worse in a situation like this:
function doStuff( x /* The next argument is an array for good reasons that we will now expound on in a long comment, etc, etc */ [y] ){ }
Quick, tell me without testing this or looking at the spec for a while whether this is a valid call to doStuff, with one argument, or a syntax error that would trigger comma insertion.
But more generally, if you just use your favorite search engine on the phrase "automatic semicolon insertion", you will get a slew of articles explaining the pitfalls.
x
<whitespace> [y]
would be invalid syntax, right?
So
x
[y]
would automatically insert a comma in the case of a function call arguments list, right?
That's exactly what would be desired. What am I missing?
Personally, I am annoyed by the extra typing required for spaces. I propose that we have a new kind of ASI: automatic SPACE insertion.
For instance, you could then write
functionfoobar(){return42;}
```js
What about...
-
variable (var)
-
donuts (do)
-
forest (for)
-
awaiter (await, module-specific)
-
async (caolan/async)
-
className (class)
-
letters (let)
-
constants (const)
Fun fact: all of these are valid, and many of them are relatively common. Please consider the ramifications of such a feature before proposing them.
I believe Bob was engaging in reductio ad absurdum, Isiah. ^_^
Oh okay. Granted, that's not always a safe assumption on this list, though...
(It's actually a bit of a refreshing surprise when people present well-researched proposals here, to be honest.)
> What am I missing?
Nothing with respect to function arguments, AFAICT.
But to beat the dead-cognitive-overhead horse again, the rules around ACI (Automatic Comma Insertion) appear to require too many exceptions. We've already covered:
- ACI doesn't apply at all between variable declarations
- ACI has exceptions around getter/setter properties in object literals.
I hadn't thought of these before, but there's also:
- ACI would need exceptions in object and array literals following yield in generator functions.
- ACI would need exceptions in object and array literals following await in async functions.
I would wager this isn't an exhaustive list yet, either.
To be clear, these aren't exceptions in the grammar itself (the ASI-style "insert comma if next token would generate a syntax error" is sufficient to handle all of these cases), but they are exceptions that developers and tooling would need to hold onto, and IMHO, relegate the purported readability improvements.
If something requires so much special casing just to work, it's
fundamentally broken and best avoided altogether. Sloppy mode is
fundamentally broken. eval
is fundamentally broken. TC39 people have
already generally accepted this as truth. Could we avoid adding more broken
features to the language? Just saying.
(TL;DR: Just let this thread die.)
Within the context of an array or object definition I could see implementing automatic commas between elements.... but not outside; think the thread is straying a little from the original post (const isn't available in either context). But only between completed expressions/function definitions. It wouldn't be horribly hard to option that into my json-6 parsing... just still don't really think I'm a fan anyway. (but that doesn't have to deal with get/set/function definitions).
On 9/13/17 1:55 PM, Naveen Chawla wrote:
x
<whitespace>[y]
would be invalid syntax, right?
Wrong.
What am I missing?
This is exactly why automatic X insertion with complicated rules is a bad idea for all values of X. ;)
The "some examples" of ASI problems might as well say "all", since there
aren't more than the two listed cases worth mentioning, and even the
return
one is kind of contrived, because putting line breaks after
return
isn't usually a thing. It also doesn't follow that ASI would be a
"bad idea"; remembering not to start lines with brackets or parens is easy
(especially with a linter, which people should be using anyway), and the
benefit is less visually noisy code and a bit less typing. The same can't
be said for comma insertion, because commas aren't as noticable or often
used as semicolons, and the syntax would have more 'gotchas' than with ASI,
so it's just not worth it.
IIFEs start with a (. Putting line breaks before certain expressions can help improve clarity. Your mind is made up but I have to protest. ASI sucks. And the extra cognitive overhead it causes is utterly pointless. If you don’t want to type them, why not program your editor to actually insert ; and a linebreak at the same time when you want to terminate a statement?
Many problems arise from parsing ambiguities — when writing code authors know exactly what they intend, why make future readers second guess?
-1 for code-maintainability. i don’t see the benefits justifying the extra complexity and rules it adds to javascript style-guides and linters (and those who follow them).
Now we have a great syntax improvement that allows us to put comma at the end of arguments lists, object and array definitions. So in multiline definition last line could have the same signature as others (not only in multiline, but multiline definition benefits the most).
I would like to propose syntax that would allow to each line looks like the last:
const object = { x: 1 y: 2 z: 3 }
The rule here: for multiline arguments list, object or array declaration if line is a complete statement and next line is not an operator than consider it as complete argument (field, element) declaration.
Not an operator so you could use
const condition = true const objext = { x: condition ? 0 : 1 y: 2 z: 3 }
It is really like ASI but without bugs with "[", "(" and "/" - they should be consider as a start of a new declaration