From 8798098e070ead9687a994babe0ebb2b99c3d169 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Fri, 5 Dec 2025 12:26:10 +0100 Subject: [PATCH] Add an implementation of BuildFrom for IArray for use with e.g. Future.sequence, LazyZip, Random.shuffle --- library/src/scala/collection/BuildFrom.scala | 10 +++++++ project/MiMaFilters.scala | 2 ++ .../stdlibExperimentalDefinitions.scala | 3 +++ .../IArrayOps-experimental-BuildFrom.scala | 26 +++++++++++++++++++ 4 files changed, 41 insertions(+) create mode 100644 tests/run/IArrayOps-experimental-BuildFrom.scala diff --git a/library/src/scala/collection/BuildFrom.scala b/library/src/scala/collection/BuildFrom.scala index cb819db200ec..a7dd4b8856fa 100644 --- a/library/src/scala/collection/BuildFrom.scala +++ b/library/src/scala/collection/BuildFrom.scala @@ -19,6 +19,7 @@ import scala.annotation.implicitNotFound import scala.collection.mutable.Builder import scala.collection.immutable.WrappedString import scala.reflect.ClassTag +import scala.annotation.experimental /** Builds a collection of type `C` from elements of type `A` when a source collection of type `From` is available. * Implicit instances of `BuildFrom` are available for all collection types. @@ -84,6 +85,15 @@ object BuildFrom extends BuildFromLowPriority1 { def newBuilder(from: Array[?]): Builder[A, Array[A]] = Factory.arrayFactory[A].newBuilder } + @experimental + given buildFromIArray[A : ClassTag]: BuildFrom[IArray[Any], A, IArray[A]] = + // IArray[?] is unreducible wildcard, but as IArray is covariant its ok to use IArray[Any] here. + new BuildFrom[IArray[Any], A, IArray[A]]: + def fromSpecific(from: IArray[Any])(it: IterableOnce[A]^): IArray[A] = + // TODO: when IArray factory is added to library, delegate there. + IArray.unsafeFromArray(Factory.arrayFactory[A].fromSpecific(it)) + def newBuilder(from: IArray[Any]): Builder[A, IArray[A]] = IArray.newBuilder[A] + implicit def buildFromView[A, B]: BuildFrom[View[A], B, View[B]] = new BuildFrom[View[A], B, View[B]] { def fromSpecific(from: View[A])(it: IterableOnce[B]^): View[B]^{it} = View.from(it) diff --git a/project/MiMaFilters.scala b/project/MiMaFilters.scala index 9a633397924a..14a6ec678aeb 100644 --- a/project/MiMaFilters.scala +++ b/project/MiMaFilters.scala @@ -9,6 +9,8 @@ object MiMaFilters { // Additions that require a new minor version of the library Build.mimaPreviousDottyVersion -> Seq( ProblemFilters.exclude[DirectMissingMethodProblem]("scala.caps.package#package.freeze"), + // IArray integration with Scala Collections: + ProblemFilters.exclude[DirectMissingMethodProblem]("scala.collection.BuildFrom.buildFromIArray"), ), ) diff --git a/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala b/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala index 4cfb23e229ac..8c32537b5d76 100644 --- a/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala +++ b/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala @@ -98,6 +98,9 @@ val experimentalDefinitionInLibrary = Set( // New feature: Erased trait "scala.compiletime.Erased", + + // New API: BuildFrom[IArray[Any], T, IArray[T]] + "scala.collection.BuildFrom$.buildFromIArray", ) diff --git a/tests/run/IArrayOps-experimental-BuildFrom.scala b/tests/run/IArrayOps-experimental-BuildFrom.scala new file mode 100644 index 000000000000..87b1941a48c9 --- /dev/null +++ b/tests/run/IArrayOps-experimental-BuildFrom.scala @@ -0,0 +1,26 @@ +//> using options -experimental + +import scala.collection.BuildFrom + +def diffs(ns: IArray[Long]): IArray[Long] = + ns.lazyZip(ns.tail).map((a, b) => b - a) + +def diffsB(ns: IArray[Long])(bf: BuildFrom[IArray[Long], Long, IArray[Long]]): IArray[Long] = + ns.lazyZip(ns.tail).map((a, b) => b - a)(using bf) + +@main def Test: Unit = + val explicit: BuildFrom[IArray[Long], Long, IArray[Long]] = + scala.collection.BuildFrom.buildFromIArray[Long] + + val contextual: BuildFrom[IArray[Long], Long, IArray[Long]] = + summon[BuildFrom[IArray[Long], Long, IArray[Long]]] + + val contextualWide: BuildFrom[IArray[Any], Long, IArray[Long]] = + summon[BuildFrom[IArray[Any], Long, IArray[Long]]] + + assert(explicit.getClass == contextual.getClass) // check default is same implementation. + assert(explicit.getClass == contextualWide.getClass) // check default is same implementation. + + assert(diffs(IArray(1L, 3L, 6L, 10L)).sameElements(IArray(2L, 3L, 4L))) + assert(diffsB(IArray(1L, 3L, 6L, 10L))(contextual).sameElements(IArray(2L, 3L, 4L))) + assert(diffsB(IArray(1L, 3L, 6L, 10L))(contextualWide).sameElements(IArray(2L, 3L, 4L)))