Skip to content

Conversation

@rssh
Copy link

@rssh rssh commented Nov 29, 2025

No description provided.

rssh added 4 commits November 29, 2025 16:43
- Clarify grammar rules for each tuple case (empty, single, multi-element)
- Add section on interaction with trailing comma tolerance (SIP-27)
- Document multiline tuple parsing behavior
- Update proof of concept section with both implementation variants
…t in different variants

  and more text cleanup

This proposal introduces trailing comma syntax for tuples, allowing `(A,)` to represent a single-element tuple type and `(a,)` to represent a single-element tuple value. This addresses the long-standing ambiguity where `(A)` is parsed as a parenthesized expression rather than a tuple, making it impossible to express single-element tuples using the concise parenthesized notation.

The proposal also allows `(,)` for empty tuples (equivalent to `EmptyTuple`) and trailing commas in multi-element tuples like `(A, B,)`, providing consistency with other Scala constructs that already support trailing commas.
Copy link
Contributor

Choose a reason for hiding this comment

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

As far as I know, no Scala construct supports trailing commas without a newline following them.

That also means that (a,) is already valid syntax, if you have a newline after the value:

scala> (
     | 1,
     | )
val res0: Int = 1

Seems a bit sketchy to have (1,) and (1,\n) mean different things. I personally find it very common to find (a, b) tuple literals growing large enough that I spread them over multiple lines, and I expect it would be common for single-element tuples as well. With this proposal, we would see a behavior change when a tuple like (a,) grew big enough that the opening and closing parens get moved to their own line

Copy link
Contributor

@lihaoyi lihaoyi Dec 30, 2025

Choose a reason for hiding this comment

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

In Python (1,\n) is indeed a tuple, and distinct from (1\n), so there is precedence for such a fine distinction.

>>> (
... 1,
... )
(1,)
>>> (
... 1
... )
1

But in Scala (1,\n) cannot be made into a tuple without breaking backwards compatibility.

Copy link
Author

Choose a reason for hiding this comment

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

Oops, then the maximum we can do is to fix the Tuple arity.

@lihaoyi
Copy link
Contributor

lihaoyi commented Dec 30, 2025

We already have Tuple() and Tuple(1) for empty and one-element tuples.

scala> Tuple(1)
val res1: Int *: EmptyTuple = (1,)
                                                                                                                       
scala> Tuple()
val res2: EmptyTuple.type = ()                           

The toString of one-element tuples is the syntax you propose here, but the fact that trailing commas already mean something in multi-line contexts seems a bit too close to that syntax being proposed

Maybe we can standardize on saying Tuple(1) is how you write a one-element tuple?

Furthermore, an EmptyTuple is basically isomorphic to Unit. I wonder if there's something we can do there to bring them closer together so you can write () for EmptyTuple or Unit depending on what is expected, perhaps with an implicit conversion

Notably, Tuple(...) syntax doesn't work for arity >1.

scala> Tuple(1, 2, 3)
-- [E134] Type Error: ----------------------------------------------------------
1 |Tuple(1, 2, 3)
  |^^^^^
  |None of the overloaded alternatives of method apply in object Tuple with types
  | [T](x: T): T *: EmptyTuple
  | (): EmptyTuple
  |match arguments ((1 : Int), (2 : Int), (3 : Int))
  |
  |where:    T is a type variable
1 error found

Perhaps that can be fixed so that Tuple(...) works for all tuple arities? I personally find writing Tuple2(a, b) nicer than (a, b) in multi-line contexts, since the opening ( on its own line looks kind of awkward, but then having to manually update it to Tuple3 or Tuple4 every time i add an element is annoying. Being able to just say Tuple() and have it work regardless of arities and single/multi-line would be pretty nice, while also standardizing on a canonical (if slightly verbose) way to write one-element and zero-element tuples

@bjornregnell
Copy link

I think generalizing Tuple.apply to take any number of arguments seems like a good idea anyways. This can be done independently of considering (,) and (1,) as sugar for that. The benefit of that sugar is not very big if we allow also Tuple(1,2) == (1,2) etc. We already have Tuple(1) == Tuple1(1) and Tuple() == EmptyTuple so it seems very logical and a nice regularity.

@rssh
Copy link
Author

rssh commented Dec 31, 2025

yes, looks like (T,) idea is dead because of backward compability.
Tuple issue is a library change, and hope does not require SIP

@rssh
Copy link
Author

rssh commented Dec 31, 2025

Or we can save this for scala4. (i.e. resubmit when start collecting information about non-backward compatible changes).

@rssh rssh closed this Dec 31, 2025
@odersky
Copy link
Contributor

odersky commented Dec 31, 2025

yes, looks like (T,) idea is dead because of backward compability.
Tuple issue is a library change, and hope does not require SIP

No, library changes don't need a SIP.

@rssh
Copy link
Author

rssh commented Dec 31, 2025

library changes submitted: scala/scala3#24874

@bracevac bracevac added status:withdrawn scala 4 Proposal worth revisiting when breaking the binary compatibility will be possible and removed stage:pre-sip status:submitted labels Jan 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

scala 4 Proposal worth revisiting when breaking the binary compatibility will be possible status:withdrawn

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants