ui-as-code icon indicating copy to clipboard operation
ui-as-code copied to clipboard

Try making optional semicolons more strict

Open munificent opened this issue 6 years ago • 7 comments

The current optional semicolon proposal is good as baseline for how non-breaking it's possible to be. Since it's very conservative and only makes newlines significant where they need to be, it's probably the least-breaking possible proposal.

That robustness is good, but it comes with some downsides:

  1. It may be harder for human readers and tools to tell when a newline is significant. You need to do potentially arbitrary lookahead on subsequent lines to see if you hit an ambiguity or not.

  2. It can paint us into a corner where future language changes become breaking changes. For example, if we add a prefix + operator, then any existing line like:

    a
      + b
    

    Becomes a breaking change since it will then start inserting a semicolon after a.

  3. The eagerness principle is harder to reason about since it only kicks in when there is an ambiguity.

Lasse suggesting something simpler and stricter. Basically, when parsing an expression in an expression statement context, if you reach a point where you have parsed a complete expression and hit a newline, stop there. "Eagerness" in this sense means if the code preceding a newline could be parsed correctly with a semicolon here, then do so, even if that causes a syntax error on subsequent lines as in:

a
+ b

That rule is too simple because it breaks method chains. So we need at least one token of lookahead so that we can ignore newlines when the next line starts with ., .., ?., or ?. I'll have to investigate to see if there are other cases like this.

munificent avatar Oct 24 '18 20:10 munificent

Have you considered requiring leaving operators on the previous line to explicitly indicate that the expression wraps to the next line? I think it actually helps with code readability. Humans need to parse code too. Prior art includes Golang.

Operators left on the previous line can disambiguate a bunch of stuff, such as:

// Two statements: "a" and "+b"
a
  + b

// One expression statement: "a + b"
a +
  b

// Function call: "a(b)"
a(
  b)

// Two expression statements: "a" and "(b)"
a
  (b)

If we get something like a builder syntax in the future, it could also disambiguate trailing closure vs anonymous block:

// Trailing closure
Foo {
  bar = 5
}

// Two statements: "Foo" and "{ ... }" block
Foo
{
  bar = 5
}

yjbanov avatar Oct 25 '18 19:10 yjbanov

Have you considered requiring leaving operators on the previous line to explicitly indicate that the expression wraps to the next line?

Yes, this is what dartfmt always does. In cases where you do put the operator at the end of the previous line, it's never ambiguous and the newline is ignored. It's only in the cases where the user didn't use dartfmt and put the operator on the next line that we need to resolve.

munificent avatar Oct 25 '18 21:10 munificent

I actually meant use this rule for all operators, including the dot invocation operator, i.e.:

foo.bar.
  baz

Instead of:

foo.bar
  .baz

dartfmt currently does the latter.

I think this would remove the need for lookahead? I admit though, that for the conditional operator I like seeing the ? and : on the next line. Not that I'm a big fan of the conditional operator. I'd prefer something like pattern matching, or if block expressions.

yjbanov avatar Oct 25 '18 21:10 yjbanov

I actually meant use this rule for all operators, including the dot invocation operator

I think that would cause mass rioting in the streets.

I think this would remove the need for lookahead?

It only saves a single token of lookahead, which is a trivial matter. It's the potentially unbounded lookahead that's more worrisome, which Dart already has. For example, given:

foo(a, b, c, d, e, f, g <more code...>

You don't know if you're parsing a function call or a local function declaration until you reach the end of the parameter list and find a body (or not). (Actually, now that I think about it, that spells real trouble for any block-argument syntax.)

munificent avatar Oct 25 '18 23:10 munificent

I think that would cause mass rioting in the streets.

What do you think would be the primary objection that would cause riots?

yjbanov avatar Oct 26 '18 00:10 yjbanov

What do you think would be the primary objection that would cause riots?

There are literally millions of method chains in existing Dart code where the . is on the next line and, as far as I can tell, all Dart users are happy with how they look. I don't think I've ever encountered code in the wild from before dartfmt existed that put the . on the previous line.

The current style is what people want. If we do optional semicolons, it should handle that style gracefully. (Fortunately, in this case, it's easy to do so.)

munificent avatar Oct 26 '18 16:10 munificent

I don't have a preference where the dots should go. However, if putting them on the previous line could unlock new language features by making the grammar stronger, then I think the benefits should outweigh personal preferences or established coding style.

yjbanov avatar Oct 26 '18 17:10 yjbanov