Skip to content

Commit 3bcbcae

Browse files
author
Martijn Hoekstra
committed
to(Seq) doesn't evaluate collection when called on a Seq
1 parent 6abe710 commit 3bcbcae

File tree

3 files changed

+40
-0
lines changed

3 files changed

+40
-0
lines changed

compat/src/main/scala-2.11_2.12/scala/collection/compat/CompatImpl.scala

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,40 @@ import scala.collection.generic.CanBuildFrom
1616
import scala.collection.mutable.Builder
1717
import scala.collection.{immutable => i, mutable => m}
1818

19+
/* builder optimized for a single ++= call, which returns identity on result if possible
20+
* and defers to the underlying builder if not.
21+
*/
22+
private final class IdentityPreservingSeqBuilder[A, C <: Seq[A]](that: Builder[A, C])
23+
extends Builder[A, Seq[A]] {
24+
var collection: Seq[A] = null
25+
var ruined = false
26+
27+
final override def ++=(elems: TraversableOnce[A]): this.type =
28+
if(!ruined && collection == null && elems.isInstanceOf[Seq[_]]) {
29+
collection = elems.asInstanceOf[Seq[A]]
30+
this
31+
}
32+
else {
33+
ruined = true
34+
if (collection != null) that ++= collection
35+
that ++= elems
36+
collection = null
37+
this
38+
}
39+
40+
final def +=(elem: A): this.type = {
41+
collection = null
42+
ruined = true
43+
that += elem
44+
this
45+
}
46+
final def clear(): Unit = {
47+
collection = null
48+
if (ruined) that.clear()
49+
}
50+
final def result(): Seq[A] = if(ruined) that.result() else if (collection eq null) Nil else collection
51+
}
52+
1953
private[compat] object CompatImpl {
2054
def simpleCBF[A, C](f: => Builder[A, C]): CanBuildFrom[Any, A, C] = new CanBuildFrom[Any, A, C] {
2155
def apply(from: Any): Builder[A, C] = apply()

compat/src/main/scala-2.11_2.12/scala/collection/compat/PackageShared.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ private[compat] trait PackageShared {
4444
def newBuilder: m.Builder[A, C] = factory()
4545
}
4646

47+
implicit def SeqCBF[A](seq: Seq.type): CanBuildFrom[Any, A, Seq[A]] = simpleCBF(new IdentityPreservingSeqBuilder[A, Seq[A]](Seq.newBuilder[A]))
48+
4749
implicit def genericCompanionToCBF[A, CC[X] <: GenTraversable[X]](
4850
fact: GenericCompanion[CC]): CanBuildFrom[Any, A, CC[A]] =
4951
simpleCBF(fact.newBuilder[A])

compat/src/test/scala/test/scala/collection/CollectionTest.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ class CollectionTest {
4040
//val mT: Map[Int, String] = m
4141
assertEquals(Map(1 -> "a", 2 -> "b"), m)
4242
assertTrue(m.isInstanceOf[Map[_, _]])
43+
44+
// Stream.to(Seq) doesn't evaluate the stream
45+
val strm = 1 #:: {throw new Exception("not lazy")} #:: Stream.empty[Int]
46+
val strmsq: Seq[Int] = strm.to(Seq)
4347
}
4448

4549
@Test

0 commit comments

Comments
 (0)