diff --git a/src/main/scala/strawman/collection/Factories.scala b/src/main/scala/strawman/collection/Factories.scala index 074a55c382..9c328199d1 100644 --- a/src/main/scala/strawman/collection/Factories.scala +++ b/src/main/scala/strawman/collection/Factories.scala @@ -1,20 +1,106 @@ package strawman package collection -import strawman.collection.mutable.Builder +import scala.language.implicitConversions + +import strawman.collection.mutable.{ArrayBuffer, Builder} import scala.{Any, Int, Nothing, Ordering} import scala.annotation.unchecked.uncheckedVariance + +/** 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. + * + * @tparam From Type of source collection + * @tparam A Type of elements (e.g. `Int`, `Boolean`, etc.) + * @tparam C Type of collection (e.g. `List[Int]`, `TreeMap[Int, String]`, etc.) + */ +trait BuildFrom[-From, -A, +C] extends Any { + def fromSpecificIterable(from: From)(it: Iterable[A]): C +} + +object BuildFrom extends BuildFromLowPriority { + /** Build the source collection type from a MapOps */ + implicit def buildFromMapOps[CC[K, V] <: Map[K, V] with MapOps[K, V, CC, _], A, B, E, F]: BuildFrom[CC[A, B], (E, F), CC[E, F]] = new BuildFrom[CC[A, B], (E, F), CC[E, F]] { + //TODO: Reuse a prototype instance + def fromSpecificIterable(from: CC[A, B])(it: Iterable[(E, F)]): CC[E, F] = from.mapFactory.fromIterable(it) + } + + /** Build the source collection type from a SortedMapOps */ + implicit def buildFromSortedMapOps[CC[K, V] <: SortedMap[K, V] with SortedMapOps[K, V, CC, _], A, B, E : Ordering, F]: BuildFrom[CC[A, B], (E, F), CC[E, F]] = new BuildFrom[CC[A, B], (E, F), CC[E, F]] { + //TODO: Reuse a prototype instance + def fromSpecificIterable(from: CC[A, B])(it: Iterable[(E, F)]): CC[E, F] = from.sortedMapFactory.fromSpecificIterable(it) + } +} + +trait BuildFromLowPriority { + /** Build the source collection type from an IterableOps */ + implicit def buildFromIterableOps[CC[X] <: IterableOps[X, CC, _], A, E]: BuildFrom[CC[A], E, CC[E]] = new BuildFrom[CC[A], E, CC[E]] { + //TODO: Reuse a prototype instance + def fromSpecificIterable(from: CC[A])(it: Iterable[E]): CC[E] = from.iterableFactory.fromIterable(it) + } + + /** Build the source collection type from an Iterable with SortedOps */ + implicit def buildFromSortedOps[CC[X] <: Iterable[X] with SortedOps[X, CC[X], CC], A, E : Ordering]: BuildFrom[CC[A], E, CC[E]] = new BuildFrom[CC[A], E, CC[E]] { + def fromSpecificIterable(from: CC[A])(it: Iterable[E]): CC[E] = from.sortedIterableFactory.fromSpecificIterable(it) + } +} + +/** A more specific `BuildFrom` for strict target collection types which can provide a `Builder`. + * Note that a `Builder` can be obtained for any `BuildFrom` via `Builder.from`. + */ +trait StrictBuildFrom[-From, -A, +C] extends Any with BuildFrom[From, A, C] { + def newBuilder(from: From): Builder[A, C] +} + +object StrictBuildFrom extends StrictBuildFromLowPriority { + /** Build the source collection type from a strict MapOps */ + implicit def strictBuildFromMapOps[CC[K, V] <: Map[K, V] with MapOps[K, V, CC, _], A, B, E, F]: StrictBuildFrom[CC[A, B], (E, F), CC[E, F]] = new StrictBuildFrom[CC[A, B], (E, F), CC[E, F]] { + //TODO: Reuse a prototype instance + def newBuilder(from: CC[A, B]): Builder[(E, F), CC[E, F]] = from.mapFactory.asInstanceOf[MapFactoryWithBuilder[CC]].newBuilder[E, F]() + def fromSpecificIterable(from: CC[A, B])(it: Iterable[(E, F)]): CC[E, F] = from.mapFactory.fromIterable(it) + } + + /** Build the source collection type from a strict SortedMapOps */ + implicit def strictBuildFromSortedMapOps[CC[K, V] <: SortedMap[K, V] with SortedMapOps[K, V, CC, _], A, B, E : Ordering, F]: StrictBuildFrom[CC[A, B], (E, F), CC[E, F]] = new StrictBuildFrom[CC[A, B], (E, F), CC[E, F]] { + //TODO: Reuse a prototype instance + def newBuilder(from: CC[A, B]): Builder[(E, F), CC[E, F]] = from.sortedMapFactory.asInstanceOf[SortedMapFactoryWithBuilder[CC]].newBuilder[E, F]() + def fromSpecificIterable(from: CC[A, B])(it: Iterable[(E, F)]): CC[E, F] = from.sortedMapFactory.fromSpecificIterable(it) + } +} + +trait StrictBuildFromLowPriority { + /** Build the source collection type from a strict IterableOps */ + implicit def strictBuildFromIterableOps[CC[X] <: IterableOps[X, CC, _] with Buildable[X, CC[X]], A, E]: StrictBuildFrom[CC[A], E, CC[E]] = new StrictBuildFrom[CC[A], E, CC[E]] { + //TODO: Reuse a prototype instance + def newBuilder(from: CC[A]): Builder[E, CC[E]] = from.iterableFactory.asInstanceOf[IterableFactoryWithBuilder[CC]].newBuilder[E]() + def fromSpecificIterable(from: CC[A])(it: Iterable[E]): CC[E] = from.iterableFactory.fromIterable(it) + } + + /** Build the source collection type from a strict Iterable with SortedOps */ + implicit def strictBuildFromSortedOps[CC[X] <: Iterable[X] with SortedOps[X, CC[X], CC] with Buildable[X, CC[X]], A, E : Ordering]: StrictBuildFrom[CC[A], E, CC[E]] = new StrictBuildFrom[CC[A], E, CC[E]] { + def newBuilder(from: CC[A]): Builder[E, CC[E]] = from.sortedIterableFactory.asInstanceOf[SortedIterableFactoryWithBuilder[CC]].newBuilder[E]() + def fromSpecificIterable(from: CC[A])(it: Iterable[E]): CC[E] = from.sortedIterableFactory.fromSpecificIterable(it) + } +} + /** * Builds a collection of type `C` from 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 FromSpecificIterable[-A, +C] extends Any { +trait FromSpecificIterable[-A, +C] extends Any with BuildFrom[Any, A, C] { + def fromSpecificIterable(from: Any)(it: Iterable[A]): C = fromSpecificIterable(it) def fromSpecificIterable(it: Iterable[A]): C } +/** A more specific `FromSpecificIterable` for strict collection types which can provide a `Builder`. */ +trait FromSpecificIterableWithBuilder[-A, +C] extends Any with FromSpecificIterable[A, C] with StrictBuildFrom[Any, A, C] { + def newBuilder(from: Any): Builder[A, C] = newBuilder + def newBuilder: Builder[A, C] +} + /** Base trait for companion objects of unconstrained collection types */ trait IterableFactory[+CC[_]] { @@ -28,13 +114,7 @@ trait IterableFactory[+CC[_]] { } -trait IterableFactoryWithBuilder[+CC[_]] extends IterableFactory[CC] { - def newBuilder[A](): Builder[A, CC[A]] -} - object IterableFactory { - import scala.language.implicitConversions - implicit def toSpecific[A, CC[_]](factory: IterableFactory[CC]): FromSpecificIterable[A, CC[A]] = new FromSpecificIterable[A, CC[A]] { def fromSpecificIterable(it: Iterable[A]): CC[A] = factory.fromIterable[A](it) @@ -44,7 +124,24 @@ object IterableFactory { def empty[A]: CC[A] = delegate.empty def fromIterable[E](it: Iterable[E]): CC[E] = delegate.fromIterable(it) } +} +trait IterableFactoryWithBuilder[+CC[_]] extends IterableFactory[CC] { + def newBuilder[A](): Builder[A, CC[A]] +} + +object IterableFactoryWithBuilder { + implicit def toSpecific[A, CC[_]](factory: IterableFactoryWithBuilder[CC]): FromSpecificIterableWithBuilder[A, CC[A]] = + new FromSpecificIterableWithBuilder[A, CC[A]] { + def fromSpecificIterable(it: Iterable[A]): CC[A] = factory.fromIterable[A](it) + def newBuilder: Builder[A, CC[A]] = factory.newBuilder[A]() + } + + class Delegate[CC[_]](delegate: IterableFactoryWithBuilder[CC]) extends IterableFactoryWithBuilder[CC] { + def empty[A]: CC[A] = delegate.empty + def fromIterable[E](it: Iterable[E]): CC[E] = delegate.fromIterable(it) + def newBuilder[A](): Builder[A, CC[A]] = delegate.newBuilder[A]() + } } trait SpecificIterableFactory[-A, +C] extends FromSpecificIterable[A, C] { @@ -55,6 +152,8 @@ trait SpecificIterableFactory[-A, +C] extends FromSpecificIterable[A, C] { def fill(n: Int)(elem: => A): C = fromSpecificIterable(View.Fill(n)(elem)) } +trait SpecificIterableFactoryWithBuilder[-A, +C] extends SpecificIterableFactory[A, C] with FromSpecificIterableWithBuilder[A, C] + /** Factory methods for collections of kind `* −> * -> *` */ trait MapFactory[+CC[X, Y]] { @@ -65,8 +164,6 @@ trait MapFactory[+CC[X, Y]] { } object MapFactory { - import scala.language.implicitConversions - implicit def toSpecific[K, V, CC[X, Y]](factory: MapFactory[CC]): FromSpecificIterable[(K, V), CC[K, V]] = new FromSpecificIterable[(K, V), CC[K, V]] { def fromSpecificIterable(it: Iterable[(K, V)]): CC[K, V] = factory.fromIterable[K, V](it) @@ -76,7 +173,24 @@ object MapFactory { def fromIterable[K, V](it: Iterable[(K, V)]): C[K, V] = delegate.fromIterable(it) def empty[K, V]: C[K, V] = delegate.empty } +} + +trait MapFactoryWithBuilder[+CC[X, Y]] extends MapFactory[CC] { + def newBuilder[K, V](): Builder[(K, V), CC[K, V]] +} +object MapFactoryWithBuilder { + implicit def toSpecific[K, V, CC[X, Y]](factory: MapFactoryWithBuilder[CC]): FromSpecificIterableWithBuilder[(K, V), CC[K, V]] = + new FromSpecificIterableWithBuilder[(K, V), CC[K, V]] { + def fromSpecificIterable(it: Iterable[(K, V)]): CC[K, V] = factory.fromIterable[K, V](it) + def newBuilder: Builder[(K, V), CC[K, V]] = factory.newBuilder[K, V]() + } + + class Delegate[C[X, Y]](delegate: MapFactoryWithBuilder[C]) extends MapFactoryWithBuilder[C] { + def fromIterable[K, V](it: Iterable[(K, V)]): C[K, V] = delegate.fromIterable(it) + def empty[K, V]: C[K, V] = delegate.empty + def newBuilder[K, V](): Builder[(K, V), C[K, V]] = delegate.newBuilder() + } } /** Base trait for companion objects of collections that require an implicit evidence */ @@ -92,8 +206,6 @@ trait SortedIterableFactory[+CC[_]] { } object SortedIterableFactory { - import scala.language.implicitConversions - implicit def toSpecific[A: Ordering, CC[_]](factory: SortedIterableFactory[CC]): FromSpecificIterable[A, CC[A]] = new FromSpecificIterable[A, CC[A]] { def fromSpecificIterable(it: Iterable[A]): CC[A] = factory.sortedFromIterable[A](it) @@ -103,7 +215,24 @@ object SortedIterableFactory { def empty[A : Ordering]: CC[A] = delegate.empty def sortedFromIterable[E : Ordering](it: Iterable[E]): CC[E] = delegate.sortedFromIterable(it) } +} +trait SortedIterableFactoryWithBuilder[+CC[_]] extends SortedIterableFactory[CC] { + def newBuilder[A : Ordering](): Builder[A, CC[A]] +} + +object SortedIterableFactoryWithBuilder { + implicit def toSpecific[A: Ordering, CC[_]](factory: SortedIterableFactoryWithBuilder[CC]): FromSpecificIterableWithBuilder[A, CC[A]] = + new FromSpecificIterableWithBuilder[A, CC[A]] { + def fromSpecificIterable(it: Iterable[A]): CC[A] = factory.sortedFromIterable[A](it) + def newBuilder: Builder[A, CC[A]] = factory.newBuilder[A]() + } + + class Delegate[CC[_]](delegate: SortedIterableFactoryWithBuilder[CC]) extends SortedIterableFactoryWithBuilder[CC] { + def empty[A : Ordering]: CC[A] = delegate.empty + def sortedFromIterable[E : Ordering](it: Iterable[E]): CC[E] = delegate.sortedFromIterable(it) + def newBuilder[A : Ordering](): Builder[A, CC[A]] = delegate.newBuilder[A]() + } } /** Factory methods for collections of kind `* −> * -> *` which require an implicit evidence value for the key type */ @@ -118,8 +247,6 @@ trait SortedMapFactory[+CC[X, Y]] { } object SortedMapFactory { - import scala.language.implicitConversions - implicit def toSpecific[K : Ordering, V, CC[_, _]](factory: SortedMapFactory[CC]): FromSpecificIterable[(K, V), CC[K, V]] = new FromSpecificIterable[(K, V), CC[K, V]] { def fromSpecificIterable(it: Iterable[(K, V)]): CC[K, V] = factory.sortedFromIterable(it) @@ -129,5 +256,22 @@ object SortedMapFactory { def empty[K: Ordering, V]: CC[K, V] = delegate.empty[K, V] def sortedFromIterable[K: Ordering, V](it: Iterable[(K, V)]): CC[K, V] = delegate.sortedFromIterable(it) } +} +trait SortedMapFactoryWithBuilder[+CC[X, Y]] extends SortedMapFactory[CC] { + def newBuilder[K : Ordering, V](): Builder[(K, V), CC[K, V]] +} + +object SortedMapFactoryWithBuilder { + implicit def toSpecific[K : Ordering, V, CC[X, Y]](factory: SortedMapFactoryWithBuilder[CC]): FromSpecificIterableWithBuilder[(K, V), CC[K, V]] = + new FromSpecificIterableWithBuilder[(K, V), CC[K, V]] { + def fromSpecificIterable(it: Iterable[(K, V)]): CC[K, V] = factory.fromSpecificIterable(it) + def newBuilder: Builder[(K, V), CC[K, V]] = factory.newBuilder[K, V]() + } + + class Delegate[C[X, Y]](delegate: SortedMapFactoryWithBuilder[C]) extends SortedMapFactoryWithBuilder[C] { + def sortedFromIterable[K : Ordering, V](it: Iterable[(K, V)]): C[K, V] = delegate.sortedFromIterable(it) + def empty[K : Ordering, V]: C[K, V] = delegate.empty + def newBuilder[K : Ordering, V](): Builder[(K, V), C[K, V]] = delegate.newBuilder() + } } diff --git a/src/main/scala/strawman/collection/Iterable.scala b/src/main/scala/strawman/collection/Iterable.scala index 8fbd85e6c1..fc19228eca 100644 --- a/src/main/scala/strawman/collection/Iterable.scala +++ b/src/main/scala/strawman/collection/Iterable.scala @@ -253,6 +253,10 @@ trait IterableOps[+A, +CC[X], +C] extends Any { } /** Base trait for strict collections that can be built using a builder. + * + * All factories provided by such a collection must be of the `*WithBuilder` variant + * even though this is not formally enforced through types. + * * @tparam A the element type of the collection * @tparam C the type of the underlying collection */ diff --git a/src/main/scala/strawman/collection/Map.scala b/src/main/scala/strawman/collection/Map.scala index b6d7a21884..e31bfc8ed8 100644 --- a/src/main/scala/strawman/collection/Map.scala +++ b/src/main/scala/strawman/collection/Map.scala @@ -19,6 +19,8 @@ trait MapOps[K, +V, +CC[X, Y] <: Map[X, Y], +C <: Map[K, V]] /** Similar to fromIterable, but returns a Map collection type */ protected[this] def mapFromIterable[K2, V2](it: Iterable[(K2, V2)]): CC[K2, V2] + def mapFactory: MapFactory[CC] + /** Optionally returns the value associated with a key. * * @param key the key value diff --git a/src/main/scala/strawman/collection/SortedMap.scala b/src/main/scala/strawman/collection/SortedMap.scala index ef3e20d500..9d76ebc159 100644 --- a/src/main/scala/strawman/collection/SortedMap.scala +++ b/src/main/scala/strawman/collection/SortedMap.scala @@ -12,7 +12,11 @@ trait SortedMap[K, +V] trait SortedMapOps[K, +V, +CC[X, Y] <: SortedMap[X, Y] with SortedMapOps[X, Y, CC, _], +C <: SortedMap[K, V]] extends MapOps[K, V, Map, C] - with SortedOps[K, C] { + with SortedOps[K, C, SortedSet] { + + def sortedIterableFactory = SortedSet + + def sortedMapFactory: SortedMapFactory[CC] protected[this] def sortedMapFromIterable[K2, V2](it: collection.Iterable[(K2, V2)])(implicit ordering: Ordering[K2]): CC[K2, V2] diff --git a/src/main/scala/strawman/collection/SortedOps.scala b/src/main/scala/strawman/collection/SortedOps.scala index ce0e7763f8..b0680004a1 100644 --- a/src/main/scala/strawman/collection/SortedOps.scala +++ b/src/main/scala/strawman/collection/SortedOps.scala @@ -3,10 +3,12 @@ package strawman.collection import scala.{Ordering, Option, Some} /** Base trait for sorted collections */ -trait SortedOps[A, +C] { +trait SortedOps[A, +C, +CC[_]] { implicit def ordering: Ordering[A] + def sortedIterableFactory: SortedIterableFactory[CC] + /** Returns the first key of the collection. */ def firstKey: A diff --git a/src/main/scala/strawman/collection/SortedSet.scala b/src/main/scala/strawman/collection/SortedSet.scala index e7e41cd007..86ff04b8c1 100644 --- a/src/main/scala/strawman/collection/SortedSet.scala +++ b/src/main/scala/strawman/collection/SortedSet.scala @@ -9,7 +9,7 @@ trait SortedSet[A] extends Set[A] with SortedSetOps[A, SortedSet, SortedSet[A]] trait SortedSetOps[A, +CC[X], +C <: SortedSet[A]] extends SetOps[A, Set, C] - with SortedOps[A, C] { + with SortedOps[A, C, CC] { protected[this] def sortedFromIterable[B: Ordering](it: Iterable[B]): CC[B] diff --git a/src/main/scala/strawman/collection/immutable/BitSet.scala b/src/main/scala/strawman/collection/immutable/BitSet.scala index b704e69bc4..9f8b8ecba0 100644 --- a/src/main/scala/strawman/collection/immutable/BitSet.scala +++ b/src/main/scala/strawman/collection/immutable/BitSet.scala @@ -27,6 +27,7 @@ sealed abstract class BitSet def empty: BitSet = BitSet.empty def iterableFactory = Set + def sortedIterableFactory = SortedSet protected[this] def fromSpecificIterable(coll: collection.Iterable[Int]): BitSet = BitSet.fromSpecificIterable(coll) protected[this] def sortedFromIterable[B : Ordering](it: collection.Iterable[B]): SortedSet[B] = SortedSet.sortedFromIterable(it) diff --git a/src/main/scala/strawman/collection/immutable/HashMap.scala b/src/main/scala/strawman/collection/immutable/HashMap.scala index 26778f57c5..9757574c7d 100644 --- a/src/main/scala/strawman/collection/immutable/HashMap.scala +++ b/src/main/scala/strawman/collection/immutable/HashMap.scala @@ -34,6 +34,7 @@ sealed trait HashMap[K, +V] import HashMap.{bufferSize, liftMerger, Merger, MergeFunction, nullToEmpty} def iterableFactory = List + def mapFactory = HashMap protected[this] def fromSpecificIterable(coll: collection.Iterable[(K, V)]): HashMap[K, V] = HashMap.fromIterable(coll) diff --git a/src/main/scala/strawman/collection/immutable/ListMap.scala b/src/main/scala/strawman/collection/immutable/ListMap.scala index b8b9ca56c0..8a8291d853 100644 --- a/src/main/scala/strawman/collection/immutable/ListMap.scala +++ b/src/main/scala/strawman/collection/immutable/ListMap.scala @@ -49,6 +49,7 @@ sealed class ListMap[K, +V] with Serializable { def iterableFactory = List + def mapFactory = ListMap protected[this] def mapFromIterable[K2, V2](it: collection.Iterable[(K2, V2)]): ListMap[K2,V2] = ListMap.fromIterable(it) diff --git a/src/main/scala/strawman/collection/immutable/TreeMap.scala b/src/main/scala/strawman/collection/immutable/TreeMap.scala index b22d1d76e8..b115c5fbb0 100644 --- a/src/main/scala/strawman/collection/immutable/TreeMap.scala +++ b/src/main/scala/strawman/collection/immutable/TreeMap.scala @@ -37,6 +37,8 @@ final class TreeMap[K, +V] private (tree: RB.Tree[K, V])(implicit val ordering: def this()(implicit ordering: Ordering[K]) = this(null)(ordering) def iterableFactory = List + def mapFactory = Map + def sortedMapFactory = TreeMap protected[this] def fromSpecificIterable(coll: collection.Iterable[(K, V)]): TreeMap[K, V] = TreeMap.sortedFromIterable(coll) diff --git a/src/main/scala/strawman/collection/immutable/TreeSet.scala b/src/main/scala/strawman/collection/immutable/TreeSet.scala index f21ce1d508..f85e623ce9 100644 --- a/src/main/scala/strawman/collection/immutable/TreeSet.scala +++ b/src/main/scala/strawman/collection/immutable/TreeSet.scala @@ -2,7 +2,7 @@ package strawman package collection package immutable -import mutable.Builder +import mutable.{ArrayBuffer, Builder} import immutable.{RedBlackTree => RB} import scala.{Boolean, Int, NullPointerException, Option, Ordering, Some, Unit} @@ -63,6 +63,8 @@ final class TreeSet[A] private (tree: RB.Tree[A, Unit])(implicit val ordering: O def iterableFactory = Set + def sortedIterableFactory = TreeSet + protected[this] def fromSpecificIterable(coll: strawman.collection.Iterable[A]): TreeSet[A] = TreeSet.sortedFromIterable(coll) @@ -103,7 +105,7 @@ final class TreeSet[A] private (tree: RB.Tree[A, Unit])(implicit val ordering: O else newSet(RB.delete(tree, elem)) } -object TreeSet extends SortedIterableFactory[TreeSet] { +object TreeSet extends SortedIterableFactoryWithBuilder[TreeSet] { def empty[A: Ordering]: TreeSet[A] = new TreeSet[A] @@ -113,4 +115,5 @@ object TreeSet extends SortedIterableFactory[TreeSet] { case _ => empty[E] ++ it } + def newBuilder[A : Ordering](): Builder[A, TreeSet[A]] = new ArrayBuffer[A].mapResult(sortedFromIterable[A] _) } diff --git a/src/main/scala/strawman/collection/mutable/BitSet.scala b/src/main/scala/strawman/collection/mutable/BitSet.scala index 335a7e92b4..8e1dafea41 100644 --- a/src/main/scala/strawman/collection/mutable/BitSet.scala +++ b/src/main/scala/strawman/collection/mutable/BitSet.scala @@ -36,6 +36,8 @@ class BitSet(protected[collection] final var elems: Array[Long]) def iterableFactory = Set + def sortedIterableFactory = SortedSet + protected[this] def sortedFromIterable[B : Ordering](it: collection.Iterable[B]): collection.mutable.SortedSet[B] = collection.mutable.SortedSet.sortedFromIterable(it) diff --git a/src/main/scala/strawman/collection/mutable/Builder.scala b/src/main/scala/strawman/collection/mutable/Builder.scala index 90302c0453..bb551bc559 100644 --- a/src/main/scala/strawman/collection/mutable/Builder.scala +++ b/src/main/scala/strawman/collection/mutable/Builder.scala @@ -1,8 +1,11 @@ package strawman.collection.mutable -import scala.{Boolean, Any, Char, Unit} +import scala.{Any, Boolean, Char, Unit} import java.lang.String -import strawman.collection.IterableOnce + +import strawman.collection.{BuildFrom, IterableFactory, IterableFactoryWithBuilder, IterableOnce, SortedIterableFactory, StrictBuildFrom} + +import scala.math.Ordering /** Base trait for collection builders */ trait Builder[-A, +To] extends Growable[A] { self => @@ -24,6 +27,29 @@ trait Builder[-A, +To] extends Growable[A] { self => } } +object Builder { + /** Get a proper builder for an IterableFactoryWithBuilder, otherwise a Builder that uses an intermediate + * ArrayBuffer to store the elements. */ + def from[A, CC[_]](fact: IterableFactory[CC]): Builder[A, CC[A]] = fact match { + case fact: IterableFactoryWithBuilder[CC] => fact.newBuilder[A]() + case fact => new ArrayBuffer[A]().mapResult(fact.fromIterable _) + } + + /** Get a proper builder for a SortedIterableFactoryWithBuilder, otherwise a Builder that uses an intermediate + * ArrayBuffer to store the elements. */ + def from[A : Ordering, CC[_]](fact: SortedIterableFactory[CC]): Builder[A, CC[A]] = fact match { + case fact: IterableFactoryWithBuilder[CC] => fact.newBuilder[A]() + case fact => new ArrayBuffer[A]().mapResult(fact.sortedFromIterable[A] _) + } + + /** Get a proper builder for a StrictBuildFrom, otherwise a Builder that uses an intermediate + * ArrayBuffer to store the elements. */ + def from[From, A, C](bf: BuildFrom[From, A, C], from: From): Builder[A, C] = bf match { + case bf: StrictBuildFrom[From, A, C] => bf.newBuilder(from) + case bf => new ArrayBuffer[A]().mapResult(bf.fromSpecificIterable(from) _) + } +} + class StringBuilder extends Builder[Char, String] { private val sb = new java.lang.StringBuilder diff --git a/src/main/scala/strawman/collection/mutable/HashMap.scala b/src/main/scala/strawman/collection/mutable/HashMap.scala index 82ccaf3028..3b0e59938f 100644 --- a/src/main/scala/strawman/collection/mutable/HashMap.scala +++ b/src/main/scala/strawman/collection/mutable/HashMap.scala @@ -26,6 +26,8 @@ final class HashMap[K, V] private[collection] (contents: HashTable.Contents[K, D with MapOps[K, V, HashMap, HashMap[K, V]] with Serializable { + def mapFactory = HashMap + private[this] val table: HashTable[K, V, DefaultEntry[K, V]] = new HashTable[K, V, DefaultEntry[K, V]] { def createNewEntry(key: K, value: V): DefaultEntry[K, V] = new Entry(key, value) diff --git a/src/main/scala/strawman/collection/mutable/TreeMap.scala b/src/main/scala/strawman/collection/mutable/TreeMap.scala index 7bcf32137e..75fcf6190b 100644 --- a/src/main/scala/strawman/collection/mutable/TreeMap.scala +++ b/src/main/scala/strawman/collection/mutable/TreeMap.scala @@ -27,6 +27,9 @@ sealed class TreeMap[K, V] private (tree: RB.Tree[K, V])(implicit val ordering: with SortedMapOps[K, V, TreeMap, TreeMap[K, V]] with Serializable { + def mapFactory = Map + def sortedMapFactory = TreeMap + /** * Creates an empty `TreeMap`. * @param ord the implicit ordering used to compare objects of type `K`. diff --git a/src/main/scala/strawman/collection/mutable/TreeSet.scala b/src/main/scala/strawman/collection/mutable/TreeSet.scala index e6689663f9..ed96195601 100644 --- a/src/main/scala/strawman/collection/mutable/TreeSet.scala +++ b/src/main/scala/strawman/collection/mutable/TreeSet.scala @@ -1,7 +1,7 @@ package strawman package collection.mutable -import collection.SortedIterableFactory +import collection.SortedIterableFactoryWithBuilder import collection.mutable.{RedBlackTree => RB} import scala.{Boolean, Int, None, Null, NullPointerException, Option, Ordering, Serializable, SerialVersionUID, Some, Unit} @@ -45,6 +45,8 @@ sealed class TreeSet[A] private (tree: RB.Tree[A, Null])(implicit val ordering: def iterableFactory = Set + def sortedIterableFactory = TreeSet + def keysIteratorFrom(start: A): collection.Iterator[A] = RB.keysIterator(tree, Some(start)) def empty: TreeSet[A] = TreeSet.empty @@ -175,10 +177,11 @@ sealed class TreeSet[A] private (tree: RB.Tree[A, Null])(implicit val ordering: * @author Lucien Pereira * */ -object TreeSet extends SortedIterableFactory[TreeSet] { +object TreeSet extends SortedIterableFactoryWithBuilder[TreeSet] { def empty[A : Ordering]: TreeSet[A] = new TreeSet[A]() def sortedFromIterable[E : Ordering](it: collection.Iterable[E]): TreeSet[E] = Growable.fromIterable(empty[E], it) + def newBuilder[A : Ordering](): Builder[A, TreeSet[A]] = new ArrayBuffer[A].mapResult(sortedFromIterable[A] _) } diff --git a/src/test/scala/strawman/collection/test/GenericTest.scala b/src/test/scala/strawman/collection/test/GenericTest.scala new file mode 100644 index 0000000000..3ce4bab7eb --- /dev/null +++ b/src/test/scala/strawman/collection/test/GenericTest.scala @@ -0,0 +1,59 @@ +package strawman +package collection +package test + +import mutable.Builder + +import scala.util.Try +import scala.{Any, Int, None, Nothing, Option, Ordering, Some, Unit} +import scala.Predef.{augmentString, implicitly, wrapRefArray, assert} +import java.lang.String + +import org.junit.Test + +trait Parse[A] { + def parse(s: String): Option[A] +} + +object Parse { + + implicit def parseInt: Parse[Int] = (s: String) => Try(s.toInt).toOption + + implicit def parseTuple[A, B](implicit + parseA: Parse[A], + parseB: Parse[B] + ): Parse[(A, B)] = { (s: String) => + val parts = s.split("-") + (parseA.parse(parts(0)), parseB.parse(parts(1))) match { + case (Some(a), Some(b)) => Some((a, b)) + case _ => None + } + } + + def parseCollection[A, C](bf: BuildFrom[Any, A, C]) + (implicit parseA: Parse[A]): Parse[C] = { (s: String) => + val parts = s.split("\\|") + parts.foldLeft[Option[Builder[A, C]]](Some(Builder.from(bf, null))) { (maybeBuilder, s) => + (maybeBuilder, parseA.parse(s)) match { + case (Some(builder), Some(a)) => + scala.Predef.println(a) + scala.Predef.println(builder.result) + Some(builder += a) + case _ => None + } + }.map(_.result) + } + +} + +class GenericTest { + + @Test + def genericTest: Unit = { + assert(Parse.parseCollection[Int, immutable.List[Int]](immutable.List).parse("1|2|3").contains(1 :: 2 :: 3 :: immutable.Nil)) + + // TODO wrap with assert when HashMap’s equality is correctly implemented + Parse.parseCollection[(Int, Int), immutable.HashMap[Int, Int]](immutable.HashMap).parse("1-2|3-4").contains(immutable.HashMap((1, 2), (3, 4))) + } + +} diff --git a/src/test/scala/strawman/collection/test/TraverseTest.scala b/src/test/scala/strawman/collection/test/TraverseTest.scala index 00970cd8be..883b035c9e 100644 --- a/src/test/scala/strawman/collection/test/TraverseTest.scala +++ b/src/test/scala/strawman/collection/test/TraverseTest.scala @@ -3,51 +3,149 @@ package strawman package collection.test import org.junit.Test -import strawman.collection.Iterable -import strawman.collection.mutable.{ArrayBuffer, Builder, Growable} import strawman.collection._ +import strawman.collection.mutable.{ArrayBuffer, Builder, Growable} -import scala.{Any, Either, Int, Left, None, Option, Right, Some, Unit} +import scala.{Any, Either, Int, Left, None, Option, Right, Some, Unit, PartialFunction, Boolean} +import java.lang.String import scala.Predef.ArrowAssoc import scala.math.Ordering -import java.lang.String class TraverseTest { - def optionSequence[CC[X] <: Iterable[X] with IterableOps[X, CC, _], A](xs: CC[Option[A]]): Option[CC[A]] = { - def folder[F[X] <: Growable[X]]: (Option[F[A]], Option[A]) => Option[F[A]] = { (bo, xo) => - (bo, xo) match { - case (Some(builder), Some(a)) => Some(builder += a) - case _ => None - } - } - val factory = xs.iterableFactory - factory match { - case iterableBuilder: IterableFactoryWithBuilder[CC] => - xs.foldLeft[Option[Builder[A, CC[A]]]]( - Some(iterableBuilder.newBuilder[A]()) - )( - folder[({ type l[X] = Builder[X, CC[X]] })#l] - ).map(_.result) - case _ => - xs.foldLeft[Option[ArrayBuffer[A]]](Some(new ArrayBuffer[A]))(folder).map(_.to(xs.iterableFactory)) - } - } + // You can either overload methods for IterableOps and Iterable with SortedOps (if you want to support constrained collection types) + def optionSequence1[C[X] <: IterableOps[X, C, _], A](xs: C[Option[A]]): Option[C[A]] = + xs.foldLeft[Option[Builder[A, C[A]]]](Some(Builder.from[A, C](xs.iterableFactory))) { + case (Some(builder), Some(a)) => Some(builder += a) + case _ => None + }.map(_.result) + def optionSequence1[C[X] <: Iterable[X] with SortedOps[X, C[X], C], A : Ordering](xs: C[Option[A]]): Option[C[A]] = + xs.foldLeft[Option[Builder[A, C[A]]]](Some(Builder.from[A, C](xs.sortedIterableFactory))) { + case (Some(builder), Some(a)) => Some(builder += a) + case _ => None + }.map(_.result) + + // ...or use BuildFrom to abstract over both and also allow building arbitrary collection types + def optionSequence2[CC[X] <: Iterable[X], A, To](xs: CC[Option[A]])(implicit bf: BuildFrom[CC[Option[A]], A, To]): Option[To] = + xs.foldLeft[Option[Builder[A, To]]](Some(Builder.from(bf, xs))) { + case (Some(builder), Some(a)) => Some(builder += a) + case _ => None + }.map(_.result) + + // Using dependent types: + def optionSequence3[A, To](xs: Iterable[Option[A]])(implicit bf: BuildFrom[xs.type, A, To]): Option[To] = + xs.foldLeft[Option[Builder[A, To]]](Some(Builder.from[xs.type, A, To](bf, xs))) { + case (Some(builder), Some(a)) => Some(builder += a) + case _ => None + }.map(_.result) + + def eitherSequence[A, B, To](xs: Iterable[Either[A, B]])(implicit bf: BuildFrom[xs.type, B, To]): Either[A, To] = + xs.foldLeft[Either[A, Builder[B, To]]](Right(Builder.from[xs.type, B, To](bf, xs))) { + case (Right(builder), Right(b)) => Right(builder += b) + case (Left(a) , _) => Left(a) + case (_ , Left(a)) => Left(a) + }.map(_.result) @Test def optionSequence1Test: Unit = { val xs1 = immutable.List(Some(1), None, Some(2)) - val o1 = optionSequence(xs1) + val o1 = optionSequence1(xs1) val o1t: Option[immutable.List[Int]] = o1 - val xs2: immutable.Set[Option[String]] = immutable.TreeSet(Some("foo"), Some("bar"), None) - val o2 = optionSequence(xs2) + val xs2: immutable.TreeSet[Option[String]] = immutable.TreeSet(Some("foo"), Some("bar"), None) + val o2 = optionSequence1(xs2) val o2t: Option[immutable.Set[String]] = o2 val xs4 = immutable.List[Option[(Int, String)]](Some((1 -> "a")), Some((2 -> "b"))) - val o4 = optionSequence(xs4) + val o4 = optionSequence1(xs4) val o4t: Option[immutable.List[(Int, String)]] = o4 val o5: Option[immutable.TreeMap[Int, String]] = o4.map(_.to(immutable.TreeMap)) } + @Test + def optionSequence2Test: Unit = { + val xs1 = immutable.List(Some(1), None, Some(2)) + val o1 = optionSequence2(xs1) + val o1t: Option[immutable.List[Int]] = o1 + + val xs2 = immutable.TreeSet(Some("foo"), Some("bar"), None) + val o2 = optionSequence2(xs2) + val o2t: Option[immutable.TreeSet[String]] = o2 + + // Breakout-like use case from https://github.com/scala/scala/pull/5233: + val xs4 = immutable.List[Option[(Int, String)]](Some((1 -> "a")), Some((2 -> "b"))) + val o4 = optionSequence2(xs4)(immutable.TreeMap) // same syntax as in `.to` + val o4t: Option[immutable.TreeMap[Int, String]] = o4 + } + + @Test + def optionSequence3Test: Unit = { + val xs1 = immutable.List(Some(1), None, Some(2)) + val o1 = optionSequence3(xs1) + val o1t: Option[immutable.List[Int]] = o1 + + val xs2 = immutable.TreeSet(Some("foo"), Some("bar"), None) + val o2 = optionSequence3(xs2) + val o2t: Option[immutable.TreeSet[String]] = o2 + + // Breakout-like use case from https://github.com/scala/scala/pull/5233: + val xs4 = immutable.List[Option[(Int, String)]](Some((1 -> "a")), Some((2 -> "b"))) + val o4 = optionSequence3(xs4)(immutable.TreeMap) // same syntax as in `.to` + val o4t: Option[immutable.TreeMap[Int, String]] = o4 + } + + @Test + def eitherSequenceTest: Unit = { + val xs3 = mutable.ListBuffer(Right("foo"), Left(0), Right("bar")) + val e1 = eitherSequence(xs3) + val e1t: Either[Int, mutable.ListBuffer[String]] = e1 + } + + // From https://github.com/scala/collection-strawman/issues/44 + def flatCollect[A, B, To](coll: Iterable[A])(f: PartialFunction[A, IterableOnce[B]]) + (implicit bf: BuildFrom[coll.type, B, To]): To = { + val builder = Builder.from[coll.type, B, To](bf, coll) + for (a <- coll) { + if (f.isDefinedAt(a)) builder ++= f(a) + } + builder.result + } + + def mapSplit[A, B, C, ToL, ToR](coll: Iterable[A])(f: A => Either[B, C]) + (implicit bfLeft: BuildFrom[coll.type, B, ToL], bfRight: BuildFrom[coll.type, C, ToR]): (ToL, ToR) = { + val left = Builder.from[coll.type, B, ToL](bfLeft, coll) + val right = Builder.from[coll.type, C, ToR](bfRight, coll) + for (a <- coll) + f(a).fold(left.add, right.add) + (left.result, right.result) + } + + + @Test + def flatCollectTest: Unit = { + val xs1 = immutable.List(1, 2, 3) + val xs2 = flatCollect(xs1) { case 2 => mutable.ArrayBuffer("foo", "bar") } + val xs3: immutable.List[String] = xs2 + + val xs4 = immutable.TreeMap((1, "1"), (2, "2")) + val xs5 = flatCollect(xs4) { case (2, v) => immutable.List((v, v)) } + val xs6: immutable.TreeMap[String, String] = xs5 + + val xs7 = immutable.HashMap((1, "1"), (2, "2")) + val xs8 = flatCollect(xs7) { case (2, v) => immutable.List((v, v)) } + val xs9: immutable.HashMap[String, String] = xs8 + } + + @Test + def mapSplitTest: Unit = { + val xs1 = immutable.List(1, 2, 3) + val (xs2, xs3) = mapSplit(xs1)(x => if (x % 2 == 0) Left(x) else Right(x.toString)) + val xs4: immutable.List[Int] = xs2 + val xs5: immutable.List[String] = xs3 + + val xs6 = immutable.TreeMap((1, "1"), (2, "2")) + val (xs7, xs8) = mapSplit(xs6) { case (k, v) => Left[(String, Int), (Int, Boolean)]((v, k)) } + val xs9: immutable.TreeMap[String, Int] = xs7 + val xs10: immutable.TreeMap[Int, Boolean] = xs8 + } }