-
Notifications
You must be signed in to change notification settings - Fork 29
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
base: main
Are you sure you want to change the base?
Conversation
content/flexible-varargs.md
Outdated
``` | ||
|
||
### Javascript | ||
Javascript's expression `...` syntax works identically to this proposal. In Python, you can mix |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Python should be Javascript
I started to wander if this SIP could help resolve this issue: scala/scala3#18009 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 |
I also think this SIP is a very nice improvement. You mention / suggest to use
I'm not sure if the 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 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 This SIP doesn't change repeated parameters (of case classes), a repeated parameter will still be last in a case classs definition. An 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 |
The motivation for
Yes, definitions are unchanged. Not just for
Yes that is right. The trailing |
Co-authored-by: odersky <[email protected]>
Co-authored-by: odersky <[email protected]>
Co-authored-by: odersky <[email protected]>
No, I did not know that. Generally, I think it's better of the SIP does not specify a precise implementation scheme. The scheme might change in the future, and we should not have to change the SIP because of that. The In terms of what we have, it looks like basing the whole thing on |
It's The spec says when using a sequence argument
Right. I was thinking what happens when using subpatterns and then got a bit lost in spec. The def f(x: Any): String = x match {
case Seq(Some(_), y: String, xs*, 1) => y
}
// translates to
def f(x: Any): String = {
val VarargsMatcher = new VarargsMatchHelper(2, 1)
x match {
case VarargsMatcher(Seq(Some(_), y: String), xs, Seq(1)) => y
}
} |
There is now an implementation of most of the proposal. One doubt I am having is spreads of def foo(x: Int*) = x.sum
val xo = Some(2)
f(xo*) That's an error since the argument of a spread operator must be a f(xo.toList*) would work today. I'd be interested in getting the committee member's opinion on this aspect before I try to implement it. |
My motivation for including a special case for The use of a naked A third option is to allow splicing options in longer sequences such as So IMO might as well allow |
Perhaps a fourth alternative is that we add a new marker trait trait VarargUnpackable[+T]{
// Used when unpacking a single value, can
// efficiently return `this` for `Seq`s
def toSeq: Seq[T]
// Used when unpacking flexible varargs to
// efficiently feed the elements of this object
// into a `Seq.builder`
def copyToArray(dest: Array[T], startIndex: Int): Unit
// Used when unpacking flexible varargs to allow
// the builder to expand its capacity precisely
// when the size of this is known. Otherwise
// returns `-1` indicating the size it not known
// and the builder would have to handle expansion
// using its own heuristics
def sizeHint: Int
// When sizeHint is -1, we cannot pre-size the
// builder array, and so we append elements one
// at a time to give the builder a chance to resize
// as necessary
def foreach(f: T => Unit): Unit
}
object VarargUnpackable{
class Builder[T]{
def addAll(v: VarargUnpackable[T])
def add(v: T)
def result(): Seq[T]
}
} // Desugaring
foo(bar*)
foo(VarargUnpackable.Builder().addAll(bar).build())
foo(bar*, qux*)
foo(VarargUnpackable.Builder().addAll(bar).addAll(qux).build())
foo(value1, bar*, value2, qux*, value3)
foo(VarargUnpackable.Builder().add(value1).addAll(bar).add(value2).addAll(qux).add(value3).build()) The That would avoid hardcoding A fifth alternative would be to use a name-based desugaring so |
Why not just let |
@Atry I think it would be good to have the language feature depend on as minimal an interface as possible, so a minimal |
I think all this goes to far. Varargs are Seq's. I see no reason to complicate things further here. Arrays can be passed to Seq's since there is an implicit conversion. The point of SIP 70 is to stay within this framework and allow multiple spreads. My PR provides that. Options unfortunately are an irregular case since there is no conversion from option to Seq (and for good reason!). I think it is much cleaner to keep that principle even if some use cases require an explicit Here's another way to put it: I can have a vararg function and a spread argument to it: def f(x: Int*)
f(xs*) I can convert the vararg to a def f(x: Seq[Int])
f(xs) That works, always. I have done that refactoring many times. But allowing options in spreads breaks it. Generally varargs are tricky to get right since they need to be treated early during type inference when types are not yet known completely. That compounds complexity. So we need to keep it simple. |
I agree with @odersky. I don't question that there are use cases for spreading |
I think it would indeed be interesting if we had a minimal interface for varargs and spreads instead of the |
As a long-term Scala user, I strongly support @lihaoyi's arguments. |
Maybe we can add If we allowed a spread type then we could use a unary |
scala/scala3#23855 now also implements the pattern matching specified in the SIP. This means the implementation is complete, except for the contentious point whether we want to support |
We can probably merge the |
Let's discuss this point next week in the SIP meeting. |
it in any way that returns a `Seq` that has the same elements as the reference implementation: | ||
```scala | ||
// User Code | ||
val total = sum(0, numbers1*, numbers2*, 4) // 10 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code generated by the implementation for this snippet is
sum(
ArraySeqBuilder.ofInt(2 + numbers1.length + numbers2.length)
.add(0)
.addArray(numbers1)
.addSeq(numbers2)
.add(4).result)
|
||
```scala | ||
// User Code | ||
numbers match { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code generated by the implementation for this snippet is
if numbers.lengthCompare >= 2 then
if numbers(0) == 1 then
val x1 = numbers.drop(1)
val xs = numbers.dropRight(1)
val x2 = numbers.takeRight(1)
if x2(0) == 3 then
return[matchResult] ...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Implementation matches proposal except that Option spreads are not implememnted
@Ichoran wrote:
We already have |
No description provided.