diff --git a/src/main/scala-2.12/collection/Factory.scala b/src/main/scala-2.12/collection/Factory.scala new file mode 100644 index 00000000..9b14aa0e --- /dev/null +++ b/src/main/scala-2.12/collection/Factory.scala @@ -0,0 +1,36 @@ +package scala.collection + +import scala.collection.generic.CanBuildFrom + +/** + * A factory that builds a collection of type `C` with elements of type `A`. + * + * @tparam A Type of elements (e.g. `Int`, `Boolean`, etc.) + * @tparam C Type of collection (e.g. `List[Int]`, `TreeMap[Int, String]`, etc.) + */ +trait Factory[-A, +C] extends Any { + + /** + * @return A collection of type `C` containing the same elements + * as the source collection `it`. + * @param it Source collection + */ + def fromSpecific(it: TraversableOnce[A]): C + + /** Get a Builder for the collection. For non-strict collection types this will use an intermediate buffer. + * Building collections with `fromSpecific` is preferred because it can be lazy for lazy collections. */ + def newBuilder(): mutable.Builder[A, C] +} + +object Factory { + + implicit def fromCanBuildFrom[A, C](implicit cbf: CanBuildFrom[Nothing, A, C]): Factory[A, C] = + new Factory[A, C] { + def fromSpecific(it: TraversableOnce[A]): C = (cbf() ++= it).result() + def newBuilder(): mutable.Builder[A, C] = cbf() + } + + implicit def fromCanBuildFromConversion[X, A, C](x: X)(implicit toCanBuildFrom: X => CanBuildFrom[Nothing, A, C]): Factory[A, C] = + fromCanBuildFrom(toCanBuildFrom(x)) + +} \ No newline at end of file diff --git a/src/main/scala-2.12/collection/compat/package.scala b/src/main/scala-2.12/collection/compat/package.scala index b6c0fc13..0cf415bd 100644 --- a/src/main/scala-2.12/collection/compat/package.scala +++ b/src/main/scala-2.12/collection/compat/package.scala @@ -23,6 +23,9 @@ package object compat { implicit def sortedMapFactoryToCBF[K : Ordering, V, CC[A, B] <: SortedMap[A, B] with SortedMapLike[A, B, CC[A, B]]](fact: SortedMapFactory[CC]): CanBuildFrom[Any, (K, V), CC[K, V]] = simpleCBF(fact.newBuilder[K, V]) + implicit def bitSetFactoryToCBF(fact: BitSetFactory[BitSet]): CanBuildFrom[Any, Int, BitSet] = + simpleCBF(fact.newBuilder) + implicit def immutableBitSetFactoryToCBF(fact: BitSetFactory[immutable.BitSet]): CanBuildFrom[Any, Int, ImmutableBitSetCC[Int]] = simpleCBF(fact.newBuilder) diff --git a/src/test/scala/collection/FactoryTest.scala b/src/test/scala/collection/FactoryTest.scala new file mode 100644 index 00000000..5ea2dd0c --- /dev/null +++ b/src/test/scala/collection/FactoryTest.scala @@ -0,0 +1,31 @@ +package collection + +import org.junit.{Assert, Test} + +import scala.collection.{BitSet, Factory, Iterable, immutable, mutable} +import scala.collection.compat._ + +class FactoryTest { + + implicitly[Factory[Char, String]] + implicitly[Factory[Char, Array[Char]]] + implicitly[Factory[Int, BitSet]] + implicitly[Factory[Int, mutable.BitSet]] + implicitly[Factory[Int, immutable.BitSet]] + + BitSet: Factory[Int, BitSet] + Iterable: Factory[Int, Iterable[Int]] + immutable.TreeSet: Factory[Int, immutable.TreeSet[Int]] + Map: Factory[(Int, String), Map[Int, String]] + immutable.TreeMap: Factory[(Int, String), immutable.TreeMap[Int, String]] + + @Test + def streamFactoryPreservesLaziness(): Unit = { + val factory = implicitly[Factory[Int, Stream[Int]]] + var counter = 0 + val source = Stream.continually { counter += 1; 1 } + val result = factory.fromSpecific(source) + Assert.assertEquals(1, counter) // One element has been evaluated because Stream is not lazy in its head + } + +}