-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Use Chunk.single rather than Chunk.apply [series/2.0.x] #8718
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
Conversation
|
On second thought, would it work if def apply[A](as: A): Chunk[A] =
single(as) |
Mima seems to be happy, so I will open another PR. |
|
@kyri-petrou it didn't work with 2.12 so reopening this one |
|
Can I instead suggest the existing |
|
Actually override def apply[A](as: A*): Chunk[A] =
if (as.size == 1) single(as.head) else fromIterable(as) |
I didn't know this until now, but the object Foo {
def apply(vs: String*): Unit = {
println(vs)
println(vs.knownSize)
}
@main def m = Foo("asd", "asd", "")
} |
|
What I have always understood from my Java time is that a vararg method is actually implemented with arrays. Now scala presents this as a Seq, but presumably it actually is still an array at runtime. According to https://users.scala-lang.org/t/storage-backing-varargs/1997 we can get the underlying array with override def apply[A](as: A*): Chunk[A] =
if (as.size == 1) single(as.head) else fromArray(as.toArray)WDYT? |
Hmm, after reading some more, I am no longer sure this is true 🤔 |
|
Looks like we get an ArraySeq so I think you're right that toArray is free. It would rely on internal implementation details that could change though, but I think it's okay. |
Nope, I was wrong: We could specialize for ArraySeq though... |
|
I wonder if it's worth bringing in a dependency on |
This works, so a good override def apply[A](as: A*): Chunk[A] =
if (as.size == 1) single(as.head)
else if (as.isInstanceOf[scala.collection.immutable.ArraySeq[?]]) fromArray(as.toArray)
else fromIterable(as)Note sure how to test the performance of this though. |
|
@erikvanoosten I think there are 2 separate things here:
For (1), I think we probably ignore optimizations for this case since the number of elements that will be passed are likely to be very few, since they're usually written in this kind of fashion: Now (2) is indeed a bit dangerous (performance-wise), and we should probably try and work something out. The most dangerous case I can think of is this one: val someList = List.fill(10_000)("foo")
val chunk = Chunk(someList*)In this case, the |
|
@kyri-petrou You are right, since you can call vargarg methods with any Seq (due to the So the optimization in this PR is questionable.
If we're here for micro-optimizations: my intuition says that the extra if statement will be faster than an extra allocation. We can fix (2) by using override def apply[A](as: A*): Chunk[A] =
if (as.knownSize == 1) single(as.head)
else if (as.isInstanceOf[scala.collection.immutable.ArraySeq[?]]) fromArray(as.toArray)
else fromIterable(as) |
|
@erikvanoosten problem with |
Ah, now I understand why you wanted this. I believe override def apply[A](as: A*): Chunk[A] =
if (as.isInstanceOf[scala.collection.immutable.IndexedSeq[?]] && as.size == 1) single(as.head)
else if (as.isInstanceOf[scala.collection.immutable.ArraySeq[?]]) fromArray(as.toArray)
else fromIterable(as) |
|
@erikvanoosten this feels like a workaround for something that there is already a solution for - which is to bring in The reason I'm advocating to bring it in is because I had to run through similar loops in zio/core/shared/src/main/scala/zio/ZIO.scala Lines 5979 to 5986 in 4a29804
|
|
Sounds okay to me to depend on |
|
I think it's okay, but |
Constructing a

Chunkof size 1 is faster usingChunk.singlerather thanChunk.applywhich usesChunk.fromIterable. Found the one coming frominterruptAsForkwhile profiling an app: