Skip to content

SIP-70 - Flexible Varargs #105

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

Open
wants to merge 20 commits into
base: main
Choose a base branch
from
Open

Conversation

lihaoyi
Copy link
Contributor

@lihaoyi lihaoyi commented Feb 28, 2025

No description provided.

```

### Javascript
Javascript's expression `...` syntax works identically to this proposal. In Python, you can mix
Copy link

Choose a reason for hiding this comment

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

Python should be Javascript

@som-snytt
Copy link

On the pattern side, the old gotcha is scala/bug#7623 (which has a lint on Scala 2). That is where two pat vars are satisfied by a single Seq, i.e., there is a kind of misalignment. (The example may temper ambition for patterns.)

@Atry
Copy link
Contributor

Atry commented Mar 6, 2025

Do you want to cite #41 ? There are a lot of previous discussions on this feature.

This SIP is a renamed version of #41 , and also adds an intermediate step to create a temporary seq.

  • applyBegin -> newBuilder
  • applyNext -> addOne
  • applyNextSeq -> addAll
  • applyEnd -> result

@kyouko-taiga kyouko-taiga changed the title SIP-XX - Flexible Varargs SIP-70 - Flexible Varargs Mar 21, 2025
@prolativ
Copy link
Contributor

I started to wander if this SIP could help resolve this issue: scala/scala3#18009
In short, the problem there is that an invocation of a curried method added by a refinement on a subtype of Selectable, e.g.

mySelectable.foo(args1*)(args2*)

should get desugared to something like

mySelectable.applyDynamic("foo")(args1*, args2*).asInstanceOf[Foo]

which is now illegal, but would be when this SIP gets implemented

@lihaoyi
Copy link
Contributor Author

lihaoyi commented Jun 29, 2025

@odersky @sjrd @lrytz have any of you had a chance to look at this? It's been a month since reviewers were assigned so I'm hoping to get some feedback

Copy link
Contributor

@odersky odersky left a comment

Choose a reason for hiding this comment

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

What's proposed:

  1. Constructing varargs with multiple splices xs*, interspersed with normal values
  2. Deconstructing sequences with a single vararg pattern that can be somewhere in the middle of the sequence, not just at the end.

These are clear quality of life improvements. The construction part is the more important one, and it should be straightforward to implement. The pattern matching part is probably also easy to implement, but it might need some care to get good performance for linear sequences. E.g you have a long list and match with List(x, y, ys*, 2, 3) you want to avoid multiple trips to the end of the list to match the 2 and 3. Nevertheless, it looks quite doable.

cleanup using `+:` and `:+` doesn't actually work due to weird associativity problems.
With this proposal, you can write what you mean and have it just work.

### Constucting Sequences
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
### Constucting Sequences
### Constructing Sequences


### Constucting Sequences

The second scenarios that this streamlines is constructing `Seq`s and other collections.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
The second scenarios that this streamlines is constructing `Seq`s and other collections.
The second scenario that this streamlines is constructing `Seq`s and other collections.

val coll = Seq() ++ foo ++ bar ++ Seq(qux, baz)
```

With those proposal, all three scenarios would look almost the same - reflecting
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
With those proposal, all three scenarios would look almost the same - reflecting
With this proposal, all three scenarios would look almost the same - reflecting

@lrytz
Copy link
Member

lrytz commented Jul 2, 2025

I also think this SIP is a very nice improvement.

You mention / suggest to use IArray, which is an opaque type alias and not part of the collection hierarchy. sum(IArray.newBuilder[Int].addOne(1).result()*) wraps the resulting array using IArray.wrapIntArray, which converts it to an immutable.ArraySeq. So it's probably better to use ArraySeq directly. Or maybe Vector?

  • ArraySeq: can wrap a primitive array, minimal memory overhead
  • Vector: better if the collections is later used for appending / prepending, concatenation etc. Memory and computational overhead is small. Primitives are boxed.

I'm not sure if the newBuilder call needs an explicit type argument, or if we can have that inferred by the typer. In Scala 3:

scala> collection.immutable.ArraySeq.newBuilder.addOne(1).result()
-- [E172] Type Error: ----------------------------------------------------------
1 |collection.immutable.ArraySeq.newBuilder.addOne(1).result()
  |                                        ^
  |                                        No ClassTag available for Any
1 error found

Interestingly, the same line works on Scala 2, the compiler infers newBuilder[Int]. Is that a known limitation @odersky?

If an explicit type argument is needed, the SIP should specify how that type is obtained.


For patterns, we'll need to update the spec to say how case E(p1, ..., pN, s*, u1, ..., uM) translates. I think the section is https://scala-lang.org/files/archive/spec/3.4/08-pattern-matching.html#pattern-sequences, the spec seems not to be fully up to date though, it doesn't reflect scala/scala3#11240.

This SIP doesn't change repeated parameters (of case classes), a repeated parameter will still be last in a case classs definition. An unapplySeq declaration should also continue to have the corresponding return type (T1, ..., Tm, Seq[S]).

So the spec needs to map the new pattern shape onto to current case class parameter list shape (or unapplySeq return type shape). Note that T1, ..., Tm can be arbitrary types, all different from each other and different from S. So I'm not sure the proposal of translating case E(p1, ..., pN, s*, u1, ..., uM) to case VarargsMatcher(Seq(p1, ..., pN), s, Seq(u1, ..., uM)) would work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants