Skip to content

Commit b8472d8

Browse files
committed
Updates of strawman
Bring strawman-4 and strawman-5 to feature-parity. Test also strawman-4.
1 parent 8cb73c0 commit b8472d8

File tree

8 files changed

+997
-113
lines changed

8 files changed

+997
-113
lines changed

src/strawman/collections/CollectionStrawMan4.scala

Lines changed: 81 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package strawman.collections
22

33
import Predef.{augmentString => _, wrapString => _, _}
44
import scala.reflect.ClassTag
5+
import annotation.unchecked.uncheckedVariance
6+
import annotation.tailrec
57

68
/** A strawman architecture for new collections. It contains some
79
* example collection classes and methods with the intent to expose
@@ -20,13 +22,6 @@ object CollectionStrawMan4 {
2022
def iterator: Iterator[A]
2123
}
2224

23-
/** Base trait for generic collections */
24-
trait Iterable[+A] extends IterableOnce[A] with FromIterable[Iterable] {
25-
def iterator: Iterator[A]
26-
def view: View[A] = View.fromIterator(iterator)
27-
def knownLength: Int = -1
28-
}
29-
3025
/** Base trait for instances that can construct a collection from an iterable */
3126
trait FromIterable[+C[X] <: Iterable[X]] {
3227
def fromIterable[B](v: Iterable[B]): C[B]
@@ -38,16 +33,27 @@ object CollectionStrawMan4 {
3833
def apply[A](xs: A*): C[A] = fromIterable(View.Elems(xs: _*))
3934
}
4035

36+
/** Base trait for generic collections */
37+
trait Iterable[+A] extends IterableOnce[A] with FromIterable[Iterable] {
38+
def view: View[A] = View.fromIterator(iterator) // view is overridden, cannot be defined in ops
39+
def knownLength: Int = -1
40+
}
41+
4142
/** Base trait for sequence collections */
4243
trait Seq[+A] extends Iterable[A] with FromIterable[Seq] {
4344
def apply(i: Int): A
4445
def length: Int
4546
}
4647

48+
/** Base trait for collection builders */
4749
trait Builder[-A, +To] {
4850
def +=(x: A): this.type
49-
def ++=(xs: IterableOnce[A]): Unit = xs.iterator.foreach(+=)
5051
def result: To
52+
53+
def ++=(xs: IterableOnce[A]): this.type = {
54+
xs.iterator.foreach(+=)
55+
this
56+
}
5157
}
5258

5359
/* ------------ Operations ----------------------------------- */
@@ -134,17 +140,18 @@ object CollectionStrawMan4 {
134140
require(!isEmpty)
135141
if (i == 0) head else tail.apply(i - 1)
136142
}
137-
def :::[B >: A](prefix: List[B]): List[B] =
138-
if (prefix.isEmpty) this
139-
else Cons(prefix.head, prefix.tail ::: this)
140143
def length: Int =
141144
if (isEmpty) 0 else 1 + tail.length
145+
def ++:[B >: A](prefix: List[B]): List[B] =
146+
if (prefix.isEmpty) this
147+
else Cons(prefix.head, prefix.tail ++: this)
142148
}
143149

144-
case class Cons[+A](x: A, xs: List[A]) extends List[A] {
150+
case class Cons[+A](x: A, private[collections] var next: List[A @uncheckedVariance]) // sound because `next` is used only locally
151+
extends List[A] {
145152
def isEmpty = false
146153
def head = x
147-
def tail = xs
154+
def tail = next
148155
}
149156

150157
case object Nil extends List[Nothing] {
@@ -157,20 +164,64 @@ object CollectionStrawMan4 {
157164
def fromIterator[B](it: Iterator[B]): List[B] =
158165
if (it.hasNext) Cons(it.next, fromIterator(it)) else Nil
159166
def fromIterable[B](c: Iterable[B]): List[B] = c match {
160-
case View.Concat(xs, ys: Iterable[B]) =>
161-
fromIterable(xs) ::: fromIterable(ys)
167+
case View.Concat(xs, ys: List[B]) =>
168+
fromIterable(xs) ++: ys
162169
case View.Drop(xs: List[B], n) =>
163-
var i = 0
164-
var ys = xs
165-
while (i < n && !xs.isEmpty) {
166-
ys = ys.tail
167-
i += 1
168-
}
169-
ys
170+
@tailrec def loop(xs: List[B], n: Int): List[B] =
171+
if (n > 0) loop(xs.tail, n - 1) else xs
172+
loop(xs, n)
173+
case c: List[B] => c
170174
case _ => fromIterator(c.iterator)
171175
}
172176
}
173177

178+
/** Concrete collection type: ListBuffer */
179+
class ListBuffer[A] extends Seq[A] with FromIterable[ListBuffer] with Builder[A, List[A]] {
180+
private var first, last: List[A] = Nil
181+
private var aliased = false
182+
def iterator = first.iterator
183+
def fromIterable[B](coll: Iterable[B]) = ListBuffer.fromIterable(coll)
184+
def apply(i: Int) = first.apply(i)
185+
def length = first.length
186+
187+
private def copyElems(): Unit = {
188+
val buf = ListBuffer.fromIterable(result)
189+
first = buf.first
190+
last = buf.last
191+
aliased = false
192+
}
193+
def result = {
194+
aliased = true
195+
first
196+
}
197+
def +=(elem: A) = {
198+
if (aliased) copyElems()
199+
val last1 = Cons(elem, Nil)
200+
last match {
201+
case last: Cons[A] => last.next = last1
202+
case _ => first = last1
203+
}
204+
last = last1
205+
this
206+
}
207+
override def toString: String =
208+
if (first.isEmpty) "ListBuffer()"
209+
else {
210+
val b = new StringBuilder("ListBuffer(").append(first.head)
211+
first.tail.foldLeft(b)(_.append(", ").append(_)).append(")").toString
212+
}
213+
}
214+
215+
object ListBuffer extends IterableFactory[ListBuffer] {
216+
def fromIterable[B](coll: Iterable[B]): ListBuffer[B] = coll match {
217+
case pd @ View.Partitioned(partition: View.Partition[B]) =>
218+
partition.distribute(new ListBuffer[B]())
219+
pd.forced.get.asInstanceOf[ListBuffer[B]]
220+
case _ =>
221+
new ListBuffer[B] ++= coll
222+
}
223+
}
224+
174225
/** Concrete collection type: ArrayBuffer */
175226
class ArrayBuffer[A] private (initElems: Array[AnyRef], initLength: Int)
176227
extends Seq[A] with FromIterable[ArrayBuffer] with Builder[A, ArrayBuffer[A]] {
@@ -234,12 +285,6 @@ object CollectionStrawMan4 {
234285
def apply(n: Int) = elems(start + n).asInstanceOf[A]
235286
}
236287

237-
case class StringView(s: String) extends RandomAccessView[Char] {
238-
val start = 0
239-
val end = s.length
240-
def apply(n: Int) = s.charAt(n)
241-
}
242-
243288
/** Concrete collection type: String */
244289
implicit class StringOps(val s: String) extends AnyVal with Ops[Char] {
245290
def iterator: Iterator[Char] = new StringView(s).iterator
@@ -277,6 +322,12 @@ object CollectionStrawMan4 {
277322
def ++(xs: String): String = s + xs
278323
}
279324

325+
case class StringView(s: String) extends RandomAccessView[Char] {
326+
val start = 0
327+
val end = s.length
328+
def apply(n: Int) = s.charAt(n)
329+
}
330+
280331
/* ------------ Views --------------------------------------- */
281332

282333
/** A lazy iterable */
@@ -322,6 +373,8 @@ object CollectionStrawMan4 {
322373
}
323374
case class Partition[A](val underlying: Iterable[A], p: A => Boolean) {
324375
val left, right = Partitioned(this)
376+
// `distribute` makes up for the lack of generic push-based functionality.
377+
// It forces both halves of the partition with a given builder.
325378
def distribute(bf: => Builder[A, Iterable[A]]) = {
326379
val lb, rb = bf
327380
val it = underlying.iterator

src/strawman/collections/CollectionStrawMan5.scala

Lines changed: 49 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package strawman.collections
33
import Predef.{augmentString => _, wrapString => _, _}
44
import scala.reflect.ClassTag
55
import annotation.unchecked.uncheckedVariance
6+
import annotation.tailrec
67

78
/** A strawman architecture for new collections. It contains some
89
* example collection classes and methods with the intent to expose
@@ -32,24 +33,8 @@ object CollectionStrawMan5 {
3233
def apply[A](xs: A*): C[A] = fromIterable(View.Elems(xs: _*))
3334
}
3435

35-
/** Base trait for Iterable operations */
36-
trait IterableLike[+A, +C[X] <: Iterable[X]]
37-
extends FromIterable[C]
38-
with IterableOps[A]
39-
with IterableMonoTransforms[A @uncheckedVariance, C[A @uncheckedVariance]]
40-
with IterablePolyTransforms[A @uncheckedVariance, C] {
41-
protected[this] def fromLikeIterable(coll: Iterable[A]): C[A] = fromIterable(coll)
42-
}
43-
44-
/** Base trait for Seq operations */
45-
trait SeqLike[+A, +C[X] <: Seq[X]]
46-
extends IterableLike[A, C] with SeqMonoTransforms[A @uncheckedVariance, C[A @uncheckedVariance]]
47-
4836
/** Base trait for generic collections */
4937
trait Iterable[+A] extends IterableOnce[A] with IterableLike[A, Iterable] {
50-
override def iterator: Iterator[A]
51-
override def fromIterable[B](it: Iterable[B]): Iterable[B]
52-
5338
protected def coll: Iterable[A] = this
5439
def knownLength: Int = -1
5540
}
@@ -58,18 +43,21 @@ object CollectionStrawMan5 {
5843
trait Seq[+A] extends Iterable[A] with SeqLike[A, Seq] {
5944
def apply(i: Int): A
6045
def length: Int
61-
override def iterator: Iterator[A]
6246
}
6347

48+
/** Base trait for strict collections */
6449
trait Buildable[+A, +To <: Iterable[A]] extends Iterable[A] {
6550
protected[this] def newBuilder: Builder[A, To]
6651
override def partition(p: A => Boolean): (To, To) = {
6752
val l, r = newBuilder
6853
iterator.foreach(x => (if (p(x)) l else r) += x)
6954
(l.result, r.result)
7055
}
56+
// one might also override other transforms here to avoid generating
57+
// iterators if it helps efficiency.
7158
}
7259

60+
/** Base trait for collection builders */
7361
trait Builder[-A, +To] {
7462
def +=(x: A): this.type
7563
def result: To
@@ -82,6 +70,29 @@ object CollectionStrawMan5 {
8270

8371
/* ------------ Operations ----------------------------------- */
8472

73+
/** Base trait for Iterable operations
74+
*
75+
* VarianceNote
76+
* ============
77+
*
78+
* We require that for all child classes of Iterable the variance of
79+
* the child class and the variance of the `C` parameter passed to `IterableLike`
80+
* are the same. We cannot express this since we lack variance polymorphism. That's
81+
* why we have to resort at some places to write `C[A @uncheckedVariance]`.
82+
*
83+
*/
84+
trait IterableLike[+A, +C[X] <: Iterable[X]]
85+
extends FromIterable[C]
86+
with IterableOps[A]
87+
with IterableMonoTransforms[A, C[A @uncheckedVariance]] // sound bcs of VarianceNote
88+
with IterablePolyTransforms[A, C] {
89+
protected[this] def fromLikeIterable(coll: Iterable[A]): C[A] = fromIterable(coll)
90+
}
91+
92+
/** Base trait for Seq operations */
93+
trait SeqLike[+A, +C[X] <: Seq[X]]
94+
extends IterableLike[A, C] with SeqMonoTransforms[A, C[A @uncheckedVariance]] // sound bcs of VarianceNote
95+
8596
trait IterableOps[+A] extends Any {
8697
def iterator: Iterator[A]
8798
def foreach(f: A => Unit): Unit = iterator.foreach(f)
@@ -93,16 +104,19 @@ object CollectionStrawMan5 {
93104
def view: View[A] = View.fromIterator(iterator)
94105
}
95106

96-
trait IterableMonoTransforms[A, +Repr] extends Any {
107+
trait IterableMonoTransforms[+A, +Repr] extends Any {
97108
protected def coll: Iterable[A]
98-
protected def fromLikeIterable(coll: Iterable[A]): Repr
109+
protected[this] def fromLikeIterable(coll: Iterable[A]): Repr
99110
def filter(p: A => Boolean): Repr = fromLikeIterable(View.Filter(coll, p))
100111
def partition(p: A => Boolean): (Repr, Repr) = {
101112
val pn = View.Partition(coll, p)
102113
(fromLikeIterable(pn.left), fromLikeIterable(pn.right))
103114
}
104115
def drop(n: Int): Repr = fromLikeIterable(View.Drop(coll, n))
105-
def to[C[X] <: Iterable[X]](fi: FromIterable[C]): C[A] = fi.fromIterable(coll)
116+
def to[C[X] <: Iterable[X]](fi: FromIterable[C]): C[A @uncheckedVariance] =
117+
// variance seems sound because `to` could just as well have been added
118+
// as a decorator. We should investigate this further to be sure.
119+
fi.fromIterable(coll)
106120
}
107121

108122
trait IterablePolyTransforms[+A, +C[A]] extends Any {
@@ -112,9 +126,10 @@ object CollectionStrawMan5 {
112126
def flatMap[B](f: A => IterableOnce[B]): C[B] = fromIterable(View.FlatMap(coll, f))
113127
def ++[B >: A](xs: IterableOnce[B]): C[B] = fromIterable(View.Concat(coll, xs))
114128
def zip[B](xs: IterableOnce[B]): C[(A @uncheckedVariance, B)] = fromIterable(View.Zip(coll, xs))
129+
// sound bcs of VarianceNote
115130
}
116131

117-
trait SeqMonoTransforms[A, +Repr] extends Any with IterableMonoTransforms[A, Repr] {
132+
trait SeqMonoTransforms[+A, +Repr] extends Any with IterableMonoTransforms[A, Repr] {
118133
def reverse: Repr = {
119134
var xs: List[A] = Nil
120135
var it = coll.iterator
@@ -150,10 +165,12 @@ object CollectionStrawMan5 {
150165
case xs: List[B] => this ++: xs
151166
case _ => super.++(xs)
152167
}
153-
override def reverse = super.reverse
168+
@tailrec final override def drop(n: Int) =
169+
if (n > 0) tail.drop(n - 1) else this
154170
}
155171

156-
case class Cons[+A](x: A, private[collections] var next: List[A @uncheckedVariance]) extends List[A] {
172+
case class Cons[+A](x: A, private[collections] var next: List[A @uncheckedVariance]) // sound because `next` is used only locally
173+
extends List[A] {
157174
override def isEmpty = false
158175
override def head = x
159176
def tail = next
@@ -176,11 +193,7 @@ object CollectionStrawMan5 {
176193
class ListBuffer[A] extends Seq[A] with SeqLike[A, ListBuffer] with Builder[A, List[A]] {
177194
private var first, last: List[A] = Nil
178195
private var aliased = false
179-
def iterator = new Iterator[A] {
180-
var current: List[A] = first
181-
def hasNext = ???
182-
def next = ???
183-
}
196+
def iterator = first.iterator
184197
def fromIterable[B](coll: Iterable[B]) = ListBuffer.fromIterable(coll)
185198
def apply(i: Int) = first.apply(i)
186199
def length = first.length
@@ -205,6 +218,12 @@ object CollectionStrawMan5 {
205218
last = last1
206219
this
207220
}
221+
override def toString: String =
222+
if (first.isEmpty) "ListBuffer()"
223+
else {
224+
val b = new StringBuilder("ListBuffer(").append(first.head)
225+
first.tail.foldLeft(b)(_.append(", ").append(_)).append(")").toString
226+
}
208227
}
209228

210229
object ListBuffer extends IterableFactory[ListBuffer] {
@@ -291,12 +310,12 @@ object CollectionStrawMan5 {
291310
def fromIterable[B](coll: Iterable[B]): List[B] = List.fromIterable(coll)
292311
def map(f: Char => Char): String = {
293312
val sb = new StringBuilder
294-
for (ch <- StringOps(s)) sb.append(f(ch))
313+
for (ch <- s) sb.append(f(ch))
295314
sb.toString
296315
}
297316
def flatMap(f: Char => String): String = {
298317
val sb = new StringBuilder
299-
for (ch <- StringOps(s)) sb.append(f(ch))
318+
for (ch <- s) sb.append(f(ch))
300319
sb.toString
301320
}
302321
def ++(xs: IterableOnce[Char]): String = {
@@ -393,15 +412,6 @@ object CollectionStrawMan5 {
393412
-1
394413
}
395414
}
396-
case class Reverse[A](underlying: Iterable[A]) extends View[A] {
397-
def iterator = {
398-
var xs: List[A] = Nil
399-
val it = underlying.iterator
400-
while (it.hasNext) xs = Cons(it.next(), xs)
401-
xs.iterator
402-
}
403-
override def knownLength = underlying.knownLength
404-
}
405415
}
406416

407417
/* ---------- Iterators ---------------------------------------------------*/

0 commit comments

Comments
 (0)