-
Notifications
You must be signed in to change notification settings - Fork 21
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
Disallow passing arrays for repeated arguments #11024
Comments
There is no particular reason that |
Are you suggesting that varargs should be specified as |
@sjrd - Yes, because doing otherwise will clobber performance of varargs for mutable collections; the "workaround" involves users writing a whole set of new |
😱 |
ummutable |
Why do we special case arrays and not just depend on the existing implicit conversions of Array[x] => Seq[x]? An Array is not a Seq... Take
No special compiler magic required... |
I think there is a goal to have the language spec refer only to |
Is there? What about |
Removing the ability to pass in mutable sequences through varargs as read-only sequences is a non-trivial change with significant consequences performance of mutable sequences: forcing people to copy their collections at a time when they most want to avoid additional overhead. Not to say we definitely shouldn't do it, but we definitely shouldn't do it "just because", especially if we can satisfy our requirements just by adding a trivial type alias. |
Treating Java varargs as effectively immutable was a conscious decision, allowing to pass arrays from Scala code was not. Uncurry does the necessary adaptation but I must have missed how it gets through typer in the first place. I expected that it would use an implicit conversion, in this case the deprecated |
I'll quote @odersky's comment in scala/scala3#4787
|
@allanrenucci I agree. That was the intention with |
class A {
def foo1(xs: Int*): Seq[Int] = xs
val seq1 = foo1(Array(1, 2, 3) : _*)
def foo2(xs: String*): Seq[String] = xs
val seq2 = foo2(Array("a", "b", "c") : _*)
} compile with
Scala 2.13.x use |
BTW, |
It looks like varargs do not really have an expected collection type. The relevant part of typer that handles def typedTyped(tree: Typed) = {
if (treeInfo isWildcardStarType tree.tpt)
typedStarInPattern(tree, mode.onlySticky, pt) And I'll change the varargs adaptation in Uncurry intead. |
Do we have any evidence that this is an extant problem that is causing significant harm? Performance regressions do cause harm. At the very least we should benchmark before/after to get an idea of how bad it is. If this is "fixed" and as a consequence people end up writing a bunch of def foo(xs: collection.Seq[A]): Foo = /* actual code */
def foo(xs: Array[A]): Foo = foo(ArraySeq.unsafeWrapArray(xs))
def foo(xs: A*): Foo = foo(xs) I don't think we've made things better. |
We have the same situation in I don't understand the purpose of your code example. If the first method takes a Calling |
The second method was anticipating a more complicated scenario than makes sense to describe here; just ignore it! So the workaround for arrays is to replace |
Right, if you want to support mutable Seqs and varargs you need two separate methods since M4. Defensive copying of explicit array values won't make this any harder. If you only have a varargs method you'll need to use |
Guess I missed it on M4, then. Was that ever benchmarked? |
I can't think of a good use case that could be benchmarked. The recommendation for migrating to 2.13 is to use If you keep using |
The problem is that historically there was no reason to create both methods. But now if I write
then I am the one who gets the deprecation warning, but ThatOtherLibrary is where the changes have to occur. There's no, "Warning, users of your library may find your API to have reduced performance as they will now have to copy mutable collections to pass them in to this method." when they go to port their library to 2.13. So likely what will happen is they do a major version upgrade and then the users find that the API is broken for them (probably not right away, as performance testing usually only comes after people notice a problem). But ThatOtherLibrary can't maintain binary compatibility if they alter their API so they need another major version upgrade, after users notice, to fix the issue. The general-purpose workaround is, I guess, It would be good to test this with something like def sumLengths(xs: String*) = {
var sum = 0
val it = xs.iterator()
while (it.hasNext) sum += it.next().length;
sum
} before and after. My quick and dirty experiments with going through toArray/WrappedArray on I'm sorry I didn't notice before, but this seems large enough to me to warrant further attention. The effect of the change is to make what was previously mostly a syntactic choice to one that has substantial performance consequences. |
Seq
will be an alias forimmutable.Seq
in 2.13. Therefore, you should not be able to pass anArray
where a repeated argument is expected. Currently:This raises the question: What do we do for Java defined methods? Should we still allow passing an
Array
? E.g.It would be nice to not have a special case for java defined methods, but this might incur a performance cost as you would need to wrap/copy the
Array
into aimmutable.ArraySeq
when the compiler will have to convert back to anArray
anyway.Note: sequence literals would not concerned by this change
The text was updated successfully, but these errors were encountered: