Skip to content

SIP-27: Trailing Commas, revised document #625

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Nov 25, 2016
Merged
174 changes: 123 additions & 51 deletions sips/completed/_posts/2016-06-25-trailing-commas.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,33 @@ layout: sip
disqus: true
title: SIP-27 - Trailing Commas

vote-status: accepted
vote-status: under review
vote-text: The following proposal needs to be updated, since only the specialized case version (with new lines) has been accepted. For more information, check the <a href="http://docs.scala-lang.org/sips/minutes/sip-20th-september-minutes.html">minutes</a>.
---

// TODO: Move from sips/completed to sips/pending

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to leave the TODO here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm removing the TODO and moving the document in #631, which can be merged after this is merged.

**By: Dale Wijnand**

## History

| Date | Version |
| ---------------|-------------------------------------------------|
| Jun 25th 2016 | Initial Draft |
| Jun 27th 2016 | Added drawback of changing existing tools |
| Jun 27th 2016 | Added motivation that it simplifies codegen |
| Jun 28th 2016 | Fixed a typo |
| Aug 10th 2016 | Renamed from SIP-NN to SIP-27 |
| Aug 10th 2016 | Changed scala-commas URL (repo was moved) |
| Aug 10th 2016 | Dialed back some of the language from review |
| Sep 04th 2016 | Split the motivation into sections |
| Sep 04th 2016 | Add VCS authorship attribution to motivation |
| Sep 04th 2016 | Add Cross building hinderance to drawbacks |
| Sep 12th 2016 | Remove Cross building hinderance from drawbacks |
| Date | Version |
| ---------------|------------------------------------------------------------|
| Jun 25th 2016 | Initial Draft ([#533][]) |
| Jun 27th 2016 | New drawback: changing existing tools ([#533][]) |
| Jun 27th 2016 | New motivation: simplifies codegen ([#533][]) |
| Aug 10th 2016 | SIP numbered: Renamed to SIP-27 ([#533][]) |
| Aug 10th 2016 | Changed scala-commas URL (repo was moved) ([#533][]) |
| Aug 10th 2016 | Dialed back some of the language ([#533][]) |
| Sep 04th 2016 | Split the motivation into sections ([#533][]) |
| Sep 04th 2016 | New motivation: VCS authorship attribution ([#533][]) |
| Sep 04th 2016 | New drawback: Cross building hinderance ([#533][]) |
| Sep 12th 2016 | Remove cross building hinderance from drawbacks ([#533][]) |
| Nov 12th 2016 | Major rework: multi-line, 2 variants & spec ([#625][]) |

## Motivation

### Easy to modify lists
### Ease of modification

When using a comma-separated sequence of elements on multiple lines, such as:

Expand All @@ -39,33 +41,33 @@ Seq(
)
{% endhighlight %}

It is quite inconvenient to remove or comment out any element because one has to think about the fact that the last element mustn't have a trailing comma:
It is inconvenient to remove or comment out elements because the last element mustn't have a trailing comma:

{% highlight scala %}
Map(
Seq(
foo,
bar //,
bar,
// baz
)
) // error: illegal start of simple expression
{% endhighlight %}

Secondly, it is quite inconvenient to re-order the sequence, for instance if you wanted `baz` before `bar` you need to micromanage which is followed by a comma and which isn't:
It is also inconvenient to reorder because every element but the last one must be followed by a comma:

{% highlight scala %}
val xs = Seq(
foo,
baz // This isn't going to work
baz
bar,
)
) // error: illegal start of simple expression
{% endhighlight %}

### Reduce diff noise
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be "Diff noise reduction" to be consistent with other section titles?


Allowing trailing commas also reduces a lot of noise in diffs, such as:
Adding and removing commas also introduces unnecessary noise in diffs:

{% highlight diff %}
@@ -4,7 +4,8 @@
Map(
Seq(
foo,
bar,
- baz
Expand All @@ -76,15 +78,15 @@ Allowing trailing commas also reduces a lot of noise in diffs, such as:

### VCS authorship attribution

Using the example above, the authorship of the `baz` line would be preserved, instead of becoming that of the author of the `quux` line.
Using the example above, adding a comma after `baz` also unnecessarily changed the authorship of the line.

### Simplify code generation

Such a feature would also simplify generating Scala source code.
Allowing trailing commas would also simplify generating Scala source code.

### Long standing ticket

There is an open ticket ([SI-4986][]) where this feature was requested, referencing the fact that it facilitates code generation by tools and allows for easier sorting of the values, initially in the context of import selectors but later also for other constructs in the syntax.
([SI-4986][]) was opened in 2011 requesting support for trailing commas, referencing that it facilitates code generation by tools and allows easier sorting of values. It was initially in the context of import selectors but later also for other constructs in the syntax.

### Real-world use-cases

Expand All @@ -95,45 +97,113 @@ Some real-world use-cases where elements of a sequence are typically added, remo

## Design Decisions

There are a number of different elements of the Scala syntax that are comma separated, but instead of changing them all a subset of the more useful ones was chosen:
### Multi-line

It is not the intent of introducing trailing commas to promote a code style such as:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you briefly explain why?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done d1ebb53


{% highlight scala %}
val xs = Seq(foo, baz, bar, )
{% endhighlight %}

Trailing comma support is therefore restricted to only comma-separated elements that are on separate lines:

{% highlight scala %}
val xs = Seq(
foo,
baz,
bar,
)
{% endhighlight %}

### What parts of the Scala grammar to change

There are a number of different parts of the Scala grammar that are comma-separated and, therefore, could support trailing commas. Specifically:

* `ArgumentExprs`
* `Params` and `ClassParams`
* `SimpleExpr1`
* `TypeArgs`, `TypeParamClause` and `FunTypeParamClause`
* `SimpleType` and `FunctionArgTypes`
* `SimplePattern`
* `ImportSelectors`
* `Import`
* `Bindings`
* `ids`, `ValDcl`, `VarDcl`, `VarDef` and `PatDef`

With this proposal I would like to present 2 variants:

* tuples
* argument and parameter groups, including for implicits, for functions, methods and constructors
* import selectors
1. The first variant adds trailing comma support to only multi-line `ArgumentExprs`, `Params` and `ClassParams`, which I consider to be the parts of the grammar that would most benefit from trailing commas.

From the spec these are:
2. The second variant adds trailing comma support to the whole grammar (again, only for multi-line), which means more consistency, but also supporting trailing commas in places that doesn't really need it, such as `ids`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean by "doesn't really need it"?


* SimpleExpr1, ArgumentExprs via Exprs
* ParamClause, ParamClauses via Params
* ClassParamClause, ClassParamClauses via ClassParams
* ImportSelector
In this proposal, only the first variant is considered: trailing comma support for `ArgumentExprs`, `Params` and `ParamClasses` for the sake of simplicity.

The elements that have not changed are:
See below for more details on what that would mean.

* ValDcl, VarDcl, VarDef via ids
* Type via FunctionArgTypes
* SimpleType, TypeArgs via Types
* Expr, ResultExpr via Bindings
* SimplePattern via Patterns
* TypeParamClause, FunTypeParamClause
* ImportExp
* PatDef
#### Changing `ArgumentExprs`

**Spec change**

{% highlight diff %}
Exprs ::= Expr {‘,’ Expr}
-ArgumentExprs ::= ‘(’ [Exprs] ‘)’
+ArgumentExprs ::= ‘(’ [Exprs] [‘,’] ‘)'
{% endhighlight %}

**Example**
{% highlight scala %}
Seq(
foo,
bar,
baz,
)
{% endhighlight %}

## `Params` and `ClassParams`

**Spec change**
{% highlight diff %}
Params ::= Param {‘,’ Param}
- ParamClause ::= [nl] ‘(’ [Params] ‘)’
-ParamClauses ::= {ParamClause} [[nl] ‘(’ ‘implicit’ Params ‘)’]
+ ParamClause ::= [nl] ‘(’ [Params] [‘,’] ‘)’
+ParamClauses ::= {ParamClause} [[nl] ‘(’ ‘implicit’ Params [‘,’] ‘)’]

ClassParams ::= ClassParam {‘,’ ClassParam}
- ClassParamClause ::= [nl] ‘(’ [ClassParams] ‘)’
-ClassParamClauses ::= {ClassParamClause} [[nl] ‘(’ ‘implicit’ ClassParams ‘)’]
+ ClassParamClause ::= [nl] ‘(’ [ClassParams] [‘,’] ‘)’
+ClassParamClauses ::= {ClassParamClause} [[nl] ‘(’ ‘implicit’ ClassParams [‘,’] ‘)’]
{% endhighlight %}

**examples**
{% highlight scala %}
def bippy(
foo: Int,
bar: String,
baz: Boolean,
)

class Bippy(
foo: Int,
bar: String,
baz: Boolean,
)
{% endhighlight %}

## Implementation

The implementation is a simple change to the parser, allowing for a trailing comma, for the groups detailed above, and has been proposed in [scala/scala#5245][].
The implementation of trailing commas is a matter of changing some of the implementation of Scala's parser. An implementation of this proposal can be found at [scala/scala#5245][].
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about "is limited to changing Parsers.scala in the Scala compiler.`?


## Drawbacks/Trade-offs

The drawback, or trade-off, to this change is that it adds another way in which it is possible to do something in Scala. But it is the opinion of this SIP that the pragmatic advantage of being able to have trailing commas is worth this drawback.
One drawback, or trade-off, to this change is that it adds an alternative way in which it is possible to do something in Scala. But I believe that the pragmatic advantage of being able to have trailing commas is worth this drawback.

Given that this is a change in syntax, another drawback is that it requires changing the existing tools, such as those that parse Scala: intellij-scala, scalariform, scala.meta and scalaparse.
Another drawback, given this is a change in syntax, is that it requires changing the existing tools, such as those that parse Scala: intellij-scala, scalariform, scala.meta and scalaparse.

## Alternatives

As an alternative, trailing commas support could be added universally to all the comma-separated elements of the syntax. This would mean changing more (but still only in the parser), but it would make it consistent.

As an alternative to changing the language, there already exists today a compiler plugin called [scala-commas][] that provides this feature. It also provides some evidence that people would even use unsupported compiler apis and reflection to add this functionality, even when such a plugin won't compose with other plugins well, though arguably only weak evidence as it's a young and obscure plugin.
As an alternative to changing the language, there already exists today a compiler plugin called [scala-commas][] that provides a variant of this feature. It also provides some evidence that people would even use unsupported compiler apis and reflection to add this functionality, even when such a plugin won't compose with other plugins well, though arguably only weak evidence as it's a young and obscure plugin.

## References

Expand All @@ -144,3 +214,5 @@ As an alternative to changing the language, there already exists today a compile
[SI-4986]: https://issues.scala-lang.org/browse/SI-4986
[scala/scala#5245]: https://github.com/scala/scala/pull/524://github.com/scala/scala/pull/5245
[scala-commas]: https://github.com/47deg/scala-commas
[#533]: https://github.com/scala/scala.github.com/pull/533
[#625]: https://github.com/scala/scala.github.com/pull/625