-
Notifications
You must be signed in to change notification settings - Fork 113
Issues with ASI #7
Comments
It's optional because of ASI. It shouldn't be optional in general - |
🔥 🔥 🔥 🔥 🔥 🔥 🔥 ASI 🔥 🔥 🔥 🔥 🔥 🔥 🔥 |
btw, ASI has some weird behavior here: class C {
foo = 'TC39';
[1]
bar = 'TC39'
[2]
prop() { console.log( this.foo, this.bar ) }
}
new C().prop() // 'TC39', '3' |
Yeah, and a whole bunch of other cases. I think this snippet covers most of the major surprises: class A {
a = 0
[b](){} // SyntaxError
a = 0
*b(){} // SyntaxError
async // property named `async`
a(){} // method named `a`
get
a(){} // getter for `a`
get; // property named `get`
a(){} // method named `a`
a = 0; // property named `a`, initialized to `0`
in // property named `in`
b; // property named `b`
a = 0
in
b; // property named `a`, initialized to `0 in b`
} |
|
I want a declaration list of fields, I'm not really interested in any of the cases @bakkot or I presented here. Saying that ASI is simple puts you in a privileged PoV we can't share with other regular developers. My examples are not common cases I expect, but I would be pretty annoyed in a case like this:
I still need time to fill myself with context from the notes and issues as I believe the champions might have had thoughtful discussions on this. I don't want to assume it's going to be easy to simply get rid of semicolon and ASI here. |
I understand the concern, but I believe that the traps of not knowing the ASI rules are far more problematic specially during the development phase where the code might not have the proper format yet, for example: function a() {
return [
'a',
'b',
'c'
];
}
function b() {
return
[
'a',
'b',
'c'
];
} There are a lot of examples of beginners having "strange" bugs due to not knowing the ASI rules and I always recommend that video to them; I got bitten a couple of times by ASI even in projects that required semicolons before I decided to finally learn how it works. |
@leobalter I agree; that case is pretty annoying. It's hard for me to think of what a better alternative would be, though. We've gotten pretty strong feedback from developers that they don't want to have to start putting semicolons after their declarations. Other languages use an explicit |
@littledan Why not make ASI a bit more intelligent by adding a rule for class methods. |
@borela Sure, what would the intelligence be exactly? |
@littledan The problem there is that ASI is treating the |
@borela That would arbitrary lookahead, which is not something we generally allow in the grammar. In particular, you can't distinguish between multiplication and a method until you've gotten to the brace: class A {
x = 0
* y (a1, a2, ...a3) // multiplication
} class A {
x = 0;
* y (a1, a2, ...a3) {} // method
} Similarly for distinguishing between a computed name and a computed property access. |
Yes, the idea would be to look for |
So the proposal would be that class A {
x = 0
* y (a1, a2, ...a3)
} would parse as a class field followed by a generator method, and then be a syntax error due to the lack of a function body? That would be something very different from ASI as it currently stands; I don't think that would fly. |
No, if a |
Right, so distinguishing between a method definition and an expression requires reading arbitrarily far ahead. As I say, I doubt that would fly. (Technically we did do something similar with arrow parameters, actually, but it's caused a lot of pain in implementations, and I'm not sure how analogous the situations are.) And it still doesn't solve the problem, anyway: is the following two fields, or one field whose initializer is an array access? Either would be syntactically valid. class A {
x = 0
[y]
} |
I would consider it two fields, I don't see why I would ever put Another option is to drop ASI completely, @leobalter was right that the rules are not that simple, after talking to other programmers I realized ASI is a bit unnatural to many people. |
@borela Sorry, a better example would have been Dropping ASI is an option, but we've heard a lot of pushback on that idea from people who are accustomed to the no-semi style, who are after all the only people who'd have to deal with the complexity. And, well, making new exceptions to ASI is also kind of confusing. (Personally I'm approximately neutral on that question, but I don't ever use the no-semi style, so I'm not a good person to ask.) |
I used no semicolons in my sources because of ASI, I had too many gotchas after years of C++; by not using semicolons I force myself to read my code more carefully and prevent stuff like returning undefined when I meant to return something; I would be ok if there was an option to at least disable ASI. |
I didn't have time to explore the previous discussions yet or the proposal's history, but as this is already being discussed, I have had a slight idea of removing the The problem is that I agree the lookahead issue would be a potential blocker to move this proposal towards to Stage 3. One thing we might try is allowing a trailing comma in the FieldDefinitionList:
This way we can separate a class field from a method in a better visual way: class C {
x = 42,
*gen() {}
} This should work just fine as the FieldDefinition expects an AssignmentExpression as the Initializer, not Expressions. I'm reopening this issue to allow more feedback from other people. Feel free to close it if this is noisy. |
@bakkot Arrow functions already have a lot of this complexity, but it's not tied into ASI, so I agree, this would not be great. @leobalter Thanks for the thread name change. Well, this sounds like it's getting back into the ES2015-era discussion, should we use |
My suggestion only adds the trailing comma, it already exists between fields in this proposal.
|
Declaration lists don't generally permit trailing commas. Should we allow them for |
Ping, any further ideas on this thread? |
I don't believe this should force a trailing comma for var, etc declarations, considering this declaration list also allows the uncommon class method definitions. |
For me, @littledan has the important point:
We've already decided on semicolons between class elements. Although, I think it would be best to remove the comma-list option from class fields. It feels bizarre to me for some reason. |
random suggestion gremlin:I realized this morning that this idea might ruin dot-chaining, and a significant portion of the rest of ECMAscript … and I have no idea how hard it might actually be to get implemented… but what if infix operators/keywords were forbidden at the beginnings of lines within class bodies? That way you could enforce the unary meanings of overloaded operators ( |
@thepeoplesbourgeois The case of
Also, your suggestion does not resolve the issue: (1) class C {
foo // semicolon inserted here
bar() { }
} (2) class C {
get // no semicolon inserted here
bar() { }
} |
I see your point @claudepache , and it seems pretty inherent to anything we do here with ASI. If you want to be able to omit semicolons anywhere, you should be able to do it after a field declaration which has no initializer. But I'm not sure how much the "field named get/set/static" issue will come up. I think |
@littledan captured my line of reasoning pretty succinctly. I had actually believed I guess the potential issues this would open up center around forward extensibility; what becomes a keyword in future specs would need immeasurably more consideration in the event that it might already have gained common use in a framework. It's not difficult to imagine the usefulness of something like ... then again, this would already have needed to be a consideration for |
The extensibility point is really huge, and @waldemarhorwat emphasized it to me after I presented on ASI previously. If we allow ASI, it has cross-cutting effects for any changes we want to make in the class grammar; the most obvious one is no more |
Experimenting with different newline combinations in the Babel REPL shows that // PRESETS: es2015, es2017, react, stage-0
class C {
get() {} // method
something() {} // method
async somethingElse() {} // async method
async
somethingYetEvenMoreDifferent() {} // method
get
anotherThing() {} // getter
static
set
anotherThing(param) {} // class setter
static() {} // method
*
yetAnotherThing() {} // generator
foo = 5 *
2 // == 10
} |
@thepeoplesbourgeois That's right. We will have to go 'async style' with all future keywords within classes if we do this sort of ASI. |
I'm in favor of that. I think the current ASI rules for get/set/static/* are a bit silly. |
We discussed ASI in the November 2017 TC39 meeting. The committee's conclusion was that we should maintain ASI in class fields, but be more explicit in the specification with notes about what using ASI means and the risks of doing so. I'm preparing a patch which does this. |
Closing per resolution in #7 (comment) |
@littledan The comment above at #7 (comment) seems to indicate that ASI in the context of class fields is future hostile in the sense that we won't be able to add any member modifiers without peppering newline restrictions all over the place. Was there consensus that such newline restrictions are acceptable? |
@zenparsing yes, there was (specifically, consensus that future language additions would no longer be held back by asi concerns alone) |
There are a couple of places where ASI is not applied, for good reasons (e.g. |
@zenparsing We discussed that specific question in committee and decided that they did not. See notes, slides. I was initially against allowing ASI here, but I've since been convinced it's not worth adding another exception, mostly because there doesn't seem to be any group of users which would be served by it. (The people who rely on ASI really object to having more required semicolons even if that means more edge cases, and the people who don't won't run into such ASI issues anyway. And as far as I can tell, no one puts a linebreak between |
@bakkot Thanks for the links! I certainly agree that in current practice users almost universally place contextual keywords on the same line as the following token. Still, for this particular case (field declarations without an initializer) my 🕷 sense is tingling quite strongly. |
@zenparsing What do you think we should do for next steps to evaluate this concern? |
@waldemarhorwat Do you have any more thoughts on this issue? |
@littledan I have further thoughts on this topic but I'm not quite ready to discuss them yet. I'll follow up soon! |
My biggest worry (that ASI would overly constrain MethodDefinition in both classes and object literals) turned out to be unjustified. We can still extend the method syntax with leading operator characters and other things down the road if we choose (and are willing to accept the mild ASI hazards). The lesser worry is whether relying on NLT for new contextual keywords within class bodies (and object literal methods) creates a confusing situation, since some of the contextual keywords ( Before
Which is fairly simple. What is the situation now? Internally, the syntax is a mess, but what is the simple intuition? I suppose that the simple intuition, if we admit ASI here, is that contextual keywords should never end a line, regardless of what parsing context we might be in. This intuition seems acceptable to me. It's unfortunate that we have to sacrifice internal consistency and simplicity in order to accommodate history and ergonomics (ASI in this case), but it's a tradeoff that we've had to make many times before, and it's probably the right call here (shrug)... Unless @waldemarhorwat has any other thoughts? |
Yeah, that's my position. (Though I might be happier if we could add NLTH restrictions to No idea how risky that would be, though given that almost all tooling got these wrong until relatively recently, I suspect it might be doable.) |
The current grammar for FieldDefinitionLists requires a closing semicolon:
https://tc39.github.io/proposal-class-fields/#sec-updated-syntax
I feel like it should optional, but maybe it's intentional and I'm here to request clarification.
Thanks
The text was updated successfully, but these errors were encountered: