SE-0257: Eliding commas from multiline expression lists

Yes, the following is a valid Swift program:

func foo(bar: Int) { print(bar) }

foo(
  bar
  : 1
)

It compiles fine and prints 1.

We should probably stop taking up space in this thread with stuff that everyone can simply test and/or discuss elsewhere.

2 Likes

I have mixed feelings here, and I’m not keen on the specifics being proposed.

Commas are syntactically light-weight, and at the end of a line they are barely noticeable. This works both ways—on one hand, they are so minor that they are neither noisy nor distracting, so there’s no harm in keeping them. On the other hand, they don’t really add much visual information either, so it’s probably not a big loss if they go.

From a purely “clarity of reading” perspective, I don’t see much difference either way. Taking an example from the proposal, I think that aligning the colons in a function call provides a vastly greater benefit to readability than anything comma-related:

let config = TestConfig(
  delim         : "abc",
  sampleTime    : 42.0,
  numIters      : nil,
  numSamples    : nil,
  quantile      : nil,
  delta         : true,
  verbose       : false,
  logMemory     : true,
  afterRunSleep : nil
)

This way it is much easier to see at a glance what values are being passed in which order, and it is *also* easier to locate any particular argument label since there is more space around them. By contrast, the presence or absence of trailing commas has a frankly negligible impact on readability.

• • •

If we were to allow commas to be omitted from lists, I would want to place some restrictions on when it can be done:

1. Consistency within a list
If any item in the list (other than the last) omits a trailing comma, then all items must omit them. Mixed lists would be a mess to untangle when reading code.

2. Single-line items only
If any item in the list spans more than one line, then the list must use trailing commas. We should not have to contend with multiline entries in a new-line delimited list. This solves the ambiguity problem by requiring commas in those cases.

• • •

I don’t have a strong opinion overall, except that a change of this nature should need to be found not only beneficial and desirable, but also non-harmful. We certainly don’t want to get in a situation where we accept something like this, only to later find out there were unforeseen problems that make us want to roll it back.

We’ve been through that once before with access control, and it would not be good to repeat such an exercise.

1 Like

This would be…well, "impossible" is perhaps too strong a word, so let's just say "not straightforward". The problem is that you want an argument list to be parsed differently depending on the declaration it calls, but the declaration being called is not unambiguously known until typechecking, which can't be performed until parsing is long over. If it could be done, it would take a terrifying hack.

Sorry, I’m not in front of my computer right now and can’t check.

The reason I’m asking is that I’m trying to figure out if the original example that @jawbroken posted is an oversight in the grammar spec (and should be corrected) or if such very subtle differences in code are intended to be allowed. If they are, it seems like there could be some unfortunate interactions between that grammar and elided commas WRT variadic argument lists.

Parsing could be done with the assumption that commas are optional and but then compiler issues an error during type-checking if they’re not allowed. Not sure if that’s considered a hack but as I said it’s a crazy idea :stuck_out_tongue:

Probably need to discuss this on another tread

1 Like

Would it still be terrifying if the annotation was made for a body of code? Like

func foo() {
  // calls in here need all the commas
  @commaAreOptional {
    // calls in here don’t need as many commas
  }
}

(I’m not so much asking if this is a good idea, just whether it’d still be hacky to implement)

I assume that you're hoping to get some of the "exceptional" cases, like leading dot syntax, interpreted differently when the declaration is @commaAreOptional. If you aren't and you're just suggesting we should diagnose an error when you use this feature with a declaration that hasn't been blessed, I don't think the inconsistency is buying you anything.

True but it’ll at least avoid the inconsistent looking code that we can now end up with:

let foo = [
1,
2
3
]

Maybe a linter is a better tool for catching that but it’s a little annoying that this will be now possible to do unintentionally.

Is it possible to correctly parse when the type is declared in another module?

There's no similar warning or error for semicolon consistency

let a = 1;
let b = 2
let c = 3

so I don't see the need for this. Anything other than potentially confusing cases (which might deserve a warning) should be handled by linting/formatting. People who care will naturally be consistent, and people who don't care won't be bothered by inconsistency.

Edit: i.e. In the following, I strongly believe that 1 shouldn't be addressed, but 2 should be discussed

That's right. This isn't a vote, and we'd prefer more detailed feedback — but if it's all you've got time and energy for, then "+1" or "-1" or "I agree with Xiaodi" is a lot more meaningful to us than a :heart:. As a review manager, I'm not going to crawl over the thread collating likes.

4 Likes

Mildly +1, assuming tooling doesn’t suffer. I’ve always wanted Swift to allow trailing commas and use them in literals, but this seems like a viable alternative.

Having only looked at this briefly, I’m -1 on this feature. The fact that there are edge cases which require special handling or are visually ambiguous is enough for me to not want this in Swift. By contrast, I never find I have to add semicolons to disambiguate or to make things visually clear.

1 Like

If you want a consistency rule, we can do that with pure syntax. (I don’t think we should have one of those either—forbidding ugly uses of syntax is a rabbit hole the size of an oil well—but a syntactic rule would at least be simpler and more direct.)

1 Like

I'm -1 on this because I think it makes the code visually ambiguous (not for the compiler but me as a reader).
I imagine it is also harder to visually parse what's going on for newcomers to the language if there are multiple ways of spelling out things.

It for sure take me longer to grasp what I'm actually looking at when there are no commas.

3 Likes

Strong -1. This is a syntactic sugar proposal that does not even attempt to live up to the standards of sugar proposals (enabling new kinds of expressivity to improve clarity), which introduces more ambiguities into the grammar, and could cut off future lines of evolution.

This sort of proposal makes Swift less uniform and pushes it towards the direction of Perl, where early in its life, people spent time trying to "make syntax work" and it turned into a pile of special cases.

This proposal is akin to allowing the colon to be emitted between a variable name and a type (golang style) in certain cases. Yes, it could be done in certain cases, but not all, and the irregularity introduced would harm the language for almost zero benefit.

No. I have never heard anyone complain about this in a serious way.

No, it reduces clarity, and introduced grammar ambiguities that don't otherwise exist. What is the meaning of this code, and why do we want it to be "unclear"?

var x : Any = [
    foo
    (6 * 9) // Function application or separate value?
]

The "When will you still use commas" section points out several other weird and non-obvious cases that make the language harder to understand.

If this proposal were to be taken seriously, it should also allow omission of the semicolon in single-line statements lists "in the cases when it could work".

I've participated into this in the various incarnations that have spanned various pitch threads. The concerns I raised before have not been addressed.

These are the points of concern I raised on the pitch thread:

  • The entire premise of this proposal is that separators do not provide value to humans. This is not obviously true and I would like to see some evidence to defend this position.
  • As you point out in your 'exceptions' section, the Swift grammar relies on juxtaposition and sequencing as part of the existing grammar. This is a source breaking change for at least some cases.
  • Taking this significantly restricts future evolution of the language, by taking away juxtaposition from the expression grammar. This is extremely problematic for me given the early development of the Swift language. We should not be pouring sugar in as tasty mortar to fill the grammatical cracks in the Swift building - we need to be able to drop entirely new bricks into the language in the future, and that sugar will get in the way, and make evolution much more difficult.
  • This is trivial sugar with almost no benefit. The only precedented discussions we have had along these lines have been about allowing an extra comma at the end of an argument list (to allow copy and paste) and those have been extremely controversial to say the least. I don't see why we'd go completely the opposite way and take away all separators.
  • I suspect that this will significantly regress QoI on error diagnostics in common cases in the user/IDE experience.

-Chris

41 Likes

To be fair, here

let foo: (Int) -> Int = { $0 * 2 }

let x = foo
  (6 * 9) // Function application or separate value?

Though (presumably, without checking) this has the benefit of the unused result warning like in my first post about prefix/infix operators.

I like it a lot. I’ve hit the problem of commenting out some lines and having the compiler complain enough times that I think it’s a major hindrance, and I like the way the code looks without commas. Commas are the new semicolons.

3 Likes

Strong -1.

The arguments exposed by Chris, mainly the reduced flexibility for future language evolution, in my opinion overweighted the convenience when commenting out lines of code.

1 Like

This concern should be taken very seriously. It was raised in the most-recent pitch thread, and not addressed, there, and has not been addressed in the proposal, at all.

The proposal does not engage in a discussion of the role of juxtaposition as a tool in future evolution, and does not even mention the possibility.

It is surprising to me that the proposal makes no effort whatsoever to address a fundamental, credible concern. I do not expect the proposal authors to have a crystal ball view into future evolution. I do expect the proposal authors to engage in an analysis of the current role of juxtaposition in the language's grammar toolbox and of other roles it might play in the future. At a bare minimum, any proposal that has received this level of concern at the pitch stage should explicitly describe the concern within the body of the proposal and explain why the concern is not valid.

On that basis, alone, this proposal should be rejected. Swift is an immensely valuable project with a bright and open future. A "significant restriction" on that future should be subjected to deeply intense vetting.

9 Likes