Skip to content
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

ArraySeq map method boxing Java runtime primitives. #11761

Open
Rich2 opened this issue Oct 5, 2019 · 4 comments

Comments

@Rich2
Copy link

commented Oct 5, 2019

So mapping from ArraySeq[Int] to ArraySeq[Int] maps to an underlying Array of AnyRef.

scala> collection.immutable.ArraySeq(1, 2, 3)
res0: scala.collection.immutable.ArraySeq[Int] = ArraySeq(1, 2, 3)

scala> res0.unsafeArray
res1: Array[_] = Array(1, 2, 3)

scala> res1.isInstanceOf[Array[Int]]
res2: Boolean = true

scala> res0.map(_ * 2)
res3: scala.collection.immutable.ArraySeq[Int] = ArraySeq(2, 4, 6)

scala> res3.unsafeArray
res4: Array[_] = Array(2, 4, 6)

scala> res4.isInstanceOf[Array[Int]]
res5: Boolean = false

Unlike Arrays the ArraySeq map method doesn't take an implicit ClassTag parameter, so it doesn't have the data to create the correct underlying Array type. I presume this will also apply to all methods returning an ArraySeq, as they all seem to lack the ClassTag parameters, unlike the corresponding methods on raw Arrays.

This will apply to 2.13.0 and 2.13.1, but not earlier versions as ArraySeq introduced in 2.13.0.

@SethTisue

This comment has been minimized.

Copy link
Member

commented Oct 6, 2019

there is discussion at https://contributors.scala-lang.org/t/is-arrayseq-fit-for-purpose/3736/7 about whether this may actually be necessary behavior

@szeiger

This comment has been minimized.

Copy link

commented Oct 7, 2019

Unless you can make use of specialized function types (at compile-time; there is no sufficiently efficient way to make the required decisions at run-time) you're generally better off keeping the boxed values instead of unboxing them again.

@lrytz

This comment has been minimized.

Copy link
Member

commented Oct 7, 2019

@szeiger to clarify, you pointing out that Array(1,2,3).map(_+1) produces an Array[Int], but boxes and unboxes the values in the process - right?

@Rich2

This comment has been minimized.

Copy link
Author

commented Oct 9, 2019

So to repeat the Opening post but with Doubles

scala> collection.immutable.ArraySeq(1.5, 2.5, 3.5)
res0: scala.collection.immutable.ArraySeq[Double] = ArraySeq(1.5, 2.5, 3.5)

scala> res0.unsafeArray
res1: Array[_] = Array(1.5, 2.5, 3.5)

scala> res1.isInstanceOf[Array[Double]]
res2: Boolean = true

scala> res0.map(_ * 2)
res3: scala.collection.immutable.ArraySeq[Double] = ArraySeq(3.0, 5.0, 7.0)

scala> res3.unsafeArray
res4: Array[_] = Array(3.0, 5.0, 7.0)

scala> res4.isInstanceOf[Array[Double]]
res5: Boolean = false

So again we get boxing, but we shouldn't because Function1 has been specialised for Int and Double on both parameter and return types, and the type's are known at compile, and the specialisation works in my own experiment even when the map method is implemented in the base trait.

@annotation.implicitNotFound(msg = "No implicit view available from ${T1} => ${R}.")
trait Function1[@specialized(Specializable.Arg) -T1, @specialized(Specializable.Return) +R] extends AnyRef
 { self =>
final val Arg: Group[(Int, Long, Float, Double)]
final val Return: Group[(Int, Long, Float, Double, Boolean, Unit)]

Boxing shouldn't occur even when using a Function2 for Int or Double as parameter or return type, as it is specialised, just not for Float on Argument type.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
4 participants
You can’t perform that action at this time.