Skip to content
This repository was archived by the owner on Dec 22, 2021. It is now read-only.

Commit c8434cc

Browse files
committed
Introduce factories with builders.
Optimize optionSequence (in TraverseTest) when the underlying IterableFactory happens to be an IterableFactoryWithBuilder
1 parent b0c61ac commit c8434cc

File tree

9 files changed

+53
-62
lines changed

9 files changed

+53
-62
lines changed

src/main/scala/strawman/collection/Factories.scala

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package strawman
22
package collection
33

4-
import scala.{Any, Int, Ordering, Nothing}
4+
import strawman.collection.mutable.Builder
5+
6+
import scala.{Any, Int, Nothing, Ordering}
57
import scala.annotation.unchecked.uncheckedVariance
68

79
/**
@@ -26,6 +28,10 @@ trait IterableFactory[+CC[_]] {
2628

2729
}
2830

31+
trait IterableFactoryWithBuilder[+CC[_]] extends IterableFactory[CC] {
32+
def newBuilder[A](): Builder[A, CC[A]]
33+
}
34+
2935
object IterableFactory {
3036
import scala.language.implicitConversions
3137

src/main/scala/strawman/collection/SortedSet.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ trait SortedSetOps[A, +CC[X], +C <: SortedSet[A]]
1313

1414
protected[this] def sortedFromIterable[B: Ordering](it: Iterable[B]): CC[B]
1515

16+
def iterableFactory: IterableFactory[Set]
17+
1618
def firstKey: A = head
1719
def lastKey: A = last
1820

src/main/scala/strawman/collection/immutable/LazyList.scala

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,5 @@ object LazyList extends IterableFactory[LazyList] {
6565
def fromIterator[A](it: Iterator[A]): LazyList[A] =
6666
new LazyList(if (it.hasNext) Some(it.next(), fromIterator(it)) else None)
6767

68-
def newBuilder[A]: Builder[A, LazyList[A]] = ???
69-
7068
def empty[A]: LazyList[A] = new LazyList[A](None)
7169
}

src/main/scala/strawman/collection/immutable/List.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,14 +55,14 @@ case object Nil extends List[Nothing] {
5555
override def tail: Nothing = throw new UnsupportedOperationException("tail of empty list")
5656
}
5757

58-
object List extends IterableFactory[List] {
58+
object List extends IterableFactoryWithBuilder[List] {
5959

6060
def fromIterable[B](coll: collection.Iterable[B]): List[B] = coll match {
6161
case coll: List[B] => coll
6262
case _ => ListBuffer.fromIterable(coll).toList
6363
}
6464

65-
def newBuilder[A]: Builder[A, List[A]] = new ListBuffer[A].mapResult(_.toList)
65+
def newBuilder[A](): Builder[A, List[A]] = new ListBuffer[A].mapResult(_.toList)
6666

6767
def empty[A]: List[A] = Nil
6868
}

src/main/scala/strawman/collection/immutable/SortedMap.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,21 @@ trait SortedMapOps[K, +V, +CC[X, +Y] <: SortedMap[X, Y] with SortedMapOps[X, Y,
1313
extends MapOps[K, V, Map, C]
1414
with collection.SortedMapOps[K, V, CC, C] {
1515

16+
protected[this] def coll: CC[K, V]
17+
1618
protected def mapFromIterable[K2, V2](it: collection.Iterable[(K2, V2)]): Map[K2, V2] =
1719
Map.fromIterable(it)
1820

1921
// We override these methods to fix their return type (which would be `Map` otherwise)
2022
def updated[V1 >: V](key: K, value: V1): CC[K, V1]
2123
override def + [V1 >: V](kv: (K, V1)): CC[K, V1] = updated(kv._1, kv._2)
2224

25+
override def concat[V2 >: V](xs: collection.Iterable[(K, V2)]): CC[K, V2] = {
26+
var result: CC[K, V2] = coll
27+
val it = xs.iterator()
28+
while (it.hasNext) result = result + it.next()
29+
result
30+
}
31+
2332
}
2433

src/main/scala/strawman/collection/mutable/ArrayBuffer.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ class ArrayBuffer[A] private (initElems: Array[AnyRef], initLength: Int)
124124
override def className = "ArrayBuffer"
125125
}
126126

127-
object ArrayBuffer extends IterableFactory[ArrayBuffer] {
127+
object ArrayBuffer extends IterableFactoryWithBuilder[ArrayBuffer] {
128128

129129
/** Avoid reallocation of buffer if length is known. */
130130
def fromIterable[B](coll: collection.Iterable[B]): ArrayBuffer[B] =
@@ -136,9 +136,9 @@ object ArrayBuffer extends IterableFactory[ArrayBuffer] {
136136
}
137137
else new ArrayBuffer[B] ++= coll
138138

139-
def newBuilder[A]: Builder[A, ArrayBuffer[A]] = new ArrayBuffer[A]()
139+
def newBuilder[A](): Builder[A, ArrayBuffer[A]] = new ArrayBuffer[A]()
140140

141-
def empty[A <: Any]: ArrayBuffer[A] = new ArrayBuffer[A]()
141+
def empty[A]: ArrayBuffer[A] = new ArrayBuffer[A]()
142142
}
143143

144144
class ArrayBufferView[A](val array: Array[AnyRef], val length: Int) extends IndexedView[A] {

src/main/scala/strawman/collection/mutable/HashSet.scala

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ final class HashSet[A](contents: FlatHashTable.Contents[A])
3535

3636
protected[this] def fromSpecificIterable(coll: collection.Iterable[A]): HashSet[A] = fromIterable(coll)
3737

38-
protected[this] def newBuilder: Builder[A, HashSet[A]] = new GrowableBuilder[A, HashSet[A]](empty)
38+
protected[this] def newBuilder: Builder[A, HashSet[A]] = HashSet.newBuilder[A]()
3939

4040
def add(elem: A): this.type = {
4141
table.addElem(elem)
@@ -75,9 +75,12 @@ final class HashSet[A](contents: FlatHashTable.Contents[A])
7575

7676
}
7777

78-
object HashSet extends IterableFactory[HashSet] {
78+
object HashSet extends IterableFactoryWithBuilder[HashSet] {
7979

8080
def fromIterable[B](it: strawman.collection.Iterable[B]): HashSet[B] = Growable.fromIterable(empty[B], it)
8181

8282
def empty[A]: HashSet[A] = new HashSet[A]
83+
84+
def newBuilder[A](): Builder[A, HashSet[A]] = new GrowableBuilder[A, HashSet[A]](empty)
85+
8386
}

src/main/scala/strawman/collection/mutable/ListBuffer.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,11 +200,11 @@ class ListBuffer[A]
200200
override def className = "ListBuffer"
201201
}
202202

203-
object ListBuffer extends IterableFactory[ListBuffer] {
203+
object ListBuffer extends IterableFactoryWithBuilder[ListBuffer] {
204204

205205
def fromIterable[A](coll: collection.Iterable[A]): ListBuffer[A] = new ListBuffer[A] ++= coll
206206

207-
def newBuilder[A]: Builder[A, ListBuffer[A]] = new ListBuffer[A]
207+
def newBuilder[A](): Builder[A, ListBuffer[A]] = new ListBuffer[A]
208208

209209
def empty[A]: ListBuffer[A] = new ListBuffer[A]
210210
}

src/test/scala/strawman/collection/test/TraverseTest.scala

Lines changed: 23 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ package collection.test
44

55
import org.junit.Test
66
import strawman.collection.Iterable
7-
import strawman.collection.mutable.{Builder, ArrayBuffer}
7+
import strawman.collection.mutable.{ArrayBuffer, Builder, Growable}
88
import strawman.collection._
99

1010
import scala.{Any, Either, Int, Left, None, Option, Right, Some, Unit}
@@ -14,67 +14,40 @@ import java.lang.String
1414

1515
class TraverseTest {
1616

17-
// You can either overload methods for PolyBuildable and OrderedPolyBuildable (if you want to support ordered collection types)
18-
def optionSequence1[C[X] <: Iterable[X] with IterableOps[X, C, _], A](xs: C[Option[A]]): Option[C[A]] =
19-
xs.foldLeft[Option[ArrayBuffer[A]]](Some(new ArrayBuffer[A])) { (bo, xo) =>
17+
def optionSequence[CC[X] <: Iterable[X] with IterableOps[X, CC, _], A](xs: CC[Option[A]]): Option[CC[A]] = {
18+
def folder[F[X] <: Growable[X]]: (Option[F[A]], Option[A]) => Option[F[A]] = { (bo, xo) =>
2019
(bo, xo) match {
2120
case (Some(builder), Some(a)) => Some(builder += a)
2221
case _ => None
2322
}
24-
}.map(_.to(xs.iterableFactory))
25-
26-
// def optionSequence1[CC[_], A](xs: OrderedPolyBuildable[Option[A], CC] with Iterable[Option[A]])(implicit ev: Ordering[A]): Option[CC[A]] =
27-
// xs.foldLeft[Option[Builder[A, CC[A]]]](Some(xs.newOrderedBuilder)) {
28-
// case (Some(builder), Some(a)) => Some(builder += a)
29-
// case _ => None
30-
// }.map(_.result)
31-
//
32-
// // ...or use BuildFrom to abstract over both and also allow building arbitrary collection types
33-
// def optionSequence[CC[X] <: Iterable[X], A](xs: CC[Option[A]])(implicit bf: BuildFrom[CC[Option[A]], A]): Option[bf.To] =
34-
// xs.foldLeft[Option[Builder[A, bf.To]]](Some(bf.newBuilder(xs))) {
35-
// case (Some(builder), Some(a)) => Some(builder += a)
36-
// case _ => None
37-
// }.map(_.result)
38-
//
39-
// def eitherSequence[C[X] <: Iterable[X], A, B](xs: C[Either[A, B]])(implicit bf: BuildFrom[xs.type, B]): Either[A, bf.To] =
40-
// xs.foldLeft[Either[A, Builder[B, bf.To]]](Right(bf.newBuilder(xs))) {
41-
// case (Right(builder), Right(b)) => Right(builder += b)
42-
// case (Left(a) , _) => Left(a)
43-
// case (_ , Left(a)) => Left(a)
44-
// }.right.map(_.result)
23+
}
24+
val factory = xs.iterableFactory
25+
factory match {
26+
case iterableBuilder: IterableFactoryWithBuilder[CC] =>
27+
xs.foldLeft[Option[Builder[A, CC[A]]]](
28+
Some(iterableBuilder.newBuilder[A]())
29+
)(
30+
folder[({ type l[X] = Builder[X, CC[X]] })#l]
31+
).map(_.result)
32+
case _ =>
33+
xs.foldLeft[Option[ArrayBuffer[A]]](Some(new ArrayBuffer[A]))(folder).map(_.to(xs.iterableFactory))
34+
}
35+
}
4536

4637
@Test
4738
def optionSequence1Test: Unit = {
4839
val xs1 = immutable.List(Some(1), None, Some(2))
49-
val o1 = optionSequence1(xs1)
40+
val o1 = optionSequence(xs1)
5041
val o1t: Option[immutable.List[Int]] = o1
5142

5243
val xs2: immutable.Set[Option[String]] = immutable.TreeSet(Some("foo"), Some("bar"), None)
53-
val o2 = optionSequence1(xs2)
44+
val o2 = optionSequence(xs2)
5445
val o2t: Option[immutable.Set[String]] = o2
55-
}
5646

57-
// def optionSequenceTest: Unit = {
58-
// val xs1 = immutable.List(Some(1), None, Some(2))
59-
// val o1 = optionSequence(xs1)
60-
// val o1t: Option[immutable.List[Int]] = o1
61-
//
62-
// val xs2 = immutable.TreeSet(Some("foo"), Some("bar"), None)
63-
// val o2 = optionSequence(xs2)
64-
// val o2t: Option[immutable.TreeSet[String]] = o2
65-
//
66-
// // Breakout-like use case from https://github.com/scala/scala/pull/5233:
67-
// val xs4 = immutable.List[Option[(Int, String)]](Some((1 -> "a")), Some((2 -> "b")))
68-
// val o4 = optionSequence(xs4)(immutable.TreeMap) // same syntax as in `.to`
69-
// val o4t: Option[immutable.TreeMap[Int, String]] = o4
70-
// }
71-
//
72-
// @Test
73-
// def eitherSequenceTest: Unit = {
74-
// val xs3 = mutable.ListBuffer(Right("foo"), Left(0), Right("bar"))
75-
// val xs3t: mutable.ListBuffer[Either[Int, String]] = xs3
76-
// val e1 = eitherSequence(xs3)
77-
// val e1t: Either[Int, mutable.ListBuffer[String]] = e1
78-
// }
47+
val xs4 = immutable.List[Option[(Int, String)]](Some((1 -> "a")), Some((2 -> "b")))
48+
val o4 = optionSequence(xs4)
49+
val o4t: Option[immutable.List[(Int, String)]] = o4
50+
val o5: Option[immutable.TreeMap[Int, String]] = o4.map(_.to(immutable.TreeMap))
51+
}
7952

8053
}

0 commit comments

Comments
 (0)