SE-0257: Eliding commas from multiline expression lists

I agree that this concern should be taken very seriously. However, it was addressed in detail by @Douglas_Gregor in the pitch thread. Nobody (including @Chris_Lattner3) replied to Doug’s post explaining why any part of it might be inaccurate.

If there is a disagreement about facts related to this fundamental point of concern on the core team it would be nice to see it resolved during this review so reviewers can at least have a shared set of facts to consider. Right now, I think everyone is assuming that the perspective that aligns with their preference is factual, taking the word of either @Douglas_Gregor or @Chris_Lattner3 as fact (depending on their preference).

I was personally on the fence about this proposal until reading Doug’s post. After reading that post and looking at a number of EDSL examples in detail I became a big fan (assuming Doug’s post is accurate). Yes, it will require good judgement to use well, but that is true of many things in programming. IMO, the benefits outweigh the costs (at least for programmers capable of exercising good judgement).

2 Likes

What is your evaluation of the proposal?

+1

Variadics add a layer that could be awkward.

If this is applied to function calls it would be nice to also see this implemented for function declarations, when written one argument per line.

Is the problem being addressed significant enough to warrant a change to Swift?

This would be nice to have.

Does this proposal fit well with the feel and direction of Swift?

I think this parallels the removal of semicolons. In the cases outlined in the proposal the commas do add extra noise similar to semicolons.

I could see it causing some minor confusion when reading arrays. That said, I do still find it cleaner and should facilitate when refactoring requires rearranging, adding, or removing items.

If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

Function calls, when parameters are labeled, are similar Objective-C message passing. I think dictionaries may also be read similarly to labeled function calls.

Many languages I've used allow trailing commas. This (SE-0084 previously rejected) would also solve issues with diffing and refactoring, although they wouldn't help with noise reduction for readability.

How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

Quick reading of the proposal and discussion.

This is an overstatement of my view of the proposal. My view is that comma separators do not provide value to humans in some important contexts. Specifically, I have looked at a range of EDSL examples which now appear cluttered to me when commas are present. These examples look much better and more clear without commas for all the same reasons Swift code looks much better without semicolons.

Domain specific languages often aim to be syntactically light, allowing the domain itself to be front and center. These languages often consist of lists of declarations (which often include lists of attributes). Nobody in their right mind would design such a language to require commas between each declaration and / or attribute. They are usually designed with one declaration or attribute per line with no line terminator. This proposal supports the embedding of such designs into Swift without the currently required syntactic compromise of trailing commas. These EDSLs can be designed with comma elision in mind, avoiding most or all of the gotchas that may occur when comma elision is used in more general settings.

IMO, supporting the embedding of such languages into Swift in a syntactically light manner is a huge win. Of course YMMV and the benefit any individual Swift programmer realizes will depend upon how often they use declarative EDSLs.

Can you please provide an example of how this proposal breaks existing Swift source code all of which currently uses commas? This proposal does not remove support for commas and does not suggest that people should never use commas except where absolutely necessary. I believe it will require good judgment to decide where comma elision is appropriate and where it is not. I also agree with @John_McCall’s suggestion that a list should either have commas on every line or no lines. IMO, single commas for the purpose of disambiguation would be bad style.

You never responded to @Douglas_Gregor’s disagreement on this point. If you engage this topic in an attempt to get on the same page with him you may end up convincing me to change my mind about comma elision. I don’t have the expertise the two of you have and would appreciate it very much if you were able to agree on the facts.

See the discussion above about EDSLs. If you don’t feel that supporting EDSLs in this way is important, can you elaborate on why you don’t?

Regarding trailing commas, this is something I also very much want to see. I want my multiline lists to have consistent lines, either commas or no commas. This looks cleaner and makes editing easier.

2 Likes

@anandabits, well stated. My experience was similar.

On April 5, I read @Douglas_Gregor's very short response:

Juxtaposition is already restricted by semicolon elision for statements. It's not a viable mechanism for new bricks (which I hope are tasty ones), so this sugar isn't cutting off future evolution.

... and saw no reply to it. I felt much as you probably did: the absence of a response by @Chris_Lattner3 probably indicates that Doug's analysis is definitive.I was wishing for a more thorough explanation from Doug, but for purposes of theoretical discussion of the matter his two-sentence statement was sufficient at the pitch stage.

Chris' reassertion of his original statement, here, reveals my assumption to be incorrect. There seems to be a real difference of opinion on the matter. I do not believe one over the other. Instead, I find myself disregarding other priorities in my life in order to examine the compiler code and think hard about the question. I do not have sufficient experience in the matter to draw proper judgments, but I hope to add to the discussion.

The difference needs to be resolved.

Imho you can't blame the proposal author for this: Chris' concerns may be strong, but not very specific; I don't think that you can expect someone whose insights in the future of Swift are probably limited (at least in comparison) to effectively argue against his statement.
It's possible that this proposal doesn't play well with upcoming changes, but not even the core team can predict the future - and in the end, it's their decision anyways.

My take: -1. I'm not against streamlining syntax in favor of more compact but better readable code.

What I've seen so far is a step towards Python's world where whitespace matters a lot. A clumsy auto formatter could cause real damage on code having such an ambiguity (think of condensing a multi-line array expression into a single without placing separators between members).

Downloadable toolchains with this feature are available here:

cc @Paul_Cantrell @Jens

4 Likes

+1. It seems to me that all those raised "issues" can be made to semicolons and invalid.

I'm totally convinced with this line.

4 Likes

+1

Yes, the ability to choose when commas will aid clarity and when they won't is very much welcome's.

Yes. Swift is very close to being great from a eDSL standpoint. This helps it get closer.

The fact that we would be able to add commas where desired puts it above some implementations but, overall, it is on par.

I followed the pitch and read the proposal.

I don't have strong feelings about the proposal, but I'll note that though EDSLs are a primary motivation, most of the EDSLs I work on rely heavily on dot-prefix syntax and wouldn't get to take advantage of comma elision.

2 Likes

After reading some other reviews, in particular @Chris_Lattner3's, I'm changing mine to a -1. I still really want trailing commas, but I feel making them optional will restrict future evolution too much.

Strong –1 from me. Most of the points I would have made have already been made in more detail by @Chris_Lattner3 and others, and while I was somewhat mollified by some of the Core Team's thoughts on the matter, for me it ultimately boils down to:

  • I don't agree with the premise that commas are "visual clutter" to such a significant degree that they should be elided. The proposal makes a lot of very subjective claims like "the difference is subtle but striking" but there's not really any evidence of that.

  • I believe this change would harm Swift's goal of being easy to learn by newcomers. It makes the language less consistent, because now users have a choice between using commas or not in a multi-line situation. The proposal defeats itself by having a two-page long section titled "When will you still use commas?", because this is a list of exceptions that users now have to internalize and I think it serves as great evidence of why we should not do this.

    To be fair, the same claim could be made for semicolons; on that point I would say that I wish Swift had never supported semicolons to begin with, instead of making them optional. But I strongly disagree that commas and semicolons are comparable here. It's not terribly common in Swift (or any other modern semicolon language) to want to put multiple delimited statements on the same line to a significant degree—programmers rarely ask themselves "should I put these N statements on the same line or lay them out across multiple lines" because the obvious answer is almost always "of course they should be on separate lines," so there's not really a decision to be made.

    Making decisions about laying out comma-delimited lists horizontally or vertically (or a combination of the two) occurs far more frequently, and I don't see a strong reason to allow users to make a completely arbitrary choice here.

  • I also share the concerns mentioned elsewhere in the thread that arbitrarily allowing commas to be elided could hamper future evolution by making it more difficult to express certain syntactic constructs that we may want to add.

When SE-0084 (trailing commas in all comma-delimited lists) was under review, I disagreed with that proposal as well, for a fairly technical reason—argument lists tend to be heterogeneous and fixed size whereas collection literals are mostly homogeneous and more likely subject to change. Since then, I've done some more work in Typescript (which, like Javascript, allows trailing commas in the situations where they were proposed in SE-0084), and have also used a formatter that enforces them when line-wrapping such declarations.

As such, I've reconsidered my objection to trailing commas. If one of the stronger motivating factors for comma elision is to improve consistency of argument lists and to make it easier to write DSL-like code that can be shifted around easily without worrying about punctuation, then if I were given the choice between trailing commas and comma elision, I would strongly prefer trailing commas.

14 Likes

I feel like everyone overlooked this in the pitch…

3 Likes

A strong -1

No. I have never looked at code and thought "you know what this needs? fewer commas". This is change that evolves the language to be more complex for little benefit. What benefit there might be has not been clearly demonstrated.

Unclear, but leaning towards no.

N/A

Read the proposal, read the whole thread.

1 Like

What he said.

Strong -1. I disagree with the premise that commas add no value to the reader.

No. I also have never heard anyone complain about using commas in lists (in any language). I personally get more annoyed when I read Ruby and no commas are there to communicate to me that separate elements are being listed.

No. Precisely because Swift has decided that semicolons are optional, it has gained expressivity where we can continue expressions on the next line and it's as if it was on a single line. At the same time, new statements are actually interpreted as new statements when on separate lines. In other words, "it just works".

In the case of commas, removing them seems to actually loose expressivity due to the exact reasons listed in "When will you still use commas?" in this proposal. Sure, in those cases the user can just use commas, but I don't think this philosophy fits with Swift.

Swift stands for readability, and commas help communicate to me the boundaries between elements. This is otherwise hard to see since expressions can span multiple lines (which is a good thing in Swift).

This seems very Ruby-like to me, which as I mentioned above annoys me because intuitively I expect lists to be comma-separated.

I read the proposal and some of the responses.

2 Likes
  • What is your evaluation of the proposal?

At first I was -1 but now I am +1, IF there is no case that is ambiguous.
(But I still would love to see the opposite, too: Allow dangling commas as described in my comment here)

  • Is the problem being addressed significant enough to warrant a change to Swift?

Yes. Makes Swift consistent in respect to semicolons.

  • Does this proposal fit well with the feel and direction of Swift?

No opinion.

  • If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

Shell-Scripts use space as argument separators.

  • How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

Quick reading.

Strong -1.

I largely didn’t care either way until I saw @hartbit’s post, but since it convinced me to speak up, I will share all my thoughts. (Because I like suspense, the third is the most important.)

  1. The proposal has the general premise that commas are just unhelpful noise, and that intent is clearer without them. That idea seems strange to me.

    No writing used commas until about 300 A.D. when Aristophanes invented them to make reading easier. They were such an improvement that their use has spread to every human language on the planet, and none of them have ever changed their minds.

    Even where other formatting makes them “unnecessary”, every style guide I have ever read requires using them anyway because they improve readability:

    You can express your disagreement with me by:

    • replying with a thumbs down emoji,
    • replying with a detailed explanation of why my reasoning is flawed,
    • asking the administrators to block my account,
    • sending me hate mail, or
    • hacking my account to invert all my opinions.

    However, only one of those is a wise course of action.

    The cumulative experience of 1700 years and 7 000 000 000 users seems to suggest that commas are helpful.

    I am saying here only that I don’t understand the utility. I do not object to others having access to something I don’t need—as long as the consequences aren’t too severe. For that, see the third thought below.

  2. I am aware that tooling for tracking changes to source tends to work on a line‐by‐line basis. Since joining commas may not share the line with both joined elements, changes can appear to affect more than they actually do. Such change reports are deceptive and suboptimal. (But they really aren’t that bad.)

    The real solution to this would be difference reporting based on the tokenized file instead of based on lines. Git can already be rigged up with any arbitrary difference reporting tool. All it would take would be for someone to put together a tool running on SwiftSyntax or SourceKit‐LSP. If it were an official enough part of the Swift project, it could be included in the Git that is shipped with Xcode and probably even submitted for GitHub to use in its interface.

    If we really need a compromise solution faster than that, then despite my natural aversion to the illogical trailing comma, I have to agree with @allevato:

  3. @hartbit’s first example terrifies me, because the ambiguity occurs far from the cause in a way that is not naturally obvious when writing the triggering code. Let me rearrange it slightly:

    Here is a package “Library”:

    public enum FooBar {
         case foo
         case bar
    }
    

    The package is tagged as version 1.0.0 and released.

    Now here is someone else’s package “Client” which discovers the first package and links against it:

    import Library // @ 1.0.0 ..< 2.0.0
    let a: [FooBar] = [
        FooBar.foo
        .bar
    ]
    assert(a.count == 2) // ✓
    

    Everything works well so “Client” is tagged as 1.0.0 and released. The client is doing nothing foolish. It does not conform an unowned type to an unowned protocol. It does not declare any symbol likely to be subsumed upstream and lead to a name clash (which in most cases would simply trigger shadowing by the same‐module definition anyway). The client is following all the rules.

    Now the “Library” authors discover utility in convenience properties for enumerations, so they add some:

    extension FooBar {
        public var foo: FooBar? { /* ... */ }
        public var bar: FooBar? { /* ... */ }
    }
    

    They tag it as 1.1.0 and release. Again, the package authors are properly following semantic versioning and doing everything right—or so we all think. Do we really expect them to realize they just made a source‐breaking change?

    That’s right; source‐breaking. The “Client” package is now broken. It still says this:

    import Library // @ 1.0.0 ..< 2.0.0
    let a: [FooBar] = [
        FooBar.foo
        .bar // 💥
    ]
    assert(a.count == 2)
    

    Is there any part of Swift in its current state that is comparably unpredictable when it comes to API stability? If there is, I am not aware of it.

4 Likes

There is no ambiguity here; the assertion above will never pass.

The array literal is always parsed to contain exactly one element, the expression FooBar.foo.bar. This doesn't depend on what cases or properties FooBar might have.

2 Likes