SE-0257: Eliding commas from multiline expression lists

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.

8 Likes

-1 echoing the comments made by @chrislattner earlier on. It seems very late to add such syntactic sugar and it seems to add complexity and special cases for minor visual benefits: I think Doug addressed the Swift language ability to “drop new bricks of functionality in and out smoothly” by saying this does not kill it on its own as the fatal blow for that was actually the optional elision of semicolons... not helping to support this case honestly, but making me wish we did not have that feature and wonder if eliding semicolons was actually worth it (although people love not to have to type them, I am not sure we appreciate the cost for such a minor thing IMHO).

Would you, or @Chris_Lattner3, mind expanding on this point? I'm having a lot of trouble understanding it. It's very hard for me to imagine a language feature that relies on the presence of commas in collection literals and function calls for disambiguation, but not semicolons between statements in other contexts like method bodies. Are you talking about some contextual feature that is only present in function argument lists? Is there a concrete example that would clarify this for me?

1 Like

I want to emphasize my -1 with this example from the proposal:

let table = Table(
    name: "Employees"
    columns:
        guid ("record_id", isPrimaryKey: true, nullable: false)
        guid ("manager_id", isPrimaryKey: false, nullable: true)
        string ("name", length: 1024, nullable: false)
        int64 ("employee_id", nullable: false)
        date ("start_date", nullable: false)
)

This is IMHO horrible and I hope it never catches on in Swift.
I have no idea at what I am looking here. What is guid and int64? Are those function names, parameters followed by tuples?
I think we should not obscure which building blocks of a language are used, and the trailing closure syntax is about as far as it should go.

This is very much IMHO though and I don't want to disparage the incredible effort and thought the authors have put into this proposal.

4 Likes

jawbroken
April 10
mattrips:
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.

Would you, or @Chris_Lattner3, mind expanding on this point? I'm having a lot of trouble understanding it. It's very hard for me to imagine a language feature that relies on the presence of commas in collection literals and function calls for disambiguation, but not semicolons between statements.

Which would make a point for not allowing semicolon elision more than making a point for the elision of commas. Similar point was made by Doug G. I believe and to me it kind of hints that the community did not explore or appreciate in depth the cost of granting such a minor visual thing (do we think that Swift would have lost users if it never had semicolon elision? I would disagree).

The compiler, which practically speaking is the embodiment of the language, divines meaning from source code via an elaborate set of tests. Individually, most of those tests are straightforward, while some are quite complex and occasionally brittle. Generally, adding flexibility in the source code representation of the language results in the need for more tests and more complicated tests. Generally, as the body of tests increases in complexity, it becomes more difficult to introduce new or different tests within that body. The tests needed for one feature can collide with the tests needed for another feature.

Take the semicolon as an example. Mandating a semicolon as a statement separator makes for relatively simple tests: each statement is bounded by, at one end, the beginning of its containing file or block or the end of the preceding statement, and, at the other end, a semicolon. Replacing the semicolon mandate with a rule based upon newline characters substantially complicates the tests. A newline character may or may not be the end of a statement. A large number of tests are needed to determine whether any given newline character ends a statement or is merely a formatting preference. Those tests depend greatly upon the surrounding context. From that context, the tests infer the intent.

Inferring intent from context is expensive. Far more expensive than explicitly declaring intent. With every newline character, the compiler must go through its tests, and draw its inferences. Explicit semicolons ease the burden, thus allowing the compiler to apply its resources to other problems.

Continuing with the semicolon example. Imagine the implementation of a future language feature. That implementation would be facilitated by its own set of tests in the compiler to contextually resolve user intent. It easily could be that some of those needed tests would collide with the tests used to implement the absence of semicolons. It easily could be that the collision would be irreconcilable, thus forcing the new feature to be redesigned with a less desirable source code representation, or abandoned.

Context is a resource, and as a resource it should be husbanded carefully. When context is used to implement a new rule, such as eliding commas, that same context may cease to be usable for purposes of implementing future language features.

Commas are pervasive. Comma elision would be pervasive. To me, there is no doubt that comma elision would restrict the future evolution of Swift. With that said, the question of how significant the impact would be is unanswered. Studying that question would be a major undertaking. For this sort of pervasive change , that sort of study is necessary. Anything less would be a blind leap.

1 Like

I don't have a clear +1 or -1 to give. As this is purely syntactic taste we can find subjective arguments for both sides. Having syntactic discussions is always hard.

The only problem I personally encounter with commas is the fact that I can write a comma at after the last element of a literal array but not at the end of a parameter list. That's really the only thing that annoys me about commas in the language, I don't have any issue with the rest of usages.

I have the same feelings about many style guides, is hard to decide in a rule because saying that something is readable or not is not a science. I'm not distracted by commas so I don't mind having them, one is used to it, but would I miss them? I doubt it.

The only strong feeling I have is about language semantics, error reporting, etc. I would leave to the more knowledgeable ones to make sure that accepting this doesn't make error reporting or edge cases worse. Yes we can always write a comma, but if we get used to not do it and then we need to think about edge cases or ambiguities we are gonna be in a worse position.

One could argue in both sides.

  • No semicolons -> no commas
  • No need for self -> no commas
  • importance of clarity -> yes commas

I don't think I have enough experience with languages that do this to argue in one way or another.

Followed the pitch closely because it seemed to open a big can of worms :joy: and read the proposal.

  • What is your evaluation of the proposal?

Strong -1 for me too for three seperate reasons:

Ambiguity

This proposal introduces several ambiguities which can have a severe impact when writing Swift and more importantly, when reading it, especially in environments with no access to any kind of tooling (code review online for example).

Static Member Ambiguity

The proposal mentions this ambiguity. I've written a concrete example which I think is fairly believable (users do tend to define properties on enums for avoiding to use the pattern matching syntax) which parses and type-checks successfully but differently depending on the presence of the comma.

enum FooBar {
    case foo
    case bar

    var foo: FooBar? {
        if case .foo = self {
            return self
        } else {
            return nil
        }
    }

    var bar: FooBar? {
        if case .bar = self {
            return self
        } else {
            return nil
        }
    }
}

let a = [
    FooBar.foo
    .bar // enum case or property?
]

Closure Ambiguity

The proposal does not mention this ambiguity and I've not seen anybody mention it. Again, the following piece of code parses and type-checks successfully but differently depending on the presence of the comma.

func doSomething(_ action: (Any) -> Void) -> Void {
    action("something")
}

let b = [
    doSomething
    { _ in print("next") } // closure or trailing closure?
]

Consequences of Ambiguity

As a consequence of the previous ambiguities, the proposal mentions that the comma can sometimes be necesary to disambiguate. I personally think that the noise from one off commas are worse than consisteny commas:

// While no commas is nice

foo(
	foo
	baz
	.bar
	foobar
)

// I find one-off commas

foo(
	foo
	baz,
	.bar
	foobar
)

// worse than consistent commas

foo(
	foo,
	baz,
	.bar,
	foobar
)

Editing Inconveniences

While I agree that code is more often read than written, I think that this change can cause some minor editing annoyances. I often find myself rewriting expression lists from single-line to multi-line when they grow too long to nicely fit on one line, and from multi-line to single-line when they shrink enough to be more readable on one line. If one chooses to take advantage of this proposal to ommit trailing commas in multi-line expression lists, switching back and forth between single and multi-line can be more annoying than currently because of having to add/remove commas.

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

This proposal is interesting for EDSLs. But in my opinion, the small benefit it brings is not worth the list of disadvantages presented above.

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

No opinion here.

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

I don't know of a language with a similar feature. It would be interesting to research if there exists one and how that feature works in that language.

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

A good read.

15 Likes

-1, after some internal back and forth.

I do not think so for reasons that have been stated before. Mainly, the syntactical ambiguities that arise by omitting the commas are very problematic, IMHO.

I should maybe point out that I’m kind of meh on the whole topic of allowing trailing commas everywhere. The motivations that brought up that argument also don’t move me here.

Yes and no. Yes in that I understand the arguments made in the proposal arguably can be perceived as “looking nicer”. No in that the ambiguities in terms of how these things can be written and how they can be parsed muddy up the language.

I think we’d give up some amount of clarity (and potential for future changes as @Chris_Lattner3 argued) for something that would probably remain nothing more than a stylistic choice for most users. I obviously don’t know for sure, but I very unscientifically suspect that omitting commas would not be something that the overwhelming majority of Swift developers would do (in contrast to omitting semicolons).

I also find the examples given in the proposal unconvincing. As has been written before: What are int64 or guid in these examples? Free functions? To me, enums feel better suited in this particular example and those wouldn’t work because of the static vs. instance member lookup ambiguities.

No.

I haven’t come across this topic before and only read the proposal and this thread.

The problem of ambiguity will be solved if a formatter (SE-0250: Swift Code Style Guidelines and Formatter) is adopted and Xcode also cooperates with it. Expressions connected to the previous line are easy to understand if nested one level.

For example, if formatted as below, it is easy to understand.

_ = [
    "foo": foo
        .method()
    "bar": baz
]

let solutions = [
    2*(1-sqrt(a))*sqrt(b+sqrt(b))
    -(1+sqrt(a))*(sqrt(2*b)-sqrt(2))
] // two solutions
    
let solutions = [
    2*(1-sqrt(a))*sqrt(b+sqrt(b))
        - (1+sqrt(a))*(sqrt(2*b)-sqrt(2))
] // one solution

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

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

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

I am not sure it is a good argument as it puts a solution with drawbacks dependent on another Mother of All Discussions kind of decisions which has its own pain points. It kinds of makes the -1 stronger for me, not weaker :/...

3 Likes

If we need a code formatting tool to disambiguate and understand the code, we might as well go the route of Python and make whitespace have meaning.

That was sarcasm, I do not propose this.

3 Likes

-1
Just like we see the

if (x = 1) {}

in the old day, it's useful in some situation but could make the code much less readable.

I do appreciate the clean look of no commas, but after consideration of the pros & cons enumerated by others in this thread, I come down on the side of:

-1

I’d rather see us go the other way—allowing trailing commas in expression lists. My biggest comma-related pain point in swift by far is the inability to use trailing commas in multi-expression guards.

3 Likes

I said that the recognized code structure of the parser can be well visualized by the formatter.
It is not that we should control parser behavior by indentation (like Python does).

I don't see how any of this is specific to whether there are commas on the end of those lines or not.

Okay, but your ship sailed on this before even the first public announcement of Swift. Semicolon elision is a feature of Swift (and many/most other recent languages) and will never go away. And the constraints that semicolon elision places on how multi-line statements are parsed are similarly not going away.

This is all very general and it doesn't shed any light on the specific statement I was asking about. Any syntax change needs tests, and necessarily stops the exact same syntax in the same context from being used for some other purpose. However, considering this specific proposal, the rules are approximately just an expansion of the already-existing semicolon elision feature which will obviously not be removed. Swift already needs to be able to work out if a piece of code is either a statement that continues over multiple lines or is multiple independent statements. So any hypothetical future language feature that this change precluded would seem to me to have to be restricted to the context of expression lists. Do you have any example of such a feature? I honestly can't think of one.

I am interested in addressing confusing cases like the ones you mention, and those that I've posted previously in the thread. These specific examples could all be addressed by e.g. warnings when a statement is split over multiple lines in an expression list without trailing commas. This would be similar to how unused result warnings let you know if your multi-line statement is actually two statements when semicolons are elided. Would that make it more acceptable to you?

It wouldn't be acceptable to me. For a series of statements, I would generally not want an expression to be treated as a separate statement and have its value discarded. When I do want that behavior, I don't mind adding _ = to the single line of code to clarify my intent and silence the warning.

With lists, I would have to silence the warning by adding a comma. Adding a single comma in the middle of a list doesn't sit right with me, and thus I'd have to add commas to all entries. And now my desire to have, e.g. reasonably readable long expressions, forces me to add commas to unrelated lines.

I realize this is purely a style choice, but I don't think a mix of comma-separated and newline separated entries will be good form.

It would make it more acceptable, but then there's still the issues I raised in my Consequences of Ambiguity section. Those are more subjective arguments, but so is the proposal in a way :man_shrugging:

Function application in Swift does not allow separation of foo and (6 * 9) so I guess x.count would have to be 2 there.

The same rule means that (in current Swift) this compiles:

let x = foo(bar: 6)

but this is an error:

let x = foo
    (bar: 6) // Error: Cannot create a single-element tuple with an element label

So if your example is "unclear" then I guess the following is too (today):

func foo() -> Int { return 123 }

let x = foo
    ()

print(x) // Will this print 123 or (Function)?
3 Likes

I agree with this

3 Likes