diff --git a/src/main/scala/strawman/collection/Factories.scala b/src/main/scala/strawman/collection/Factories.scala index 074a55c382..253defe64e 100644 --- a/src/main/scala/strawman/collection/Factories.scala +++ b/src/main/scala/strawman/collection/Factories.scala @@ -1,133 +1,165 @@ 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 + + /** Get a Builder for the collection. For non-strict collection types this will use an intermediate buffer. + * Building collections with `fromSpecificIterable` is preferred because it can be lazy for lazy collections. */ + def newBuilder(from: From): Builder[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 newBuilder(from: CC[A, B]): Builder[(E, F), CC[E, F]] = from.mapFactory.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 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]] { + def newBuilder(from: CC[A, B]): Builder[(E, F), CC[E, F]] = from.sortedMapFactory.newBuilder[E, F]() + 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] <: Iterable[X] with IterableOps[X, CC, _], A, E]: BuildFrom[CC[A], E, CC[E]] = new BuildFrom[CC[A], E, CC[E]] { + //TODO: Reuse a prototype instance + def newBuilder(from: CC[A]): Builder[E, CC[E]] = from.iterableFactory.newBuilder[E]() + 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 newBuilder(from: CC[A]): Builder[E, CC[E]] = from.sortedIterableFactory.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 + 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[_]] { - def fromIterable[E](it: Iterable[E]): CC[E] - def empty[A]: CC[A] - def apply[A](xs: A*): CC[A] = fromIterable(View.Elems(xs: _*)) - def fill[A](n: Int)(elem: => A): CC[A] = fromIterable(View.Fill(n)(elem)) - -} - -trait IterableFactoryWithBuilder[+CC[_]] extends IterableFactory[CC] { - def newBuilder[A](): Builder[A, CC[A]] + def newBuilder[A](): Builder[A, CC[A]] = new ArrayBuffer[A]().mapResult(fromIterable _) } 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) + def newBuilder: Builder[A, CC[A]] = factory.newBuilder[A]() } class Delegate[CC[_]](delegate: IterableFactory[CC]) extends IterableFactory[CC] { def empty[A]: CC[A] = delegate.empty def fromIterable[E](it: Iterable[E]): CC[E] = delegate.fromIterable(it) + override def newBuilder[A](): Builder[A, CC[A]] = delegate.newBuilder[A]() } - } trait SpecificIterableFactory[-A, +C] extends FromSpecificIterable[A, C] { def empty: C - def apply(xs: A*): C = fromSpecificIterable(View.Elems(xs: _*)) - def fill(n: Int)(elem: => A): C = fromSpecificIterable(View.Fill(n)(elem)) + def newBuilder: Builder[A, C] = new ArrayBuffer[A]().mapResult(fromSpecificIterable _) } /** Factory methods for collections of kind `* −> * -> *` */ trait MapFactory[+CC[X, Y]] { - def empty[K, V]: CC[K, V] def fromIterable[K, V](it: Iterable[(K, V)]): CC[K, V] - def apply[K, V](elems: (K, V)*): CC[K, V] = fromIterable(elems.toStrawman) + def newBuilder[K, V](): Builder[(K, V), CC[K, V]] = new ArrayBuffer[(K, V)]().mapResult(fromIterable _) } 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) + def newBuilder: Builder[(K, V), CC[K, V]] = factory.newBuilder[K, V]() } class Delegate[C[X, Y]](delegate: MapFactory[C]) extends MapFactory[C] { def fromIterable[K, V](it: Iterable[(K, V)]): C[K, V] = delegate.fromIterable(it) def empty[K, V]: C[K, V] = delegate.empty + override def newBuilder[K, V](): Builder[(K, V), C[K, V]] = delegate.newBuilder() } - } /** Base trait for companion objects of collections that require an implicit evidence */ trait SortedIterableFactory[+CC[_]] { - def sortedFromIterable[E : Ordering](it: Iterable[E]): CC[E] - def empty[A : Ordering]: CC[A] - def apply[A : Ordering](xs: A*): CC[A] = sortedFromIterable(View.Elems(xs: _*)) - def fill[A : Ordering](n: Int)(elem: => A): CC[A] = sortedFromIterable(View.Fill(n)(elem)) + def newBuilder[A : Ordering](): Builder[A, CC[A]] = new ArrayBuffer[A]().mapResult(sortedFromIterable[A] _) } 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) + def newBuilder: Builder[A, CC[A]] = factory.newBuilder[A]() } class Delegate[CC[_]](delegate: SortedIterableFactory[CC]) extends SortedIterableFactory[CC] { def empty[A : Ordering]: CC[A] = delegate.empty def sortedFromIterable[E : Ordering](it: Iterable[E]): CC[E] = delegate.sortedFromIterable(it) + override 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 */ trait SortedMapFactory[+CC[X, Y]] { - def empty[K : Ordering, V]: CC[K, V] - def sortedFromIterable[K : Ordering, V](it: Iterable[(K, V)]): CC[K, V] - def apply[K : Ordering, V](elems: (K, V)*): CC[K, V] = sortedFromIterable(elems.toStrawman) + def newBuilder[K : Ordering, V](): Builder[(K, V), CC[K, V]] = new ArrayBuffer[(K, V)]().mapResult(sortedFromIterable[K, V] _) } object SortedMapFactory { - import scala.language.implicitConversions - - implicit def toSpecific[K : Ordering, V, CC[_, _]](factory: SortedMapFactory[CC]): FromSpecificIterable[(K, V), CC[K, V]] = + implicit def toSpecific[K : Ordering, V, CC[X, Y]](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) + def fromSpecificIterable(it: Iterable[(K, V)]): CC[K, V] = factory.sortedFromIterable[K, V](it) + def newBuilder: Builder[(K, V), CC[K, V]] = factory.newBuilder[K, V]() } - class Delegate[CC[_, _]](delegate: SortedMapFactory[CC]) extends SortedMapFactory[CC] { - 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) + class Delegate[C[X, Y]](delegate: SortedMapFactory[C]) extends SortedMapFactory[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 + override 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..885a258221 100644 --- a/src/main/scala/strawman/collection/Iterable.scala +++ b/src/main/scala/strawman/collection/Iterable.scala @@ -253,6 +253,7 @@ trait IterableOps[+A, +CC[X], +C] extends Any { } /** Base trait for strict collections that can be built using a builder. + * * @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/List.scala b/src/main/scala/strawman/collection/immutable/List.scala index 05315e578a..ccd60a88ad 100644 --- a/src/main/scala/strawman/collection/immutable/List.scala +++ b/src/main/scala/strawman/collection/immutable/List.scala @@ -55,14 +55,14 @@ case object Nil extends List[Nothing] { override def tail: Nothing = throw new UnsupportedOperationException("tail of empty list") } -object List extends IterableFactoryWithBuilder[List] { +object List extends IterableFactory[List] { def fromIterable[B](coll: collection.Iterable[B]): List[B] = coll match { case coll: List[B] => coll case _ => ListBuffer.fromIterable(coll).toList } - def newBuilder[A](): Builder[A, List[A]] = new ListBuffer[A].mapResult(_.toList) + override def newBuilder[A](): Builder[A, List[A]] = new ListBuffer[A].mapResult(_.toList) def empty[A]: List[A] = Nil } 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..198d6fb7b3 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) @@ -112,5 +114,4 @@ object TreeSet extends SortedIterableFactory[TreeSet] { case ts: TreeSet[E] => ts case _ => empty[E] ++ it } - } diff --git a/src/main/scala/strawman/collection/mutable/ArrayBuffer.scala b/src/main/scala/strawman/collection/mutable/ArrayBuffer.scala index 4c759959e9..5df7da3275 100644 --- a/src/main/scala/strawman/collection/mutable/ArrayBuffer.scala +++ b/src/main/scala/strawman/collection/mutable/ArrayBuffer.scala @@ -124,7 +124,7 @@ class ArrayBuffer[A] private (initElems: Array[AnyRef], initLength: Int) override def className = "ArrayBuffer" } -object ArrayBuffer extends IterableFactoryWithBuilder[ArrayBuffer] { +object ArrayBuffer extends IterableFactory[ArrayBuffer] { /** Avoid reallocation of buffer if length is known. */ def fromIterable[B](coll: collection.Iterable[B]): ArrayBuffer[B] = @@ -136,7 +136,7 @@ object ArrayBuffer extends IterableFactoryWithBuilder[ArrayBuffer] { } else new ArrayBuffer[B] ++= coll - def newBuilder[A](): Builder[A, ArrayBuffer[A]] = new ArrayBuffer[A]() + override def newBuilder[A](): Builder[A, ArrayBuffer[A]] = new ArrayBuffer[A]() def empty[A]: ArrayBuffer[A] = new ArrayBuffer[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..f8b1390680 100644 --- a/src/main/scala/strawman/collection/mutable/Builder.scala +++ b/src/main/scala/strawman/collection/mutable/Builder.scala @@ -1,9 +1,12 @@ 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 scala.math.Ordering + /** Base trait for collection builders */ trait Builder[-A, +To] extends Growable[A] { self => 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/HashSet.scala b/src/main/scala/strawman/collection/mutable/HashSet.scala index 8d79668a80..10e871d755 100644 --- a/src/main/scala/strawman/collection/mutable/HashSet.scala +++ b/src/main/scala/strawman/collection/mutable/HashSet.scala @@ -75,12 +75,12 @@ final class HashSet[A](contents: FlatHashTable.Contents[A]) } -object HashSet extends IterableFactoryWithBuilder[HashSet] { +object HashSet extends IterableFactory[HashSet] { def fromIterable[B](it: strawman.collection.Iterable[B]): HashSet[B] = Growable.fromIterable(empty[B], it) def empty[A]: HashSet[A] = new HashSet[A] - def newBuilder[A](): Builder[A, HashSet[A]] = new GrowableBuilder[A, HashSet[A]](empty) + override def newBuilder[A](): Builder[A, HashSet[A]] = new GrowableBuilder[A, HashSet[A]](empty) } diff --git a/src/main/scala/strawman/collection/mutable/ListBuffer.scala b/src/main/scala/strawman/collection/mutable/ListBuffer.scala index 2408549c73..0a25694786 100644 --- a/src/main/scala/strawman/collection/mutable/ListBuffer.scala +++ b/src/main/scala/strawman/collection/mutable/ListBuffer.scala @@ -200,11 +200,11 @@ class ListBuffer[A] override def className = "ListBuffer" } -object ListBuffer extends IterableFactoryWithBuilder[ListBuffer] { +object ListBuffer extends IterableFactory[ListBuffer] { def fromIterable[A](coll: collection.Iterable[A]): ListBuffer[A] = new ListBuffer[A] ++= coll - def newBuilder[A](): Builder[A, ListBuffer[A]] = new ListBuffer[A] + override def newBuilder[A](): Builder[A, ListBuffer[A]] = new ListBuffer[A] def empty[A]: ListBuffer[A] = new ListBuffer[A] } 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..f8b5691265 100644 --- a/src/main/scala/strawman/collection/mutable/TreeSet.scala +++ b/src/main/scala/strawman/collection/mutable/TreeSet.scala @@ -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 @@ -180,5 +182,4 @@ object TreeSet extends SortedIterableFactory[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) - } 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..8c983c2fee --- /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: FromSpecificIterable[A, C]) + (implicit parseA: Parse[A]): Parse[C] = { (s: String) => + val parts = s.split("\\|") + parts.foldLeft[Option[Builder[A, C]]](Some(bf.newBuilder)) { (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..7cd8ab2d79 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.{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(xs.iterableFactory.newBuilder[A]())) { + 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(xs.sortedIterableFactory.newBuilder[A]())) { + 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(bf.newBuilder(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(bf.newBuilder(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(bf.newBuilder(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 = bf.newBuilder(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 = bfLeft.newBuilder(coll) + val right = bfRight.newBuilder(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 + } }