SE-0439: Allow trailing comma in comma-separated lists

Hello Swift community,

The review of SE-0439: Allow trailing comma in comma-separated lists begins now and runs through July 14, 2024.

Reviews are an important part of the Swift evolution process. All review feedback should be either on this forum thread or, if you would like to keep your feedback private, directly to the review manager via the forum messaging feature. When contacting the review manager directly, please keep the proposal link at the top of the message.

Try it out!

You can try this proposal out by using an experimental toolchain (linked below) for your platform and adding -enable-experimental-feature TrailingComma to your build:

What goes into a review?

The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of Swift. When writing your review, here are some questions you might want to answer in your review:

  • What is your evaluation of the proposal?
  • Is the problem being addressed significant enough to warrant a change to Swift?
  • Does this proposal fit well with the feel and direction of Swift?
  • If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
  • How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

More information about the Swift evolution process is available at

https://github.com/apple/swift-evolution/blob/main/process.md

Thank you,

Xiaodi Wu
Review Manager

42 Likes
  • What is your evaluation of the proposal?

-1, this is a stylistic change preferring one style over another that harms readability and forces the creation of formatter and linting rules to disable it. While the compiler may be able to handle it, readers may not, as it makes it hard to determine whether code is correctly written.

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

There is no problem being addressed here, it is simply allowing a desired code style. This creates no new language capabilities and unlocks nothing new for the language.

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

No, it makes code less readable.

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

Yes, and most well structured code bases either enforce their removal or forces the addition of one, for consistency. Lacking those tools, Swift should stay far away from these stylistic allowances.

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

Read through the proposal, getting more and more horrified at the code it enables. No thank you!

17 Likes
  • What is your evaluation of the proposal?

I don’t like the looks of it, but with the examples provided I see the use cases for it, especially the commenting out part.
My dislike of the looks can easily be solved with a formatter, so no big issue there.

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

I guess it will make the life of some developers easier without obvious drawbacks, so yes.

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

I do not see a major change here that could affect the direction of Swift, so yes, it does.
Also I do not see any reason why it would affect some possible future feature of Swift.

5 Likes

I really don’t see the harm in allowing other styles.

The proposal allows to use another style (and brings some QOL improvements at temporarily commenting something out) without, in any way, affecting code not using trailing commas.

I don’t like the looks either but it does not make the code less readable for me and if someone likes to use it, why should the language prevent it?

11 Likes
  • What is your evaluation of the proposal?
    big +1. I run into this frequently when hacking around with APIs. Linter or formatting rules can enforce the style people want, but it would save me a lot of failed builds (which can be 30sec-1min of wasted time) just because my code is in flux. I can't say it looks pretty (some of these examples are quite frightening to behold), but once again, that's for the formatter to decide. I feel the same way every time @taylorswift posts a code snippet with a bunch of weird line breaks between keywords :sweat_smile:

  • Is the problem being addressed significant enough to warrant a change to Swift?
    It's a pretty significant problem in my workflow, but maybe that's just a skill issue

  • Does this proposal fit well with the feel and direction of Swift?
    I think so, seeing how we already have trailing collection commas.

  • If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
    I like how trailing commas don't cause errors in JS and yet get formatted out by my tools.

21 Likes
  • What is your evaluation of the proposal?
    -1. Even though I sometimes wish to be able to leave trailing comma in some of cases, that's not worth complicating language outlook. Yet, personal preferences put aside, I was confused by examples from the proposal as it reaches disallowed cases: there is an odd asymmetry to what is allowed and what's not, despite the reasoning, on top of still somewhat limiting certain cases of codegen.

  • Is the problem being addressed significant enough to warrant a change to Swift?
    Don't think so. It is a minor inconvenience from the developer perspective once in a while, and perfectly solvable task in case of macros.

  • Does this proposal fit well with the feel and direction of Swift?
    It seems alien to Swift outlook, which usually goes towards less symbols in the code.

  • If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
    I've been using GoLang for a while where trailing comma is mandatory for multiline cases, it was odd, yet sound with overall language design and approach (mandatory formatter with its own "no ones preferred" style). With Swift being more openminded on code-style, that's additional detail to consider, and difference from project to project.

  • How much effort did you put into your review? A glance, a quick reading, or an in-depth study?
    Read proposal, part of the initial discussion, and meditated on Go forcing to put comma every time.

3 Likes

While some of the unsupported cases are a bit surprising at first glance, I think since we already have trailing commas in collection literals it makes more sense to expand on that even if we can't do it for 100% of the cases we'd like.

Not being able to use trailing commas in if/guard conditions in particular is a limitation I seem to run into pretty often.

18 Likes

+1. I don’t like the look of trailing commas when the list is written on a single line, as presented in many of the examples in the proposal. I use trailing commas when writing literals across multiple lines and would like to do the same with the proposed use cases.

The proposal is not wrong since whitespace is insignificant to the compiler in these contexts and the presentation is more compact, but I think reformatting the examples to match the common use case of multiple lines would help make trailing commas feel less out of place.

24 Likes

-1, the issue is a real pain, but the proposed solution trades readability for writability (there’s some saying about code should be written to be read by humans that comes to mind).

The underlying issue would be non-existant if we had a proper language-wide opt-outable automatic formatter, much like gofmt. Then these things would be adjusted automatically before compilation.

So to me, this is solving what should be a Swift code editor problem by making the language more complex and allowing less readable constructs.

Edit: these kind of language changes takes us babysteps closer to being a C++ + and would make it more difficult for beginners to understand code.

4 Likes

I think it might be constructive to the ensuing debate to state explicitly that, indeed, this proposal would make code easier to write, and maybe slightly harder to read. It would actually make diffs easier to read, which is not the same as making code easier to read, but is still something that carries at least some value.

So I think it may be relevant to first establish the existence of three distinct activities:

  1. Writing code.
  2. Reading code.
  3. Reading code changes (i.e., diffs).

Now we can consider the relative proportions of time spent on each activity. I’ve often heard something like that code is read 50 times more than it is written, but I doubt that that is more than an intuition that someone expressed long ago that caught on for whatever reason, perhaps partially simply because it’s a nicely round number. Regardless, I’ll use that number for the sake of argument.

We may also want to somehow consider the relative importance of each activity: I think the activity that takes up the least amount of time statistically speaking must surely be reading diffs, but perhaps we should also take into account that bugs are more likely to be caught when reviewing recently changed code as compared to when reading preexisting, committed code.

People often use the 50:1 reading to writing ratio argument to seemingly imply that no effort should be made to facilitate writing code, but I think it would make more sense to simply value impacts to each activity according to the ratio, keeping in mind that 50:1 and 50:0 are very different ratios.

So, in this case, the question would be:

“Is the positive impact on writing at least 50 times more potent than the negative impact on reading?”

I’m not claiming that the answer is obviously and unequivocally “yes” - I do understand why some people could have doubts - but based on my personal experience, the answer does seem to me to be clearly “yes”.

So, +1 from me.

23 Likes

Huge +1 on this, if only for the ability to have trailing commas in multiline function calls & tuples:

object.method(
    arg1: a.long.expression,
    arg2: a.second.long.expression,
)

The current need to "manage" that last comma as arguments get added, removed, and reshuffled is extremely annoying, and leads to git diffs that are harder to read than strictly necessary.

I love that Rust allows this, and use it extensively there.

I see a lot of negative sentiment, which I don't really understand — why should array literals be any different to lists with other delimiters?

Perhaps this is related to the proposal's insistence on writing everything as single-line, when although these extra commas may be valid in single-line, that's not the intended use-case?

I'd also be OK restricting this to only [ and (-delimited contexts (array & dictionary literals, function arguments, tuples) if that made it less controversial. So long as I get my trailing comma in multiline function calls and tuples, I'd be happy!

26 Likes

I would add "changing code" to the list as well (maybe that's intended to be covered by "writing", but it's also kind of separate). As I said, I've run into this mostly when dealing with lists of if/guard conditions, so being able to rearrange, delete or add thing in those lists without regard to which one happens to be last and therefore doesn't need a comma would be quite nice.

8 Likes

A solid +1 here.

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

I recognize my younger self in the “it looks wrong and makes things complicated“ flavor of some of the comments against. I used to feel exactly the same way.

Then, after using the corresponding feature extensively in Ruby and Javascript, I suddenly found it hard to live without — and immensely irritating when absent. The freedom to reorder lists by line is lovely, and quickly stops looking wrong.

(As always in these forums, it seems familiarity reigns supreme.)

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

There are some concerns about the disallowed cases. That's fair; where clauses for protocol members in particular are something one might want to put on multiple lines, and folks will no doubt find it frustrating when the trailing comma is disallowed in that position.

However, the inconsistency these corner cases present is far smaller than the current inconsistency between array/dict literals and function call arguments:

// So in today's Swift, I can do this…
print([
  "foo",
  "bar",
])

// …but not this?!
print(
  "foo",
  "bar",
)

Swift's syntax has always favored “it mostly works for language users” over “it makes sense for parser authors.” This proposal is consistent with that precedent.

How much effort did you put into your review?

A quick read of the full proposal, and a skim of existing comments.

28 Likes

+1 for the purposes of expressions, specifically at the call-site. When separating function parameters on their own lines, I face exactly the same issues that motivated trailing commas for members of array literals. There's an impedance mismatch between the two currently. Variadic arguments especially so.

Trailing commas in parameter lists for function declarations might be useful. In general, I get hit more by the call-site than the declaration site, but for symmetry it could be nice.

Trailing commas in other declarations, especially in types, do look odd. I'm somewhat indifferent and personally would have a coding convention of not using or allowing them. I haven't worked enough with variadic generics to have further intuition here.

My 2 cents: definitely extend the trailing comma allowance for literals to apply to function parameters. Beyond that, I'd defer to the Language Steering Group's insight.

13 Likes

--1.

Consider the following argument.

After seeing the declaration:

struct Object {
   func move (x: Int, x: Int)
   func move (x: Int, y: Int, z: Int)
}

then the statement:

object.move (
    x: 2,
    y: 3,
)

The trailing coma in the argument list would make me think that the real intention was to invoke the second method, which would make me to ponder if this could be a bug.

6 Likes

I am extremely enthusiastic to see this change adopted. I would say that, in iterating on code, I trip up on needing to add/remove the trailing comma from arguments and parameters on an almost daily basis. The ability to trivially comment out individual entries would be a significant quality of life improvement to iterating on code. The lack of this feature is particularly frustrating for tuples, given they are array-like, with all the same reasons why allowing them would be beneficial, and yet don't support this feature like arrays do.

I see that many of the reviewers opposed cite clarity. I do sympathize that this enhancement is primarily targeted at the process of writing code, rather than the final result – code at rest does not need this feature. However, one of the primary goals of Swift is to be enjoyable to write, and personally I find the lack of this feature a major crimp in my enjoyment.

Regarding the clarity/readability argument: I find it hard to believe this impact is material, even if it were present. While this feature differs from array trailing commas, there is little evidence of this harm there.

On the contrary, opposing this change is imposing a stylistic preference. This change will allow either style – trailing or not – to be chosen.

41 Likes

It is, of course, a minor harm, like all stylistic choices in a language. However, all of these minor harms add up, so what you don't find bothersome, or even prefer, can impact those both new and old. So please don't dismiss the concerns of others because you don't see it. Like you said, this is largely a stylistic issue, and brings with it quite subjective opinions. That those opinions differ from yours doesn't mean they don't exist (or that's there's "little evidence").

Sure, in the same way that requiring paired parenthesis, decimal points, or semicolons impose a stylistic preference. Many languages get away without those, or mixing them, any number of things. So I suppose I should say this allows a stylistic preference that has negative consequences for the readability of the code, and pushes Swift further down that path when some in the community already find Swift increasingly unreadable (if for other reasons). But given the proposal directly cites the preference of various repos, it can hardly be said that there's no preferencing going on, as there's an evolution proposal up for review.

In some ways even allowing the choice is a minor harm, as it forces the community to create tooling and form opinions one way or the other. This would be less relevant if Swift had a built-in formatter or linter like Go, but so far it doesn't (I think I read that swift-format now ships with the Swift 6 toolchains though?).

2 Likes

Ignoring the paired parenthesis (though eliding parens, like Ruby, is an interesting and valid choice a language might make), I should point out that decimal points in literals and semicolons are both optional, like trailing commas are for arrays but aren't for tuples. So that seems to reinforce the idea that this change is in keeping with the general direction of Swift.

12 Likes
  • What is your evaluation of the proposal?

-1 absolutely not. This unnecessarily complicates the language for minimal benefit.

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

No. In my opinion, changing the language to address an occasional inconvenience should not even have made it out of the pitch phase.

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

No. This makes the language more complicated. There are more edge cases that people have to learn that are hard to communicate. My daughter is trying to learn Swift over the summer and watching her learn has me tearing my hair out over how ridiculously complicated everything is, and this makes that whole situation far worse. The weird equivalence between , and && in if statements is weird enough, but having "unfinished" lists like this would be a nightmare to explain, especially given how many exceptions there are to the rule.

6 Likes

To be fair, the complication already exists, since today we arbitrarily allow trailing commas in collection literals but not in other comma separated lists. To my reading, this proposal does about as good a job as it can making that trailing comma support unconditional, which IMO is a simplification since developers no longer have to think about the arbitrary restriction. The remaining places where trailing commas aren't allowed seem to me like they can be summarized as, "a statement or declaration cannot end in a comma", which is an unfortunate remaining limitation but at least understandable and based on a real technical limitation.

34 Likes