Skip to content

Commit 720b52d

Browse files
committed
Retrofit BuildFrom into the new design
`BuildFrom` is like `FromSpecificIterable` but with an extra `From` argument, like in the final version of scala#45. `FromSpecificIterable` existed conceptually in that version as `BuildFrom[Any, …]` but didn’t have a separate type. This new version has separate abstractions for buildable (strict) collection types in the form of `StrictBuildFrom` and `FromSpecificIterableWithBuilder`. Since we can get a surrogate builder (through one of the new `Builder.from` methods) for any lazy collection and we can restrict code to work only with strict collections via the `Buildable` trait, this is not strictly necessary. The only reason for separating the `Builder` abstractions is to avoid exposing them in `FromSpecificIterable`. Even though everything can be built in a strict way, these abstractions sit on top of the lazy ones, not below them.
1 parent 93ea768 commit 720b52d

File tree

11 files changed

+216
-51
lines changed

11 files changed

+216
-51
lines changed

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

Lines changed: 94 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,76 @@
11
package strawman
22
package collection
33

4-
import strawman.collection.mutable.Builder
4+
import scala.language.implicitConversions
5+
6+
import strawman.collection.mutable.{ArrayBuffer, Builder}
57

68
import scala.{Any, Int, Nothing, Ordering}
79
import scala.annotation.unchecked.uncheckedVariance
810

11+
12+
/** Builds a collection of type `C` from elements of type `A` when a source collection of type `From` is available.
13+
* Implicit instances of `BuildFrom` are available for all collection types.
14+
*
15+
* @tparam From Type of source collection
16+
* @tparam A Type of elements (e.g. `Int`, `Boolean`, etc.)
17+
* @tparam C Type of collection (e.g. `List[Int]`, `TreeMap[Int, String]`, etc.)
18+
*/
19+
trait BuildFrom[-From, -A, +C] extends Any {
20+
def fromSpecificIterable(from: From)(it: Iterable[A]): C
21+
}
22+
23+
object BuildFrom {
24+
/** Build the source collection type from an IterableOps */
25+
implicit def buildFromIterableOps[C[X] <: IterableOps[X, C, _], A, E]: BuildFrom[C[A], E, C[E]] = new BuildFrom[C[A], E, C[E]] {
26+
//TODO: Reuse a prototype instance
27+
def fromSpecificIterable(from: C[A])(it: Iterable[E]): C[E] = from.iterableFactory.fromIterable(it)
28+
}
29+
30+
/** Build the source collection type from an Iterable with SortedOps */
31+
implicit def buildFromSortedOps[C[X] <: Iterable[X] with SortedOps[X, C[X], C], A, E : Ordering]: BuildFrom[C[A], E, C[E]] = new BuildFrom[C[A], E, C[E]] {
32+
def fromSpecificIterable(from: C[A])(it: Iterable[E]): C[E] = from.sortedIterableFactory.fromSpecificIterable(it)
33+
}
34+
}
35+
36+
/** A more specific `BuildFrom` for strict target collection types which can provide a `Builder`.
37+
* Note that a `Builder` can be obtained for any `BuildFrom` via `Builder.from`.
38+
*/
39+
trait StrictBuildFrom[-From, -A, +C] extends Any with BuildFrom[From, A, C] {
40+
def newBuilder(from: From): Builder[A, C]
41+
}
42+
43+
object StrictBuildFrom {
44+
/** Build the source collection type from a strict IterableOps */
45+
implicit def strictBuildFromIterableOps[C[X] <: IterableOps[X, C, _] with Buildable[X, C[X]], A, E]: StrictBuildFrom[C[A], E, C[E]] = new StrictBuildFrom[C[A], E, C[E]] {
46+
//TODO: Reuse a prototype instance
47+
def newBuilder(from: C[A]): Builder[E, C[E]] = from.iterableFactory.asInstanceOf[IterableFactoryWithBuilder[C]].newBuilder[E]()
48+
def fromSpecificIterable(from: C[A])(it: Iterable[E]): C[E] = from.iterableFactory.fromIterable(it)
49+
}
50+
51+
/** Build the source collection type from a strict Iterable with SortedOps */
52+
implicit def strictBuildFromSortedOps[C[X] <: Iterable[X] with SortedOps[X, C[X], C] with Buildable[X, C[X]], A, E : Ordering]: StrictBuildFrom[C[A], E, C[E]] = new StrictBuildFrom[C[A], E, C[E]] {
53+
def newBuilder(from: C[A]): Builder[E, C[E]] = from.sortedIterableFactory.asInstanceOf[SortedIterableFactoryWithBuilder[C]].newBuilder[E]()
54+
def fromSpecificIterable(from: C[A])(it: Iterable[E]): C[E] = from.sortedIterableFactory.fromSpecificIterable(it)
55+
}
56+
}
57+
958
/**
1059
* Builds a collection of type `C` from elements of type `A`
1160
* @tparam A Type of elements (e.g. `Int`, `Boolean`, etc.)
1261
* @tparam C Type of collection (e.g. `List[Int]`, `TreeMap[Int, String]`, etc.)
1362
*/
14-
trait FromSpecificIterable[-A, +C] extends Any {
63+
trait FromSpecificIterable[-A, +C] extends Any with BuildFrom[Any, A, C] {
64+
def fromSpecificIterable(from: Any)(it: Iterable[A]): C = fromSpecificIterable(it)
1565
def fromSpecificIterable(it: Iterable[A]): C
1666
}
1767

68+
/** A more specific `FromSpecificIterable` for strict collection types which can provide a `Builder`. */
69+
trait FromSpecificIterableWithBuilder[-A, +C] extends Any with FromSpecificIterable[A, C] with StrictBuildFrom[Any, A, C] {
70+
def newBuilder(from: Any): Builder[A, C] = newBuilder
71+
def newBuilder: Builder[A, C]
72+
}
73+
1874
/** Base trait for companion objects of unconstrained collection types */
1975
trait IterableFactory[+CC[_]] {
2076

@@ -28,13 +84,7 @@ trait IterableFactory[+CC[_]] {
2884

2985
}
3086

31-
trait IterableFactoryWithBuilder[+CC[_]] extends IterableFactory[CC] {
32-
def newBuilder[A](): Builder[A, CC[A]]
33-
}
34-
3587
object IterableFactory {
36-
import scala.language.implicitConversions
37-
3888
implicit def toSpecific[A, CC[_]](factory: IterableFactory[CC]): FromSpecificIterable[A, CC[A]] =
3989
new FromSpecificIterable[A, CC[A]] {
4090
def fromSpecificIterable(it: Iterable[A]): CC[A] = factory.fromIterable[A](it)
@@ -44,7 +94,24 @@ object IterableFactory {
4494
def empty[A]: CC[A] = delegate.empty
4595
def fromIterable[E](it: Iterable[E]): CC[E] = delegate.fromIterable(it)
4696
}
97+
}
4798

99+
trait IterableFactoryWithBuilder[+CC[_]] extends IterableFactory[CC] {
100+
def newBuilder[A](): Builder[A, CC[A]]
101+
}
102+
103+
object IterableFactoryWithBuilder {
104+
implicit def toSpecific[A, CC[_]](factory: IterableFactoryWithBuilder[CC]): FromSpecificIterableWithBuilder[A, CC[A]] =
105+
new FromSpecificIterableWithBuilder[A, CC[A]] {
106+
def fromSpecificIterable(it: Iterable[A]): CC[A] = factory.fromIterable[A](it)
107+
def newBuilder: Builder[A, CC[A]] = factory.newBuilder[A]()
108+
}
109+
110+
class Delegate[CC[_]](delegate: IterableFactoryWithBuilder[CC]) extends IterableFactoryWithBuilder[CC] {
111+
def empty[A]: CC[A] = delegate.empty
112+
def fromIterable[E](it: Iterable[E]): CC[E] = delegate.fromIterable(it)
113+
def newBuilder[A](): Builder[A, CC[A]] = delegate.newBuilder[A]()
114+
}
48115
}
49116

50117
trait SpecificIterableFactory[-A, +C] extends FromSpecificIterable[A, C] {
@@ -55,6 +122,8 @@ trait SpecificIterableFactory[-A, +C] extends FromSpecificIterable[A, C] {
55122
def fill(n: Int)(elem: => A): C = fromSpecificIterable(View.Fill(n)(elem))
56123
}
57124

125+
trait SpecificIterableFactoryWithBuilder[-A, +C] extends SpecificIterableFactory[A, C] with FromSpecificIterableWithBuilder[A, C]
126+
58127
/** Factory methods for collections of kind `* −> * -> *` */
59128
trait MapFactory[+CC[X, Y]] {
60129

@@ -65,8 +134,6 @@ trait MapFactory[+CC[X, Y]] {
65134
}
66135

67136
object MapFactory {
68-
import scala.language.implicitConversions
69-
70137
implicit def toSpecific[K, V, CC[X, Y]](factory: MapFactory[CC]): FromSpecificIterable[(K, V), CC[K, V]] =
71138
new FromSpecificIterable[(K, V), CC[K, V]] {
72139
def fromSpecificIterable(it: Iterable[(K, V)]): CC[K, V] = factory.fromIterable[K, V](it)
@@ -76,7 +143,6 @@ object MapFactory {
76143
def fromIterable[K, V](it: Iterable[(K, V)]): C[K, V] = delegate.fromIterable(it)
77144
def empty[K, V]: C[K, V] = delegate.empty
78145
}
79-
80146
}
81147

82148
/** Base trait for companion objects of collections that require an implicit evidence */
@@ -92,8 +158,6 @@ trait SortedIterableFactory[+CC[_]] {
92158
}
93159

94160
object SortedIterableFactory {
95-
import scala.language.implicitConversions
96-
97161
implicit def toSpecific[A: Ordering, CC[_]](factory: SortedIterableFactory[CC]): FromSpecificIterable[A, CC[A]] =
98162
new FromSpecificIterable[A, CC[A]] {
99163
def fromSpecificIterable(it: Iterable[A]): CC[A] = factory.sortedFromIterable[A](it)
@@ -103,7 +167,24 @@ object SortedIterableFactory {
103167
def empty[A : Ordering]: CC[A] = delegate.empty
104168
def sortedFromIterable[E : Ordering](it: Iterable[E]): CC[E] = delegate.sortedFromIterable(it)
105169
}
170+
}
106171

172+
trait SortedIterableFactoryWithBuilder[+CC[_]] extends SortedIterableFactory[CC] {
173+
def newBuilder[A : Ordering](): Builder[A, CC[A]]
174+
}
175+
176+
object SortedIterableFactoryWithBuilder {
177+
implicit def toSpecific[A: Ordering, CC[_]](factory: SortedIterableFactoryWithBuilder[CC]): FromSpecificIterableWithBuilder[A, CC[A]] =
178+
new FromSpecificIterableWithBuilder[A, CC[A]] {
179+
def fromSpecificIterable(it: Iterable[A]): CC[A] = factory.sortedFromIterable[A](it)
180+
def newBuilder: Builder[A, CC[A]] = factory.newBuilder[A]()
181+
}
182+
183+
class Delegate[CC[_]](delegate: SortedIterableFactoryWithBuilder[CC]) extends SortedIterableFactoryWithBuilder[CC] {
184+
def empty[A : Ordering]: CC[A] = delegate.empty
185+
def sortedFromIterable[E : Ordering](it: Iterable[E]): CC[E] = delegate.sortedFromIterable(it)
186+
def newBuilder[A : Ordering](): Builder[A, CC[A]] = delegate.newBuilder[A]()
187+
}
107188
}
108189

109190
/** Factory methods for collections of kind `* −> * -> *` which require an implicit evidence value for the key type */
@@ -118,8 +199,6 @@ trait SortedMapFactory[+CC[X, Y]] {
118199
}
119200

120201
object SortedMapFactory {
121-
import scala.language.implicitConversions
122-
123202
implicit def toSpecific[K : Ordering, V, CC[_, _]](factory: SortedMapFactory[CC]): FromSpecificIterable[(K, V), CC[K, V]] =
124203
new FromSpecificIterable[(K, V), CC[K, V]] {
125204
def fromSpecificIterable(it: Iterable[(K, V)]): CC[K, V] = factory.sortedFromIterable(it)
@@ -129,5 +208,4 @@ object SortedMapFactory {
129208
def empty[K: Ordering, V]: CC[K, V] = delegate.empty[K, V]
130209
def sortedFromIterable[K: Ordering, V](it: Iterable[(K, V)]): CC[K, V] = delegate.sortedFromIterable(it)
131210
}
132-
133211
}

src/main/scala/strawman/collection/Iterable.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,10 @@ trait IterableOps[+A, +CC[X], +C] extends Any {
253253
}
254254

255255
/** Base trait for strict collections that can be built using a builder.
256+
*
257+
* All factories provided by such a collection must be of the `*WithBuilder` variant
258+
* even though this is not formally enforced through types.
259+
*
256260
* @tparam A the element type of the collection
257261
* @tparam C the type of the underlying collection
258262
*/

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ trait SortedMap[K, +V]
1212

1313
trait SortedMapOps[K, +V, +CC[X, Y] <: SortedMap[X, Y] with SortedMapOps[X, Y, CC, _], +C <: SortedMap[K, V]]
1414
extends MapOps[K, V, Map, C]
15-
with SortedOps[K, C] {
15+
with SortedOps[K, C, SortedSet] {
16+
17+
def sortedIterableFactory = SortedSet
1618

1719
protected[this] def sortedMapFromIterable[K2, V2](it: collection.Iterable[(K2, V2)])(implicit ordering: Ordering[K2]): CC[K2, V2]
1820

src/main/scala/strawman/collection/SortedOps.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@ package strawman.collection
33
import scala.{Ordering, Option, Some}
44

55
/** Base trait for sorted collections */
6-
trait SortedOps[A, +C] {
6+
trait SortedOps[A, +C, +CC[_]] {
77

88
implicit def ordering: Ordering[A]
99

10+
def sortedIterableFactory: SortedIterableFactory[CC]
11+
1012
/** Returns the first key of the collection. */
1113
def firstKey: A
1214

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ trait SortedSet[A] extends Set[A] with SortedSetOps[A, SortedSet, SortedSet[A]]
99

1010
trait SortedSetOps[A, +CC[X], +C <: SortedSet[A]]
1111
extends SetOps[A, Set, C]
12-
with SortedOps[A, C] {
12+
with SortedOps[A, C, CC] {
1313

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

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ sealed abstract class BitSet
2727
def empty: BitSet = BitSet.empty
2828

2929
def iterableFactory = Set
30+
def sortedIterableFactory = SortedSet
3031

3132
protected[this] def fromSpecificIterable(coll: collection.Iterable[Int]): BitSet = BitSet.fromSpecificIterable(coll)
3233
protected[this] def sortedFromIterable[B : Ordering](it: collection.Iterable[B]): SortedSet[B] = SortedSet.sortedFromIterable(it)

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package strawman
22
package collection
33
package immutable
44

5-
import mutable.Builder
5+
import mutable.{ArrayBuffer, Builder}
66
import immutable.{RedBlackTree => RB}
77

88
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
6363

6464
def iterableFactory = Set
6565

66+
def sortedIterableFactory = TreeSet
67+
6668
protected[this] def fromSpecificIterable(coll: strawman.collection.Iterable[A]): TreeSet[A] =
6769
TreeSet.sortedFromIterable(coll)
6870

@@ -103,7 +105,7 @@ final class TreeSet[A] private (tree: RB.Tree[A, Unit])(implicit val ordering: O
103105
else newSet(RB.delete(tree, elem))
104106
}
105107

106-
object TreeSet extends SortedIterableFactory[TreeSet] {
108+
object TreeSet extends SortedIterableFactoryWithBuilder[TreeSet] {
107109

108110
def empty[A: Ordering]: TreeSet[A] = new TreeSet[A]
109111

@@ -113,4 +115,5 @@ object TreeSet extends SortedIterableFactory[TreeSet] {
113115
case _ => empty[E] ++ it
114116
}
115117

118+
def newBuilder[A : Ordering](): Builder[A, TreeSet[A]] = new ArrayBuffer[A].mapResult(sortedFromIterable[A] _)
116119
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ class BitSet(protected[collection] final var elems: Array[Long])
3636

3737
def iterableFactory = Set
3838

39+
def sortedIterableFactory = SortedSet
40+
3941
protected[this] def sortedFromIterable[B : Ordering](it: collection.Iterable[B]): collection.mutable.SortedSet[B] =
4042
collection.mutable.SortedSet.sortedFromIterable(it)
4143

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

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package strawman.collection.mutable
22

3-
import scala.{Boolean, Any, Char, Unit}
3+
import scala.{Any, Boolean, Char, Unit}
44
import java.lang.String
5-
import strawman.collection.IterableOnce
5+
6+
import strawman.collection.{BuildFrom, IterableFactory, IterableFactoryWithBuilder, IterableOnce, SortedIterableFactory, StrictBuildFrom}
7+
8+
import scala.math.Ordering
69

710
/** Base trait for collection builders */
811
trait Builder[-A, +To] extends Growable[A] { self =>
@@ -24,6 +27,29 @@ trait Builder[-A, +To] extends Growable[A] { self =>
2427
}
2528
}
2629

30+
object Builder {
31+
/** Get a proper builder for an IterableFactoryWithBuilder, otherwise a Builder that uses an intermediate
32+
* ArrayBuffer to store the elements. */
33+
def from[A, CC[_]](fact: IterableFactory[CC]): Builder[A, CC[A]] = fact match {
34+
case fact: IterableFactoryWithBuilder[CC] => fact.newBuilder[A]()
35+
case fact => new ArrayBuffer[A]().mapResult(fact.fromIterable _)
36+
}
37+
38+
/** Get a proper builder for a SortedIterableFactoryWithBuilder, otherwise a Builder that uses an intermediate
39+
* ArrayBuffer to store the elements. */
40+
def from[A : Ordering, CC[_]](fact: SortedIterableFactory[CC]): Builder[A, CC[A]] = fact match {
41+
case fact: IterableFactoryWithBuilder[CC] => fact.newBuilder[A]()
42+
case fact => new ArrayBuffer[A]().mapResult(fact.sortedFromIterable[A] _)
43+
}
44+
45+
/** Get a proper builder for a StrictBuildFrom, otherwise a Builder that uses an intermediate
46+
* ArrayBuffer to store the elements. */
47+
def from[From, A, C](bf: BuildFrom[From, A, C], from: From): Builder[A, C] = bf match {
48+
case bf: StrictBuildFrom[From, A, C] => bf.newBuilder(from)
49+
case bf => new ArrayBuffer[A]().mapResult(bf.fromSpecificIterable(from) _)
50+
}
51+
}
52+
2753
class StringBuilder extends Builder[Char, String] {
2854
private val sb = new java.lang.StringBuilder
2955

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package strawman
22
package collection.mutable
33

4-
import collection.SortedIterableFactory
4+
import collection.SortedIterableFactoryWithBuilder
55
import collection.mutable.{RedBlackTree => RB}
66

77
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:
4545

4646
def iterableFactory = Set
4747

48+
def sortedIterableFactory = TreeSet
49+
4850
def keysIteratorFrom(start: A): collection.Iterator[A] = RB.keysIterator(tree, Some(start))
4951

5052
def empty: TreeSet[A] = TreeSet.empty
@@ -175,10 +177,11 @@ sealed class TreeSet[A] private (tree: RB.Tree[A, Null])(implicit val ordering:
175177
* @author Lucien Pereira
176178
*
177179
*/
178-
object TreeSet extends SortedIterableFactory[TreeSet] {
180+
object TreeSet extends SortedIterableFactoryWithBuilder[TreeSet] {
179181

180182
def empty[A : Ordering]: TreeSet[A] = new TreeSet[A]()
181183

182184
def sortedFromIterable[E : Ordering](it: collection.Iterable[E]): TreeSet[E] = Growable.fromIterable(empty[E], it)
183185

186+
def newBuilder[A : Ordering](): Builder[A, TreeSet[A]] = new ArrayBuffer[A].mapResult(sortedFromIterable[A] _)
184187
}

0 commit comments

Comments
 (0)