aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Petrashko <dark@d-d.me>2016-04-20 15:29:38 +0200
committerDmitry Petrashko <dark@d-d.me>2016-04-20 15:29:38 +0200
commit0514d0efcdf159ea0ffdb51ac1e9a59d3ee429c0 (patch)
tree60c15b19d98fec378ac7046d34f08acce4454cce
parent247e91365f06cd482bf5fc456bd643d89acca157 (diff)
parent79a284fe9229adecebffb29d48687fe96b292746 (diff)
downloaddotty-0514d0efcdf159ea0ffdb51ac1e9a59d3ee429c0.tar.gz
dotty-0514d0efcdf159ea0ffdb51ac1e9a59d3ee429c0.tar.bz2
dotty-0514d0efcdf159ea0ffdb51ac1e9a59d3ee429c0.zip
Merge pull request #1219 from dotty-staging/fix-strawmans
Fix strawmans
-rw-r--r--src/dotty/tools/dotc/ast/Desugar.scala18
-rw-r--r--src/dotty/tools/dotc/core/NameOps.scala18
-rw-r--r--src/dotty/tools/dotc/core/Names.scala2
-rw-r--r--src/dotty/tools/dotc/core/TypeApplications.scala12
-rw-r--r--src/dotty/tools/dotc/core/Types.scala6
-rw-r--r--src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala2
-rw-r--r--src/dotty/tools/dotc/transform/ExtensionMethods.scala6
-rw-r--r--src/dotty/tools/dotc/transform/FirstTransform.scala4
-rw-r--r--src/dotty/tools/dotc/transform/SyntheticMethods.scala8
-rw-r--r--src/strawman/collections/CollectionStrawMan4.scala111
-rw-r--r--src/strawman/collections/CollectionStrawMan5.scala513
-rw-r--r--tests/run/CollectionTests.scala4
-rw-r--r--tests/run/colltest4.check85
-rw-r--r--tests/run/colltest4/CollectionStrawMan4_1.scala539
-rw-r--r--tests/run/colltest4/CollectionTests_2.scala172
-rw-r--r--tests/run/colltest5.check85
-rw-r--r--tests/run/colltest5/CollectionStrawMan5_1.scala513
-rw-r--r--tests/run/colltest5/CollectionTests_2.scala172
18 files changed, 2222 insertions, 48 deletions
diff --git a/src/dotty/tools/dotc/ast/Desugar.scala b/src/dotty/tools/dotc/ast/Desugar.scala
index ac2f8ae73..b6a4dc5e7 100644
--- a/src/dotty/tools/dotc/ast/Desugar.scala
+++ b/src/dotty/tools/dotc/ast/Desugar.scala
@@ -256,7 +256,21 @@ object desugar {
// prefixed by type or val). `tparams` and `vparamss` are the type parameters that
// go in `constr`, the constructor after desugaring.
+ /** Does `tree' look like a reference to AnyVal? Temporary test before we have inline classes */
+ def isAnyVal(tree: Tree): Boolean = tree match {
+ case Ident(tpnme.AnyVal) => true
+ case Select(qual, tpnme.AnyVal) => isScala(qual)
+ case _ => false
+ }
+ def isScala(tree: Tree): Boolean = tree match {
+ case Ident(nme.scala_) => true
+ case Select(Ident(nme.ROOTPKG), nme.scala_) => true
+ case _ => false
+ }
+
val isCaseClass = mods.is(Case) && !mods.is(Module)
+ val isValueClass = parents.nonEmpty && isAnyVal(parents.head)
+ // This is not watertight, but `extends AnyVal` will be replaced by `inline` later.
val constrTparams = constr1.tparams map toDefParam
val constrVparamss =
@@ -398,7 +412,9 @@ object desugar {
companionDefs(parent, applyMeths ::: unapplyMeth :: defaultGetters)
}
else if (defaultGetters.nonEmpty)
- companionDefs(anyRef, defaultGetters)
+ companionDefs(anyRef, defaultGetters)
+ else if (isValueClass)
+ companionDefs(anyRef, Nil)
else Nil
diff --git a/src/dotty/tools/dotc/core/NameOps.scala b/src/dotty/tools/dotc/core/NameOps.scala
index 81240a9fc..72f89aec3 100644
--- a/src/dotty/tools/dotc/core/NameOps.scala
+++ b/src/dotty/tools/dotc/core/NameOps.scala
@@ -116,7 +116,12 @@ object NameOps {
name.drop(tpnme.hkArgPrefixLength).toString.toInt
def isLambdaTraitName(implicit ctx: Context): Boolean =
- name.startsWith(tpnme.hkLambdaPrefix)
+ name.isTypeName && name.startsWith(tpnme.hkLambdaPrefix)
+
+ def lambdaTraitVariances(implicit ctx: Context): List[Int] = {
+ val vs = name.drop(tpnme.hkLambdaPrefix.length)
+ vs.toList.map(c => tpnme.varianceSuffixes.indexOf(c) - 1)
+ }
/** If the name ends with $nn where nn are
* all digits, strip the $ and the digits.
@@ -179,7 +184,13 @@ object NameOps {
* an encoded name, e.g. super$$plus$eq. See #765.
*/
def unexpandedName: N = {
- val idx = name.lastIndexOfSlice(nme.EXPAND_SEPARATOR)
+ var idx = name.lastIndexOfSlice(nme.EXPAND_SEPARATOR)
+
+ // Hack to make super accessors from traits work. They would otherwise fail because of #765
+ // TODO: drop this once we have more robust name handling
+ if (name.slice(idx - FalseSuperLength, idx) == FalseSuper)
+ idx -= FalseSuper.length
+
if (idx < 0) name else (name drop (idx + nme.EXPAND_SEPARATOR.length)).asInstanceOf[N]
}
@@ -431,4 +442,7 @@ object NameOps {
name.dropRight(nme.LAZY_LOCAL.length)
}
}
+
+ private final val FalseSuper = "$$super".toTermName
+ private val FalseSuperLength = FalseSuper.length
}
diff --git a/src/dotty/tools/dotc/core/Names.scala b/src/dotty/tools/dotc/core/Names.scala
index f1e6f7606..10eef16c1 100644
--- a/src/dotty/tools/dotc/core/Names.scala
+++ b/src/dotty/tools/dotc/core/Names.scala
@@ -37,7 +37,7 @@ object Names {
*/
abstract class Name extends DotClass
with PreName
- with Seq[Char]
+ with collection.immutable.Seq[Char]
with IndexedSeqOptimized[Char, Name] {
/** A type for names of the same kind as this name */
diff --git a/src/dotty/tools/dotc/core/TypeApplications.scala b/src/dotty/tools/dotc/core/TypeApplications.scala
index d5f44e632..3ed1798ed 100644
--- a/src/dotty/tools/dotc/core/TypeApplications.scala
+++ b/src/dotty/tools/dotc/core/TypeApplications.scala
@@ -377,6 +377,14 @@ class TypeApplications(val self: Type) extends AnyVal {
false
}
+ /** Dealias type if it can be done without forcing anything */
+ def safeDealias(implicit ctx: Context): Type = self match {
+ case self: TypeRef if self.denot.exists && self.symbol.isAliasType =>
+ self.info.bounds.hi.stripTypeVar.safeDealias
+ case _ =>
+ self
+ }
+
/** Replace references to type parameters with references to hk arguments `this.$hk_i`
* Care is needed not to cause cyclic reference errors, hence `SafeSubstMap`.
*/
@@ -546,8 +554,8 @@ class TypeApplications(val self: Type) extends AnyVal {
substHkArgs(body)
case self: PolyType =>
self.instantiate(args)
- case _ =>
- appliedTo(args, typeParams)
+ case self1 =>
+ self1.safeDealias.appliedTo(args, typeParams)
}
}
diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala
index fa049815a..913339409 100644
--- a/src/dotty/tools/dotc/core/Types.scala
+++ b/src/dotty/tools/dotc/core/Types.scala
@@ -903,7 +903,9 @@ object Types {
case pre: RefinedType =>
object instantiate extends TypeMap {
var isSafe = true
- def apply(tp: Type): Type = tp match {
+ def apply(tp: Type): Type =
+ if (!isSafe) tp
+ else tp match {
case TypeRef(RefinedThis(`pre`), name) if name.isHkArgName =>
member(name).info match {
case TypeAlias(alias) => alias
@@ -2061,7 +2063,7 @@ object Types {
false
}
override def computeHash = doHash(refinedName, refinedInfo, parent)
- override def toString = s"RefinedType($parent, $refinedName, $refinedInfo)"
+ override def toString = s"RefinedType($parent, $refinedName, $refinedInfo | $hashCode)"
}
class CachedRefinedType(parent: Type, refinedName: Name, infoFn: RefinedType => Type) extends RefinedType(parent, refinedName) {
diff --git a/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
index ad6ddf7fe..535ddd216 100644
--- a/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
+++ b/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
@@ -252,6 +252,8 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) {
readPackageRef().termRef
case TYPEREF =>
val name = readName().toTypeName
+ if (name.isLambdaTraitName) // Make sure curresponding lambda trait exists
+ defn.LambdaTrait(name.lambdaTraitVariances)
TypeRef(readType(), name)
case TERMREF =>
readNameSplitSig() match {
diff --git a/src/dotty/tools/dotc/transform/ExtensionMethods.scala b/src/dotty/tools/dotc/transform/ExtensionMethods.scala
index a1d2e5c68..8d61cef42 100644
--- a/src/dotty/tools/dotc/transform/ExtensionMethods.scala
+++ b/src/dotty/tools/dotc/transform/ExtensionMethods.scala
@@ -33,7 +33,7 @@ import SymUtils._
* This is different from the implementation of value classes in Scala 2
* (see SIP-15) which uses `asInstanceOf` which does not typecheck.
*/
-class ExtensionMethods extends MiniPhaseTransform with DenotTransformer with FullParameterization with NeedsCompanions { thisTransformer =>
+class ExtensionMethods extends MiniPhaseTransform with DenotTransformer with FullParameterization { thisTransformer =>
import tpd._
import ExtensionMethods._
@@ -45,10 +45,6 @@ class ExtensionMethods extends MiniPhaseTransform with DenotTransformer with Ful
override def runsAfterGroupsOf = Set(classOf[FirstTransform]) // need companion objects to exist
- def isCompanionNeeded(cls: ClassSymbol)(implicit ctx: Context): Boolean = {
- isDerivedValueClass(cls)
- }
-
override def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation = ref match {
case moduleClassSym: ClassDenotation if moduleClassSym is ModuleClass =>
moduleClassSym.linkedClass match {
diff --git a/src/dotty/tools/dotc/transform/FirstTransform.scala b/src/dotty/tools/dotc/transform/FirstTransform.scala
index 37ae1d94e..97dd9404f 100644
--- a/src/dotty/tools/dotc/transform/FirstTransform.scala
+++ b/src/dotty/tools/dotc/transform/FirstTransform.scala
@@ -44,7 +44,7 @@ class FirstTransform extends MiniPhaseTransform with IdentityDenotTransformer wi
this
}
- def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = {
+ def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = tp/*{
tp match {
//create companions for value classes that are not from currently compiled source file
case tp@ClassInfo(_, cls, _, decls, _)
@@ -59,7 +59,7 @@ class FirstTransform extends MiniPhaseTransform with IdentityDenotTransformer wi
case _ => tp
}
}
-
+*/
override def checkPostCondition(tree: Tree)(implicit ctx: Context): Unit = {
tree match {
case Select(qual, _) if tree.symbol.exists =>
diff --git a/src/dotty/tools/dotc/transform/SyntheticMethods.scala b/src/dotty/tools/dotc/transform/SyntheticMethods.scala
index a496f80ce..9dfd92fe9 100644
--- a/src/dotty/tools/dotc/transform/SyntheticMethods.scala
+++ b/src/dotty/tools/dotc/transform/SyntheticMethods.scala
@@ -10,6 +10,7 @@ import DenotTransformers._
import ast.Trees._
import ast.untpd
import Decorators._
+import NameOps._
import ValueClasses.isDerivedValueClass
import scala.collection.mutable.ListBuffer
import scala.language.postfixOps
@@ -79,14 +80,17 @@ class SyntheticMethods(thisTransformer: DenotTransformer) {
def forwardToRuntime(vrefss: List[List[Tree]]): Tree =
ref(defn.runtimeMethodRef("_" + sym.name.toString)).appliedToArgs(This(clazz) :: vrefss.head)
+ def ownName(vrefss: List[List[Tree]]): Tree =
+ Literal(Constant(clazz.name.stripModuleClassSuffix.decode.toString))
+
def syntheticRHS(implicit ctx: Context): List[List[Tree]] => Tree = synthetic.name match {
case nme.hashCode_ if isDerivedValueClass(clazz) => vrefss => valueHashCodeBody
case nme.hashCode_ => vrefss => caseHashCodeBody
- case nme.toString_ => forwardToRuntime
+ case nme.toString_ => if (clazz.is(ModuleClass)) ownName else forwardToRuntime
case nme.equals_ => vrefss => equalsBody(vrefss.head.head)
case nme.canEqual_ => vrefss => canEqualBody(vrefss.head.head)
case nme.productArity => vrefss => Literal(Constant(accessors.length))
- case nme.productPrefix => vrefss => Literal(Constant(clazz.name.decode.toString))
+ case nme.productPrefix => ownName
}
ctx.log(s"adding $synthetic to $clazz at ${ctx.phase}")
DefDef(synthetic, syntheticRHS(ctx.withOwner(synthetic)))
diff --git a/src/strawman/collections/CollectionStrawMan4.scala b/src/strawman/collections/CollectionStrawMan4.scala
index 9159b1cfc..874f67a2d 100644
--- a/src/strawman/collections/CollectionStrawMan4.scala
+++ b/src/strawman/collections/CollectionStrawMan4.scala
@@ -2,6 +2,8 @@ package strawman.collections
import Predef.{augmentString => _, wrapString => _, _}
import scala.reflect.ClassTag
+import annotation.unchecked.uncheckedVariance
+import annotation.tailrec
/** A strawman architecture for new collections. It contains some
* example collection classes and methods with the intent to expose
@@ -20,14 +22,7 @@ object CollectionStrawMan4 {
def iterator: Iterator[A]
}
- /** Base trait for generic collections */
- trait Iterable[+A] extends IterableOnce[A] with FromIterable[Iterable] {
- def iterator: Iterator[A]
- def view: View[A] = View.fromIterator(iterator)
- def knownLength: Int = -1
- }
-
- /** Base trait for instances that can construct a collection from an iterator */
+ /** Base trait for instances that can construct a collection from an iterable */
trait FromIterable[+C[X] <: Iterable[X]] {
def fromIterable[B](v: Iterable[B]): C[B]
}
@@ -38,16 +33,27 @@ object CollectionStrawMan4 {
def apply[A](xs: A*): C[A] = fromIterable(View.Elems(xs: _*))
}
+ /** Base trait for generic collections */
+ trait Iterable[+A] extends IterableOnce[A] with FromIterable[Iterable] {
+ def view: View[A] = View.fromIterator(iterator) // view is overridden, cannot be defined in ops
+ def knownLength: Int = -1
+ }
+
/** Base trait for sequence collections */
trait Seq[+A] extends Iterable[A] with FromIterable[Seq] {
def apply(i: Int): A
def length: Int
}
+ /** Base trait for collection builders */
trait Builder[-A, +To] {
def +=(x: A): this.type
- def ++=(xs: IterableOnce[A]): Unit = xs.iterator.foreach(+=)
def result: To
+
+ def ++=(xs: IterableOnce[A]): this.type = {
+ xs.iterator.foreach(+=)
+ this
+ }
}
/* ------------ Operations ----------------------------------- */
@@ -134,17 +140,18 @@ object CollectionStrawMan4 {
require(!isEmpty)
if (i == 0) head else tail.apply(i - 1)
}
- def :::[B >: A](prefix: List[B]): List[B] =
- if (prefix.isEmpty) this
- else Cons(prefix.head, prefix.tail ::: this)
def length: Int =
if (isEmpty) 0 else 1 + tail.length
+ def ++:[B >: A](prefix: List[B]): List[B] =
+ if (prefix.isEmpty) this
+ else Cons(prefix.head, prefix.tail ++: this)
}
- case class Cons[+A](x: A, xs: List[A]) extends List[A] {
+ case class Cons[+A](x: A, private[collections] var next: List[A @uncheckedVariance]) // sound because `next` is used only locally
+ extends List[A] {
def isEmpty = false
def head = x
- def tail = xs
+ def tail = next
}
case object Nil extends List[Nothing] {
@@ -157,20 +164,64 @@ object CollectionStrawMan4 {
def fromIterator[B](it: Iterator[B]): List[B] =
if (it.hasNext) Cons(it.next, fromIterator(it)) else Nil
def fromIterable[B](c: Iterable[B]): List[B] = c match {
- case View.Concat(xs, ys: Iterable[B]) =>
- fromIterable(xs) ::: fromIterable(ys)
+ case View.Concat(xs, ys: List[B]) =>
+ fromIterable(xs) ++: ys
case View.Drop(xs: List[B], n) =>
- var i = 0
- var ys = xs
- while (i < n && !xs.isEmpty) {
- ys = ys.tail
- i += 1
- }
- ys
+ @tailrec def loop(xs: List[B], n: Int): List[B] =
+ if (n > 0) loop(xs.tail, n - 1) else xs
+ loop(xs, n)
+ case c: List[B] => c
case _ => fromIterator(c.iterator)
}
}
+ /** Concrete collection type: ListBuffer */
+ class ListBuffer[A] extends Seq[A] with FromIterable[ListBuffer] with Builder[A, List[A]] {
+ private var first, last: List[A] = Nil
+ private var aliased = false
+ def iterator = first.iterator
+ def fromIterable[B](coll: Iterable[B]) = ListBuffer.fromIterable(coll)
+ def apply(i: Int) = first.apply(i)
+ def length = first.length
+
+ private def copyElems(): Unit = {
+ val buf = ListBuffer.fromIterable(result)
+ first = buf.first
+ last = buf.last
+ aliased = false
+ }
+ def result = {
+ aliased = true
+ first
+ }
+ def +=(elem: A) = {
+ if (aliased) copyElems()
+ val last1 = Cons(elem, Nil)
+ last match {
+ case last: Cons[A] => last.next = last1
+ case _ => first = last1
+ }
+ last = last1
+ this
+ }
+ override def toString: String =
+ if (first.isEmpty) "ListBuffer()"
+ else {
+ val b = new StringBuilder("ListBuffer(").append(first.head)
+ first.tail.foldLeft(b)(_.append(", ").append(_)).append(")").toString
+ }
+ }
+
+ object ListBuffer extends IterableFactory[ListBuffer] {
+ def fromIterable[B](coll: Iterable[B]): ListBuffer[B] = coll match {
+ case pd @ View.Partitioned(partition: View.Partition[B]) =>
+ partition.distribute(new ListBuffer[B]())
+ pd.forced.get.asInstanceOf[ListBuffer[B]]
+ case _ =>
+ new ListBuffer[B] ++= coll
+ }
+ }
+
/** Concrete collection type: ArrayBuffer */
class ArrayBuffer[A] private (initElems: Array[AnyRef], initLength: Int)
extends Seq[A] with FromIterable[ArrayBuffer] with Builder[A, ArrayBuffer[A]] {
@@ -234,12 +285,6 @@ object CollectionStrawMan4 {
def apply(n: Int) = elems(start + n).asInstanceOf[A]
}
- case class StringView(s: String) extends RandomAccessView[Char] {
- val start = 0
- val end = s.length
- def apply(n: Int) = s.charAt(n)
- }
-
/** Concrete collection type: String */
implicit class StringOps(val s: String) extends AnyVal with Ops[Char] {
def iterator: Iterator[Char] = new StringView(s).iterator
@@ -277,6 +322,12 @@ object CollectionStrawMan4 {
def ++(xs: String): String = s + xs
}
+ case class StringView(s: String) extends RandomAccessView[Char] {
+ val start = 0
+ val end = s.length
+ def apply(n: Int) = s.charAt(n)
+ }
+
/* ------------ Views --------------------------------------- */
/** A lazy iterable */
@@ -322,6 +373,8 @@ object CollectionStrawMan4 {
}
case class Partition[A](val underlying: Iterable[A], p: A => Boolean) {
val left, right = Partitioned(this)
+ // `distribute` makes up for the lack of generic push-based functionality.
+ // It forces both halves of the partition with a given builder.
def distribute(bf: => Builder[A, Iterable[A]]) = {
val lb, rb = bf
val it = underlying.iterator
diff --git a/src/strawman/collections/CollectionStrawMan5.scala b/src/strawman/collections/CollectionStrawMan5.scala
new file mode 100644
index 000000000..1a89d9659
--- /dev/null
+++ b/src/strawman/collections/CollectionStrawMan5.scala
@@ -0,0 +1,513 @@
+package strawman.collections
+
+import Predef.{augmentString => _, wrapString => _, _}
+import scala.reflect.ClassTag
+import annotation.unchecked.uncheckedVariance
+import annotation.tailrec
+
+/** A strawman architecture for new collections. It contains some
+ * example collection classes and methods with the intent to expose
+ * some key issues. It would be good to compare this to other
+ * implementations of the same functionality, to get an idea of the
+ * strengths and weaknesses of different collection architectures.
+ *
+ * For a test file, see tests/run/CollectionTests.scala.
+ */
+object CollectionStrawMan5 {
+
+ /* ------------ Base Traits -------------------------------- */
+
+ /** Iterator can be used only once */
+ trait IterableOnce[+A] {
+ def iterator: Iterator[A]
+ }
+
+ /** Base trait for instances that can construct a collection from an iterable */
+ trait FromIterable[+C[X] <: Iterable[X]] {
+ def fromIterable[B](it: Iterable[B]): C[B]
+ }
+
+ /** Base trait for companion objects of collections */
+ trait IterableFactory[+C[X] <: Iterable[X]] extends FromIterable[C] {
+ def empty[X]: C[X] = fromIterable(View.Empty)
+ def apply[A](xs: A*): C[A] = fromIterable(View.Elems(xs: _*))
+ }
+
+ /** Base trait for generic collections */
+ trait Iterable[+A] extends IterableOnce[A] with IterableLike[A, Iterable] {
+ protected def coll: Iterable[A] = this
+ def knownLength: Int = -1
+ }
+
+ /** Base trait for sequence collections */
+ trait Seq[+A] extends Iterable[A] with SeqLike[A, Seq] {
+ def apply(i: Int): A
+ def length: Int
+ }
+
+ /** Base trait for strict collections */
+ trait Buildable[+A, +To <: Iterable[A]] extends Iterable[A] {
+ protected[this] def newBuilder: Builder[A, To]
+ override def partition(p: A => Boolean): (To, To) = {
+ val l, r = newBuilder
+ iterator.foreach(x => (if (p(x)) l else r) += x)
+ (l.result, r.result)
+ }
+ // one might also override other transforms here to avoid generating
+ // iterators if it helps efficiency.
+ }
+
+ /** Base trait for collection builders */
+ trait Builder[-A, +To] {
+ def +=(x: A): this.type
+ def result: To
+
+ def ++=(xs: IterableOnce[A]): this.type = {
+ xs.iterator.foreach(+=)
+ this
+ }
+ }
+
+ /* ------------ Operations ----------------------------------- */
+
+ /** Base trait for Iterable operations
+ *
+ * VarianceNote
+ * ============
+ *
+ * We require that for all child classes of Iterable the variance of
+ * the child class and the variance of the `C` parameter passed to `IterableLike`
+ * are the same. We cannot express this since we lack variance polymorphism. That's
+ * why we have to resort at some places to write `C[A @uncheckedVariance]`.
+ *
+ */
+ trait IterableLike[+A, +C[X] <: Iterable[X]]
+ extends FromIterable[C]
+ with IterableOps[A]
+ with IterableMonoTransforms[A, C[A @uncheckedVariance]] // sound bcs of VarianceNote
+ with IterablePolyTransforms[A, C] {
+ protected[this] def fromLikeIterable(coll: Iterable[A]): C[A] = fromIterable(coll)
+ }
+
+ /** Base trait for Seq operations */
+ trait SeqLike[+A, +C[X] <: Seq[X]]
+ extends IterableLike[A, C] with SeqMonoTransforms[A, C[A @uncheckedVariance]] // sound bcs of VarianceNote
+
+ trait IterableOps[+A] extends Any {
+ def iterator: Iterator[A]
+ def foreach(f: A => Unit): Unit = iterator.foreach(f)
+ def foldLeft[B](z: B)(op: (B, A) => B): B = iterator.foldLeft(z)(op)
+ def foldRight[B](z: B)(op: (A, B) => B): B = iterator.foldRight(z)(op)
+ def indexWhere(p: A => Boolean): Int = iterator.indexWhere(p)
+ def isEmpty: Boolean = !iterator.hasNext
+ def head: A = iterator.next
+ def view: View[A] = View.fromIterator(iterator)
+ }
+
+ trait IterableMonoTransforms[+A, +Repr] extends Any {
+ protected def coll: Iterable[A]
+ protected[this] def fromLikeIterable(coll: Iterable[A]): Repr
+ def filter(p: A => Boolean): Repr = fromLikeIterable(View.Filter(coll, p))
+ def partition(p: A => Boolean): (Repr, Repr) = {
+ val pn = View.Partition(coll, p)
+ (fromLikeIterable(pn.left), fromLikeIterable(pn.right))
+ }
+ def drop(n: Int): Repr = fromLikeIterable(View.Drop(coll, n))
+ def to[C[X] <: Iterable[X]](fi: FromIterable[C]): C[A @uncheckedVariance] =
+ // variance seems sound because `to` could just as well have been added
+ // as a decorator. We should investigate this further to be sure.
+ fi.fromIterable(coll)
+ }
+
+ trait IterablePolyTransforms[+A, +C[A]] extends Any {
+ protected def coll: Iterable[A]
+ def fromIterable[B](coll: Iterable[B]): C[B]
+ def map[B](f: A => B): C[B] = fromIterable(View.Map(coll, f))
+ def flatMap[B](f: A => IterableOnce[B]): C[B] = fromIterable(View.FlatMap(coll, f))
+ def ++[B >: A](xs: IterableOnce[B]): C[B] = fromIterable(View.Concat(coll, xs))
+ def zip[B](xs: IterableOnce[B]): C[(A @uncheckedVariance, B)] = fromIterable(View.Zip(coll, xs))
+ // sound bcs of VarianceNote
+ }
+
+ trait SeqMonoTransforms[+A, +Repr] extends Any with IterableMonoTransforms[A, Repr] {
+ def reverse: Repr = {
+ var xs: List[A] = Nil
+ var it = coll.iterator
+ while (it.hasNext) xs = new Cons(it.next, xs)
+ fromLikeIterable(xs)
+ }
+ }
+
+ /* --------- Concrete collection types ------------------------------- */
+
+ /** Concrete collection type: List */
+ sealed trait List[+A] extends Seq[A] with SeqLike[A, List] with Buildable[A, List[A]] { self =>
+ def isEmpty: Boolean
+ def head: A
+ def tail: List[A]
+ def iterator = new Iterator[A] {
+ private[this] var current = self
+ def hasNext = !current.isEmpty
+ def next = { val r = current.head; current = current.tail; r }
+ }
+ def fromIterable[B](c: Iterable[B]): List[B] = List.fromIterable(c)
+ def apply(i: Int): A = {
+ require(!isEmpty)
+ if (i == 0) head else tail.apply(i - 1)
+ }
+ def length: Int =
+ if (isEmpty) 0 else 1 + tail.length
+ protected[this] def newBuilder = new ListBuffer[A]
+ def ++:[B >: A](prefix: List[B]): List[B] =
+ if (prefix.isEmpty) this
+ else Cons(prefix.head, prefix.tail ++: this)
+ override def ++[B >: A](xs: IterableOnce[B]): List[B] = xs match {
+ case xs: List[B] => this ++: xs
+ case _ => super.++(xs)
+ }
+ @tailrec final override def drop(n: Int) =
+ if (n > 0) tail.drop(n - 1) else this
+ }
+
+ case class Cons[+A](x: A, private[collections] var next: List[A @uncheckedVariance]) // sound because `next` is used only locally
+ extends List[A] {
+ override def isEmpty = false
+ override def head = x
+ def tail = next
+ }
+
+ case object Nil extends List[Nothing] {
+ override def isEmpty = true
+ override def head = ???
+ def tail = ???
+ }
+
+ object List extends IterableFactory[List] {
+ def fromIterable[B](coll: Iterable[B]): List[B] = coll match {
+ case coll: List[B] => coll
+ case _ => ListBuffer.fromIterable(coll).result
+ }
+ }
+
+ /** Concrete collection type: ListBuffer */
+ class ListBuffer[A] extends Seq[A] with SeqLike[A, ListBuffer] with Builder[A, List[A]] {
+ private var first, last: List[A] = Nil
+ private var aliased = false
+ def iterator = first.iterator
+ def fromIterable[B](coll: Iterable[B]) = ListBuffer.fromIterable(coll)
+ def apply(i: Int) = first.apply(i)
+ def length = first.length
+
+ private def copyElems(): Unit = {
+ val buf = ListBuffer.fromIterable(result)
+ first = buf.first
+ last = buf.last
+ aliased = false
+ }
+ def result = {
+ aliased = true
+ first
+ }
+ def +=(elem: A) = {
+ if (aliased) copyElems()
+ val last1 = Cons(elem, Nil)
+ last match {
+ case last: Cons[A] => last.next = last1
+ case _ => first = last1
+ }
+ last = last1
+ this
+ }
+ override def toString: String =
+ if (first.isEmpty) "ListBuffer()"
+ else {
+ val b = new StringBuilder("ListBuffer(").append(first.head)
+ first.tail.foldLeft(b)(_.append(", ").append(_)).append(")").toString
+ }
+ }
+
+ object ListBuffer extends IterableFactory[ListBuffer] {
+ def fromIterable[B](coll: Iterable[B]): ListBuffer[B] = new ListBuffer[B] ++= coll
+ }
+
+ /** Concrete collection type: ArrayBuffer */
+ class ArrayBuffer[A] private (initElems: Array[AnyRef], initLength: Int)
+ extends Seq[A] with SeqLike[A, ArrayBuffer] with Builder[A, ArrayBuffer[A]] {
+ def this() = this(new Array[AnyRef](16), 0)
+ private var elems: Array[AnyRef] = initElems
+ private var start = 0
+ private var end = initLength
+ def apply(n: Int) = elems(start + n).asInstanceOf[A]
+ def length = end - start
+ override def knownLength = length
+ override def view = new ArrayBufferView(elems, start, end)
+ def iterator = view.iterator
+ def fromIterable[B](it: Iterable[B]): ArrayBuffer[B] =
+ ArrayBuffer.fromIterable(it)
+ def +=(elem: A): this.type = {
+ if (end == elems.length) {
+ if (start > 0) {
+ Array.copy(elems, start, elems, 0, length)
+ end -= start
+ start = 0
+ }
+ else {
+ val newelems = new Array[AnyRef](end * 2)
+ Array.copy(elems, 0, newelems, 0, end)
+ elems = newelems
+ }
+ }
+ elems(end) = elem.asInstanceOf[AnyRef]
+ end += 1
+ this
+ }
+ def result = this
+ def trimStart(n: Int): Unit = start += (n max 0)
+ override def ++[B >: A](xs: IterableOnce[B]): ArrayBuffer[B] = xs match {
+ case xs: ArrayBuffer[B] =>
+ val elems = new Array[AnyRef](length + xs.length)
+ Array.copy(this.elems, this.start, elems, 0, this.length)
+ Array.copy(xs.elems, xs.start, elems, this.length, xs.length)
+ new ArrayBuffer(elems, elems.length)
+ case _ => super.++(xs)
+ }
+
+ override def toString = s"ArrayBuffer(${elems.slice(start, end).mkString(", ")})"
+ }
+
+ object ArrayBuffer extends IterableFactory[ArrayBuffer] {
+ def fromIterable[B](coll: Iterable[B]): ArrayBuffer[B] =
+ if (coll.knownLength >= 0) {
+ val elems = new Array[AnyRef](coll.knownLength)
+ val it = coll.iterator
+ for (i <- 0 until elems.length) elems(i) = it.next().asInstanceOf[AnyRef]
+ new ArrayBuffer[B](elems, elems.length)
+ }
+ else {
+ val buf = new ArrayBuffer[B]
+ val it = coll.iterator
+ while (it.hasNext) buf += it.next()
+ buf
+ }
+ }
+
+ class ArrayBufferView[A](val elems: Array[AnyRef], val start: Int, val end: Int) extends RandomAccessView[A] {
+ def apply(n: Int) = elems(start + n).asInstanceOf[A]
+ }
+
+ /** Concrete collection type: String */
+ implicit class StringOps(val s: String)
+ extends AnyVal with IterableOps[Char]
+ with SeqMonoTransforms[Char, String]
+ with IterablePolyTransforms[Char, List] {
+ protected def coll = new StringView(s)
+ def iterator = coll.iterator
+ protected def fromLikeIterable(coll: Iterable[Char]): String = {
+ val sb = new StringBuilder
+ for (ch <- coll) sb.append(ch)
+ sb.toString
+ }
+ def fromIterable[B](coll: Iterable[B]): List[B] = List.fromIterable(coll)
+ def map(f: Char => Char): String = {
+ val sb = new StringBuilder
+ for (ch <- s) sb.append(f(ch))
+ sb.toString
+ }
+ def flatMap(f: Char => String): String = {
+ val sb = new StringBuilder
+ for (ch <- s) sb.append(f(ch))
+ sb.toString
+ }
+ def ++(xs: IterableOnce[Char]): String = {
+ val sb = new StringBuilder(s)
+ for (ch <- xs.iterator) sb.append(ch)
+ sb.toString
+ }
+ def ++(xs: String): String = s + xs
+ }
+
+ case class StringView(s: String) extends RandomAccessView[Char] {
+ val start = 0
+ val end = s.length
+ def apply(n: Int) = s.charAt(n)
+ }
+
+/* ---------- Views -------------------------------------------------------*/
+
+ /** Concrete collection type: View */
+ trait View[+A] extends Iterable[A] with IterableLike[A, View] {
+ override def view = this
+ override def fromIterable[B](c: Iterable[B]): View[B] = c match {
+ case c: View[B] => c
+ case _ => View.fromIterator(c.iterator)
+ }
+ }
+
+ /** View defined in terms of indexing a range */
+ trait RandomAccessView[+A] extends View[A] {
+ def start: Int
+ def end: Int
+ def apply(i: Int): A
+ def iterator: Iterator[A] = new Iterator[A] {
+ private var current = start
+ def hasNext = current < end
+ def next: A = {
+ val r = apply(current)
+ current += 1
+ r
+ }
+ }
+ override def knownLength = end - start max 0
+ }
+
+ object View {
+ def fromIterator[A](it: => Iterator[A]): View[A] = new View[A] {
+ def iterator = it
+ }
+ case object Empty extends View[Nothing] {
+ def iterator = Iterator.empty
+ override def knownLength = 0
+ }
+ case class Elems[A](xs: A*) extends View[A] {
+ def iterator = Iterator(xs: _*)
+ override def knownLength = xs.length
+ }
+ case class Filter[A](val underlying: Iterable[A], p: A => Boolean) extends View[A] {
+ def iterator = underlying.iterator.filter(p)
+ }
+ case class Partition[A](val underlying: Iterable[A], p: A => Boolean) {
+ val left = Partitioned(this, true)
+ val right = Partitioned(this, false)
+ }
+ case class Partitioned[A](partition: Partition[A], cond: Boolean) extends View[A] {
+ def iterator = partition.underlying.iterator.filter(x => partition.p(x) == cond)
+ }
+ case class Drop[A](underlying: Iterable[A], n: Int) extends View[A] {
+ def iterator = underlying.iterator.drop(n)
+ override def knownLength =
+ if (underlying.knownLength >= 0) underlying.knownLength - n max 0 else -1
+ }
+ case class Map[A, B](underlying: Iterable[A], f: A => B) extends View[B] {
+ def iterator = underlying.iterator.map(f)
+ override def knownLength = underlying.knownLength
+ }
+ case class FlatMap[A, B](underlying: Iterable[A], f: A => IterableOnce[B]) extends View[B] {
+ def iterator = underlying.iterator.flatMap(f)
+ }
+ case class Concat[A](underlying: Iterable[A], other: IterableOnce[A]) extends View[A] {
+ def iterator = underlying.iterator ++ other
+ override def knownLength = other match {
+ case other: Iterable[_] if underlying.knownLength >= 0 && other.knownLength >= 0 =>
+ underlying.knownLength + other.knownLength
+ case _ =>
+ -1
+ }
+ }
+ case class Zip[A, B](underlying: Iterable[A], other: IterableOnce[B]) extends View[(A, B)] {
+ def iterator = underlying.iterator.zip(other)
+ override def knownLength = other match {
+ case other: Iterable[_] if underlying.knownLength >= 0 && other.knownLength >= 0 =>
+ underlying.knownLength min other.knownLength
+ case _ =>
+ -1
+ }
+ }
+ }
+
+/* ---------- Iterators ---------------------------------------------------*/
+
+ /** A core Iterator class */
+ trait Iterator[+A] extends IterableOnce[A] { self =>
+ def hasNext: Boolean
+ def next(): A
+ def iterator = this
+ def foldLeft[B](z: B)(op: (B, A) => B): B =
+ if (hasNext) foldLeft(op(z, next))(op) else z
+ def foldRight[B](z: B)(op: (A, B) => B): B =
+ if (hasNext) op(next(), foldRight(z)(op)) else z
+ def foreach(f: A => Unit): Unit =
+ while (hasNext) f(next())
+ def indexWhere(p: A => Boolean): Int = {
+ var i = 0
+ while (hasNext) {
+ if (p(next())) return i
+ i += 1
+ }
+ -1
+ }
+ def filter(p: A => Boolean): Iterator[A] = new Iterator[A] {
+ private var hd: A = _
+ private var hdDefined: Boolean = false
+
+ def hasNext: Boolean = hdDefined || {
+ do {
+ if (!self.hasNext) return false
+ hd = self.next()
+ } while (!p(hd))
+ hdDefined = true
+ true
+ }
+
+ def next() =
+ if (hasNext) {
+ hdDefined = false
+ hd
+ }
+ else Iterator.empty.next()
+ }
+
+ def map[B](f: A => B): Iterator[B] = new Iterator[B] {
+ def hasNext = self.hasNext
+ def next() = f(self.next())
+ }
+
+ def flatMap[B](f: A => IterableOnce[B]): Iterator[B] = new Iterator[B] {
+ private var myCurrent: Iterator[B] = Iterator.empty
+ private def current = {
+ while (!myCurrent.hasNext && self.hasNext)
+ myCurrent = f(self.next()).iterator
+ myCurrent
+ }
+ def hasNext = current.hasNext
+ def next() = current.next()
+ }
+ def ++[B >: A](xs: IterableOnce[B]): Iterator[B] = new Iterator[B] {
+ private var myCurrent: Iterator[B] = self
+ private var first = true
+ private def current = {
+ if (!myCurrent.hasNext && first) {
+ myCurrent = xs.iterator
+ first = false
+ }
+ myCurrent
+ }
+ def hasNext = current.hasNext
+ def next() = current.next()
+ }
+ def drop(n: Int): Iterator[A] = {
+ var i = 0
+ while (i < n && hasNext) {
+ next()
+ i += 1
+ }
+ this
+ }
+ def zip[B](that: IterableOnce[B]): Iterator[(A, B)] = new Iterator[(A, B)] {
+ val thatIterator = that.iterator
+ def hasNext = self.hasNext && thatIterator.hasNext
+ def next() = (self.next(), thatIterator.next())
+ }
+ }
+
+ object Iterator {
+ val empty: Iterator[Nothing] = new Iterator[Nothing] {
+ def hasNext = false
+ def next = throw new NoSuchElementException("next on empty iterator")
+ }
+ def apply[A](xs: A*): Iterator[A] = new RandomAccessView[A] {
+ val start = 0
+ val end = xs.length
+ def apply(n: Int) = xs(n)
+ }.iterator
+ }
+}
diff --git a/tests/run/CollectionTests.scala b/tests/run/CollectionTests.scala
index 4696774d1..e7b34d292 100644
--- a/tests/run/CollectionTests.scala
+++ b/tests/run/CollectionTests.scala
@@ -3,7 +3,7 @@ import scala.reflect.ClassTag
object Test {
import strawman.collections._
- import CollectionStrawMan4._
+ import CollectionStrawMan5._
def seqOps(xs: Seq[Int]) = {
val x1 = xs.foldLeft("")(_ + _)
@@ -49,7 +49,7 @@ object Test {
println(xs9)
println(xs10)
println(xs11)
- println(xs12)
+ println(xs12)
println(xs13)
println(xs14)
println(xs15)
diff --git a/tests/run/colltest4.check b/tests/run/colltest4.check
new file mode 100644
index 000000000..984a0803b
--- /dev/null
+++ b/tests/run/colltest4.check
@@ -0,0 +1,85 @@
+-------
+123
+123
+1
+1
+Cons(1,Cons(2,Cons(3,Nil)))
+Cons(2,Nil)
+Cons(1,Cons(3,Nil))
+Cons(3,Nil)
+Cons(true,Cons(true,Cons(true,Nil)))
+Cons(1,Cons(-1,Cons(2,Cons(-2,Cons(3,Cons(-3,Nil))))))
+Cons(1,Cons(2,Cons(3,Cons(1,Cons(2,Cons(3,Nil))))))
+Cons(1,Cons(2,Cons(3,Nil)))
+Cons(1,Cons(2,Cons(3,Nil)))
+Cons(1,Cons(2,Cons(3,Cons(a,Nil))))
+Cons((1,true),Cons((2,true),Cons((3,true),Nil)))
+Cons(3,Cons(2,Cons(1,Nil)))
+-------
+123
+123
+1
+1
+Cons(1,Cons(2,Cons(3,Nil)))
+ArrayBuffer(2)
+ArrayBuffer(1, 3)
+ArrayBuffer(3)
+ArrayBuffer(true, true, true)
+ArrayBuffer(1, -1, 2, -2, 3, -3)
+ArrayBuffer(1, 2, 3, 1, 2, 3)
+ArrayBuffer(1, 2, 3)
+Cons(1,Cons(2,Cons(3,Nil)))
+ArrayBuffer(1, 2, 3, a)
+ArrayBuffer((1,true), (2,true), (3,true))
+ArrayBuffer(3, 2, 1)
+-------
+123
+123
+1
+1
+Cons(1,Cons(2,Cons(3,Nil)))
+ListBuffer(2)
+ListBuffer(1, 3)
+ListBuffer(3)
+ListBuffer(true, true, true)
+ListBuffer(1, -1, 2, -2, 3, -3)
+ListBuffer(1, 2, 3, 1, 2, 3)
+ListBuffer(1, 2, 3)
+Cons(1,Cons(2,Cons(3,Nil)))
+ListBuffer(1, 2, 3, a)
+ListBuffer((1,true), (2,true), (3,true))
+ListBuffer(3, 2, 1)
+-------
+123
+123
+1
+1
+Cons(1,Cons(2,Cons(3,Nil)))
+Cons(2,Nil)
+Cons(1,Cons(3,Nil))
+Cons(3,Nil)
+Cons(true,Cons(true,Cons(true,Nil)))
+Cons(1,Cons(-1,Cons(2,Cons(-2,Cons(3,Cons(-3,Nil))))))
+Cons(1,Cons(2,Cons(3,Cons(1,Cons(2,Cons(3,Nil))))))
+Cons(1,Cons(2,Cons(3,Nil)))
+Cons(1,Cons(2,Cons(3,Nil)))
+Cons(1,Cons(2,Cons(3,Cons(a,Nil))))
+Cons((1,true),Cons((2,true),Cons((3,true),Nil)))
+-------
+abc
+abc
+1
+a
+Cons(a,Cons(b,Cons(c,Nil)))
+b
+ac
+c
+Cons(98,Cons(99,Cons(100,Nil)))
+ABC
+a,ab,bc,c
+abcabc
+abcxy
+abc
+Cons(a,Cons(b,Cons(c,Nil)))
+Cons(a,Cons(b,Cons(c,Cons(xyz,Nil))))
+Cons((a,98),Cons((b,99),Cons((c,100),Nil)))
diff --git a/tests/run/colltest4/CollectionStrawMan4_1.scala b/tests/run/colltest4/CollectionStrawMan4_1.scala
new file mode 100644
index 000000000..874f67a2d
--- /dev/null
+++ b/tests/run/colltest4/CollectionStrawMan4_1.scala
@@ -0,0 +1,539 @@
+package strawman.collections
+
+import Predef.{augmentString => _, wrapString => _, _}
+import scala.reflect.ClassTag
+import annotation.unchecked.uncheckedVariance
+import annotation.tailrec
+
+/** A strawman architecture for new collections. It contains some
+ * example collection classes and methods with the intent to expose
+ * some key issues. It would be good to compare this to other
+ * implementations of the same functionality, to get an idea of the
+ * strengths and weaknesses of different collection architectures.
+ *
+ * For a test file, see tests/run/CollectionTests.scala.
+ */
+object CollectionStrawMan4 {
+
+ /* ------------ Base Traits -------------------------------- */
+
+ /** Iterator can be used only once */
+ trait IterableOnce[+A] {
+ def iterator: Iterator[A]
+ }
+
+ /** Base trait for instances that can construct a collection from an iterable */
+ trait FromIterable[+C[X] <: Iterable[X]] {
+ def fromIterable[B](v: Iterable[B]): C[B]
+ }
+
+ /** Base trait for companion objects of collections */
+ trait IterableFactory[+C[X] <: Iterable[X]] extends FromIterable[C] {
+ def empty[X]: C[X] = fromIterable(View.Empty)
+ def apply[A](xs: A*): C[A] = fromIterable(View.Elems(xs: _*))
+ }
+
+ /** Base trait for generic collections */
+ trait Iterable[+A] extends IterableOnce[A] with FromIterable[Iterable] {
+ def view: View[A] = View.fromIterator(iterator) // view is overridden, cannot be defined in ops
+ def knownLength: Int = -1
+ }
+
+ /** Base trait for sequence collections */
+ trait Seq[+A] extends Iterable[A] with FromIterable[Seq] {
+ def apply(i: Int): A
+ def length: Int
+ }
+
+ /** Base trait for collection builders */
+ trait Builder[-A, +To] {
+ def +=(x: A): this.type
+ def result: To
+
+ def ++=(xs: IterableOnce[A]): this.type = {
+ xs.iterator.foreach(+=)
+ this
+ }
+ }
+
+ /* ------------ Operations ----------------------------------- */
+
+ /** Operations returning types unrelated to current collection */
+ trait Ops[A] extends Any {
+ def iterator: Iterator[A]
+ def foreach(f: A => Unit): Unit = iterator.foreach(f)
+ def foldLeft[B](z: B)(op: (B, A) => B): B = iterator.foldLeft(z)(op)
+ def foldRight[B](z: B)(op: (A, B) => B): B = iterator.foldRight(z)(op)
+ def indexWhere(p: A => Boolean): Int = iterator.indexWhere(p)
+ def isEmpty: Boolean = !iterator.hasNext
+ def head: A = iterator.next
+ }
+
+ /** Transforms returning same collection type */
+ trait MonoTransforms[A, Repr] extends Any {
+ protected def coll: Iterable[A]
+ protected def fromIterable(it: Iterable[A]): Repr
+ def filter(p: A => Boolean): Repr = fromIterable(View.Filter(coll, p))
+ def partition(p: A => Boolean): (Repr, Repr) = {
+ val pn = View.Partition(coll, p)
+ (fromIterable(pn.left), fromIterable(pn.right))
+ }
+ def drop(n: Int): Repr = fromIterable(View.Drop(coll, n))
+ def to[C[X] <: Iterable[X]](fv: FromIterable[C]): C[A] = fv.fromIterable(coll)
+ }
+
+ trait PolyTransforms[A, C[X]] extends Any {
+ protected def coll: Iterable[A]
+ protected def fromIterable[B](it: Iterable[B]): C[B]
+ def map[B](f: A => B): C[B] = fromIterable(View.Map(coll, f))
+ def flatMap[B](f: A => IterableOnce[B]): C[B] = fromIterable(View.FlatMap(coll, f))
+ def ++[B >: A](xs: IterableOnce[B]): C[B] = fromIterable(View.Concat(coll, xs))
+ def zip[B](xs: IterableOnce[B]): C[(A, B)] = fromIterable(View.Zip(coll, xs))
+ }
+
+ /** Transforms that only apply to Seq */
+ trait MonoTransformsOfSeqs[A, Repr] extends Any with MonoTransforms[A, Repr] {
+ def reverse: Repr = fromIterable(View.Reverse(coll))
+ }
+
+ /** Implementation of Ops for all generic collections */
+ implicit class IterableOps[A](val c: Iterable[A])
+ extends AnyVal with Ops[A] {
+ def iterator = c.iterator
+ }
+
+ /** Implementation of MonoTransforms for all generic collections */
+ implicit class IterableMonoTransforms[A, C[X] <: Iterable[X]](val c: Iterable[A] with FromIterable[C])
+ extends AnyVal with MonoTransforms[A, C[A]] {
+ protected def coll = c
+ protected def fromIterable(it: Iterable[A]): C[A] = c.fromIterable(it)
+ }
+
+ /** Implementation of PolyTransforms for all generic collections */
+ implicit class IterablePolyTransforms[A, C[X] <: Iterable[X]](val c: Iterable[A] with FromIterable[C])
+ extends AnyVal with PolyTransforms[A, C] {
+ protected def coll = c
+ protected def fromIterable[B](it: Iterable[B]): C[B] = c.fromIterable(it)
+ }
+
+ /** Implementation of MonoTransformsForSeqs for all generic collections */
+ implicit class SeqMonoTransforms[A, C[X] <: Seq[X]](val c: Seq[A] with FromIterable[C])
+ extends AnyVal with MonoTransformsOfSeqs[A, C[A]] {
+ protected def coll = c
+ protected def fromIterable(it: Iterable[A]): C[A] = c.fromIterable(it)
+ }
+
+ /* --------- Concrete collection types ------------------------------- */
+
+ /** Concrete collection type: List */
+ sealed trait List[+A] extends Seq[A] with FromIterable[List] { self =>
+ def isEmpty: Boolean
+ def head: A
+ def tail: List[A]
+ def iterator = new Iterator[A] {
+ private[this] var current = self
+ def hasNext = !current.isEmpty
+ def next = { val r = current.head; current = current.tail; r }
+ }
+ def fromIterable[B](c: Iterable[B]): List[B] = List.fromIterable(c)
+ def apply(i: Int): A = {
+ require(!isEmpty)
+ if (i == 0) head else tail.apply(i - 1)
+ }
+ def length: Int =
+ if (isEmpty) 0 else 1 + tail.length
+ def ++:[B >: A](prefix: List[B]): List[B] =
+ if (prefix.isEmpty) this
+ else Cons(prefix.head, prefix.tail ++: this)
+ }
+
+ case class Cons[+A](x: A, private[collections] var next: List[A @uncheckedVariance]) // sound because `next` is used only locally
+ extends List[A] {
+ def isEmpty = false
+ def head = x
+ def tail = next
+ }
+
+ case object Nil extends List[Nothing] {
+ def isEmpty = true
+ def head = ???
+ def tail = ???
+ }
+
+ object List extends IterableFactory[List] {
+ def fromIterator[B](it: Iterator[B]): List[B] =
+ if (it.hasNext) Cons(it.next, fromIterator(it)) else Nil
+ def fromIterable[B](c: Iterable[B]): List[B] = c match {
+ case View.Concat(xs, ys: List[B]) =>
+ fromIterable(xs) ++: ys
+ case View.Drop(xs: List[B], n) =>
+ @tailrec def loop(xs: List[B], n: Int): List[B] =
+ if (n > 0) loop(xs.tail, n - 1) else xs
+ loop(xs, n)
+ case c: List[B] => c
+ case _ => fromIterator(c.iterator)
+ }
+ }
+
+ /** Concrete collection type: ListBuffer */
+ class ListBuffer[A] extends Seq[A] with FromIterable[ListBuffer] with Builder[A, List[A]] {
+ private var first, last: List[A] = Nil
+ private var aliased = false
+ def iterator = first.iterator
+ def fromIterable[B](coll: Iterable[B]) = ListBuffer.fromIterable(coll)
+ def apply(i: Int) = first.apply(i)
+ def length = first.length
+
+ private def copyElems(): Unit = {
+ val buf = ListBuffer.fromIterable(result)
+ first = buf.first
+ last = buf.last
+ aliased = false
+ }
+ def result = {
+ aliased = true
+ first
+ }
+ def +=(elem: A) = {
+ if (aliased) copyElems()
+ val last1 = Cons(elem, Nil)
+ last match {
+ case last: Cons[A] => last.next = last1
+ case _ => first = last1
+ }
+ last = last1
+ this
+ }
+ override def toString: String =
+ if (first.isEmpty) "ListBuffer()"
+ else {
+ val b = new StringBuilder("ListBuffer(").append(first.head)
+ first.tail.foldLeft(b)(_.append(", ").append(_)).append(")").toString
+ }
+ }
+
+ object ListBuffer extends IterableFactory[ListBuffer] {
+ def fromIterable[B](coll: Iterable[B]): ListBuffer[B] = coll match {
+ case pd @ View.Partitioned(partition: View.Partition[B]) =>
+ partition.distribute(new ListBuffer[B]())
+ pd.forced.get.asInstanceOf[ListBuffer[B]]
+ case _ =>
+ new ListBuffer[B] ++= coll
+ }
+ }
+
+ /** Concrete collection type: ArrayBuffer */
+ class ArrayBuffer[A] private (initElems: Array[AnyRef], initLength: Int)
+ extends Seq[A] with FromIterable[ArrayBuffer] with Builder[A, ArrayBuffer[A]] {
+ def this() = this(new Array[AnyRef](16), 0)
+ private var elems: Array[AnyRef] = initElems
+ private var start = 0
+ private var end = initLength
+ def apply(n: Int) = elems(start + n).asInstanceOf[A]
+ def length = end - start
+ override def knownLength = length
+ override def view = new ArrayBufferView(elems, start, end)
+ def iterator = view.iterator
+ def fromIterable[B](it: Iterable[B]): ArrayBuffer[B] =
+ ArrayBuffer.fromIterable(it)
+ def +=(elem: A): this.type = {
+ if (end == elems.length) {
+ if (start > 0) {
+ Array.copy(elems, start, elems, 0, length)
+ end -= start
+ start = 0
+ }
+ else {
+ val newelems = new Array[AnyRef](end * 2)
+ Array.copy(elems, 0, newelems, 0, end)
+ elems = newelems
+ }
+ }
+ elems(end) = elem.asInstanceOf[AnyRef]
+ end += 1
+ this
+ }
+ def result = this
+ def trimStart(n: Int): Unit = start += (n max 0)
+ override def toString = s"ArrayBuffer(${elems.slice(start, end).mkString(", ")})"
+ }
+
+ object ArrayBuffer extends IterableFactory[ArrayBuffer] {
+ def fromIterable[B](c: Iterable[B]): ArrayBuffer[B] = c match {
+ case View.Concat(fst: ArrayBuffer[B], snd: ArrayBuffer[B]) =>
+ val elems = new Array[AnyRef](fst.length + snd.length)
+ Array.copy(fst.elems, fst.start, elems, 0, fst.length)
+ Array.copy(snd.elems, snd.start, elems, fst.length, snd.length)
+ new ArrayBuffer(elems, elems.length)
+ case pd @ View.Partitioned(partition: View.Partition[B]) =>
+ partition.distribute(new ArrayBuffer[B]())
+ pd.forced.get.asInstanceOf[ArrayBuffer[B]]
+ case c if c.knownLength >= 0 =>
+ val elems = new Array[AnyRef](c.knownLength)
+ val it = c.iterator
+ for (i <- 0 until elems.length) elems(i) = it.next().asInstanceOf[AnyRef]
+ new ArrayBuffer[B](elems, elems.length)
+ case _ =>
+ val buf = new ArrayBuffer[B]
+ val it = c.iterator
+ while (it.hasNext) buf += it.next()
+ buf
+ }
+ }
+
+ class ArrayBufferView[A](val elems: Array[AnyRef], val start: Int, val end: Int) extends RandomAccessView[A] {
+ def apply(n: Int) = elems(start + n).asInstanceOf[A]
+ }
+
+ /** Concrete collection type: String */
+ implicit class StringOps(val s: String) extends AnyVal with Ops[Char] {
+ def iterator: Iterator[Char] = new StringView(s).iterator
+ }
+
+ implicit class StringMonoTransforms(val s: String)
+ extends AnyVal with MonoTransformsOfSeqs[Char, String] {
+ protected def coll: Iterable[Char] = StringView(s)
+ protected def fromIterable(it: Iterable[Char]): String = {
+ val sb = new StringBuilder
+ for (ch <- it) sb.append(ch)
+ sb.toString
+ }
+ }
+
+ implicit class StringPolyTransforms(val s: String)
+ extends AnyVal with PolyTransforms[Char, Seq] {
+ protected def coll = StringView(s)
+ protected def fromIterable[B](it: Iterable[B]): Seq[B] = List.fromIterable(it)
+ def map(f: Char => Char): String = {
+ val sb = new StringBuilder
+ for (ch <- s) sb.append(f(ch))
+ sb.toString
+ }
+ def flatMap(f: Char => String) = {
+ val sb = new StringBuilder
+ for (ch <- s) sb.append(f(ch))
+ sb.toString
+ }
+ def ++(xs: IterableOnce[Char]): String = {
+ val sb = new StringBuilder(s)
+ for (ch <- xs.iterator) sb.append(ch)
+ sb.toString
+ }
+ def ++(xs: String): String = s + xs
+ }
+
+ case class StringView(s: String) extends RandomAccessView[Char] {
+ val start = 0
+ val end = s.length
+ def apply(n: Int) = s.charAt(n)
+ }
+
+ /* ------------ Views --------------------------------------- */
+
+ /** A lazy iterable */
+ trait View[+A] extends Iterable[A] with FromIterable[View] {
+ override def view = this
+ override def fromIterable[B](c: Iterable[B]) = c match {
+ case c: View[B] => c
+ case _ => View.fromIterator(c.iterator)
+ }
+ }
+
+ /** Iterator defined in terms of indexing a range */
+ trait RandomAccessView[+A] extends View[A] {
+ def start: Int
+ def end: Int
+ def apply(i: Int): A
+ def iterator: Iterator[A] = new Iterator[A] {
+ private var current = start
+ def hasNext = current < end
+ def next: A = {
+ val r = apply(current)
+ current += 1
+ r
+ }
+ }
+ override def knownLength = end - start max 0
+ }
+
+ object View {
+ def fromIterator[A](it: => Iterator[A]): View[A] = new View[A] {
+ def iterator = it
+ }
+ case object Empty extends View[Nothing] {
+ def iterator = Iterator.empty
+ override def knownLength = 0
+ }
+ case class Elems[A](xs: A*) extends View[A] {
+ def iterator = Iterator(xs: _*)
+ override def knownLength = xs.length
+ }
+ case class Filter[A](val underlying: Iterable[A], p: A => Boolean) extends View[A] {
+ def iterator = underlying.iterator.filter(p)
+ }
+ case class Partition[A](val underlying: Iterable[A], p: A => Boolean) {
+ val left, right = Partitioned(this)
+ // `distribute` makes up for the lack of generic push-based functionality.
+ // It forces both halves of the partition with a given builder.
+ def distribute(bf: => Builder[A, Iterable[A]]) = {
+ val lb, rb = bf
+ val it = underlying.iterator
+ while (it.hasNext) {
+ val x = it.next()
+ (if (p(x)) lb else rb) += x
+ }
+ left.forced = Some(lb.result)
+ right.forced = Some(rb.result)
+ }
+ }
+ case class Partitioned[A](partition: Partition[A]) extends View[A] {
+ private var myForced: Option[Iterable[A]] = None
+ def forced: Option[Iterable[A]] = myForced
+ private[View] def forced_=(x: Option[Iterable[A]]): Unit = myForced = x
+ def underlying = partition.underlying
+ def iterator = forced match {
+ case Some(c) => c.iterator
+ case None =>
+ underlying.iterator.filter(
+ if (this eq partition.left) partition.p else !partition.p(_))
+ }
+ }
+ case class Drop[A](underlying: Iterable[A], n: Int) extends View[A] {
+ def iterator = underlying.iterator.drop(n)
+ override def knownLength =
+ if (underlying.knownLength >= 0) underlying.knownLength - n max 0 else -1
+ }
+ case class Map[A, B](underlying: Iterable[A], f: A => B) extends View[B] {
+ def iterator = underlying.iterator.map(f)
+ override def knownLength = underlying.knownLength
+ }
+ case class FlatMap[A, B](underlying: Iterable[A], f: A => IterableOnce[B]) extends View[B] {
+ def iterator = underlying.iterator.flatMap(f)
+ }
+ case class Concat[A](underlying: Iterable[A], other: IterableOnce[A]) extends View[A] {
+ def iterator = underlying.iterator ++ other
+ override def knownLength = other match {
+ case other: Iterable[_] if underlying.knownLength >= 0 && other.knownLength >= 0 =>
+ underlying.knownLength + other.knownLength
+ case _ =>
+ -1
+ }
+ }
+ case class Zip[A, B](underlying: Iterable[A], other: IterableOnce[B]) extends View[(A, B)] {
+ def iterator = underlying.iterator.zip(other)
+ override def knownLength = other match {
+ case other: Iterable[_] if underlying.knownLength >= 0 && other.knownLength >= 0 =>
+ underlying.knownLength min other.knownLength
+ case _ =>
+ -1
+ }
+ }
+ case class Reverse[A](underlying: Iterable[A]) extends View[A] {
+ def iterator = {
+ var xs: List[A] = Nil
+ val it = underlying.iterator
+ while (it.hasNext) xs = Cons(it.next(), xs)
+ xs.iterator
+ }
+ override def knownLength = underlying.knownLength
+ }
+ }
+
+/* ---------- Iterators ---------------------------------------------------*/
+
+ /** A core Iterator class */
+ trait Iterator[+A] extends IterableOnce[A] { self =>
+ def hasNext: Boolean
+ def next(): A
+ def iterator = this
+ def foldLeft[B](z: B)(op: (B, A) => B): B =
+ if (hasNext) foldLeft(op(z, next))(op) else z
+ def foldRight[B](z: B)(op: (A, B) => B): B =
+ if (hasNext) op(next(), foldRight(z)(op)) else z
+ def foreach(f: A => Unit): Unit =
+ while (hasNext) f(next())
+ def indexWhere(p: A => Boolean): Int = {
+ var i = 0
+ while (hasNext) {
+ if (p(next())) return i
+ i += 1
+ }
+ -1
+ }
+ def filter(p: A => Boolean): Iterator[A] = new Iterator[A] {
+ private var hd: A = _
+ private var hdDefined: Boolean = false
+
+ def hasNext: Boolean = hdDefined || {
+ do {
+ if (!self.hasNext) return false
+ hd = self.next()
+ } while (!p(hd))
+ hdDefined = true
+ true
+ }
+
+ def next() =
+ if (hasNext) {
+ hdDefined = false
+ hd
+ }
+ else Iterator.empty.next()
+ }
+
+ def map[B](f: A => B): Iterator[B] = new Iterator[B] {
+ def hasNext = self.hasNext
+ def next() = f(self.next())
+ }
+
+ def flatMap[B](f: A => IterableOnce[B]): Iterator[B] = new Iterator[B] {
+ private var myCurrent: Iterator[B] = Iterator.empty
+ private def current = {
+ while (!myCurrent.hasNext && self.hasNext)
+ myCurrent = f(self.next()).iterator
+ myCurrent
+ }
+ def hasNext = current.hasNext
+ def next() = current.next()
+ }
+ def ++[B >: A](xs: IterableOnce[B]): Iterator[B] = new Iterator[B] {
+ private var myCurrent: Iterator[B] = self
+ private var first = true
+ private def current = {
+ if (!myCurrent.hasNext && first) {
+ myCurrent = xs.iterator
+ first = false
+ }
+ myCurrent
+ }
+ def hasNext = current.hasNext
+ def next() = current.next()
+ }
+ def drop(n: Int): Iterator[A] = {
+ var i = 0
+ while (i < n && hasNext) {
+ next()
+ i += 1
+ }
+ this
+ }
+ def zip[B](that: IterableOnce[B]): Iterator[(A, B)] = new Iterator[(A, B)] {
+ val thatIterator = that.iterator
+ def hasNext = self.hasNext && thatIterator.hasNext
+ def next() = (self.next(), thatIterator.next())
+ }
+ }
+
+ object Iterator {
+ val empty: Iterator[Nothing] = new Iterator[Nothing] {
+ def hasNext = false
+ def next = throw new NoSuchElementException("next on empty iterator")
+ }
+ def apply[A](xs: A*): Iterator[A] = new RandomAccessView[A] {
+ val start = 0
+ val end = xs.length
+ def apply(n: Int) = xs(n)
+ }.iterator
+ }
+}
+
diff --git a/tests/run/colltest4/CollectionTests_2.scala b/tests/run/colltest4/CollectionTests_2.scala
new file mode 100644
index 000000000..0961180ed
--- /dev/null
+++ b/tests/run/colltest4/CollectionTests_2.scala
@@ -0,0 +1,172 @@
+import Predef.{augmentString => _, wrapString => _, _}
+import scala.reflect.ClassTag
+
+object Test {
+ import strawman.collections._
+ import CollectionStrawMan5._
+
+ def seqOps(xs: Seq[Int]) = {
+ val x1 = xs.foldLeft("")(_ + _)
+ val y1: String = x1
+ val x2 = xs.foldRight("")(_ + _)
+ val y2: String = x2
+ val x3 = xs.indexWhere(_ % 2 == 0)
+ val y3: Int = x3
+ val x4 = xs.head
+ val y4: Int = x4
+ val x5 = xs.to(List)
+ val y5: List[Int] = x5
+ val (xs6, xs7) = xs.partition(_ % 2 == 0)
+ val ys6: Seq[Int] = xs6
+ val ys7: Seq[Int] = xs7
+ val xs8 = xs.drop(2)
+ val ys8: Seq[Int] = xs8
+ val xs9 = xs.map(_ >= 0)
+ val ys9: Seq[Boolean] = xs9
+ val xs10 = xs.flatMap(x => Cons(x, Cons(-x, Nil)))
+ val ys10: Seq[Int] = xs10
+ val xs11 = xs ++ xs
+ val ys11: Seq[Int] = xs11
+ val xs12 = xs ++ Nil
+ val ys12: Seq[Int] = xs12
+ val xs13 = Nil ++ xs
+ val ys13: Seq[Int] = xs13
+ val xs14 = xs ++ Cons("a", Nil)
+ val ys14: Seq[Any] = xs14
+ val xs15 = xs.zip(xs9)
+ val ys15: Seq[(Int, Boolean)] = xs15
+ val xs16 = xs.reverse
+ val ys16: Seq[Int] = xs16
+ println("-------")
+ println(x1)
+ println(x2)
+ println(x3)
+ println(x4)
+ println(x5)
+ println(xs6)
+ println(xs7)
+ println(xs8)
+ println(xs9)
+ println(xs10)
+ println(xs11)
+ println(xs12)
+ println(xs13)
+ println(xs14)
+ println(xs15)
+ println(xs16)
+ }
+
+ def viewOps(xs: View[Int]) = {
+ val x1 = xs.foldLeft("")(_ + _)
+ val y1: String = x1
+ val x2 = xs.foldRight("")(_ + _)
+ val y2: String = x2
+ val x3 = xs.indexWhere(_ % 2 == 0)
+ val y3: Int = x3
+ val x4 = xs.head
+ val y4: Int = x4
+ val x5 = xs.to(List)
+ val y5: List[Int] = x5
+ val (xs6, xs7) = xs.partition(_ % 2 == 0)
+ val ys6: View[Int] = xs6
+ val ys7: View[Int] = xs7
+ val xs8 = xs.drop(2)
+ val ys8: View[Int] = xs8
+ val xs9 = xs.map(_ >= 0)
+ val ys9: View[Boolean] = xs9
+ val xs10 = xs.flatMap(x => Cons(x, Cons(-x, Nil)))
+ val ys10: View[Int] = xs10
+ val xs11 = xs ++ xs
+ val ys11: View[Int] = xs11
+ val xs12 = xs ++ Nil
+ val ys12: View[Int] = xs12
+ val xs13 = Nil ++ xs
+ val ys13: List[Int] = xs13
+ val xs14 = xs ++ Cons("a", Nil)
+ val ys14: View[Any] = xs14
+ val xs15 = xs.zip(xs9)
+ val ys15: View[(Int, Boolean)] = xs15
+ println("-------")
+ println(x1)
+ println(x2)
+ println(x3)
+ println(x4)
+ println(x5)
+ println(xs6.to(List))
+ println(xs7.to(List))
+ println(xs8.to(List))
+ println(xs9.to(List))
+ println(xs10.to(List))
+ println(xs11.to(List))
+ println(xs12.to(List))
+ println(xs13.to(List))
+ println(xs14.to(List))
+ println(xs15.to(List))
+ }
+
+ def stringOps(xs: String) = {
+ val x1 = xs.foldLeft("")(_ + _)
+ val y1: String = x1
+ val x2 = xs.foldRight("")(_ + _)
+ val y2: String = x2
+ val x3 = xs.indexWhere(_ % 2 == 0)
+ val y3: Int = x3
+ val x4 = xs.head
+ val y4: Int = x4
+ val x5 = xs.to(List)
+ val y5: List[Char] = x5
+ val (xs6, xs7) = xs.partition(_ % 2 == 0)
+ val ys6: String = xs6
+ val ys7: String = xs7
+ val xs8 = xs.drop(2)
+ val ys8: String = xs8
+ val xs9 = xs.map(_ + 1) // !!! need a language change to make this work without the : Char
+ val ys9: Seq[Int] = xs9
+ val xs9a = xs.map(_.toUpper) // !!! need a language change to make this work without the : Char
+ val ys9a: String = xs9a
+ val xs10 = xs.flatMap((x: Char) => s"$x,$x")
+ val ys10: String = xs10
+ val xs11 = xs ++ xs
+ val ys11: String = xs11
+ val xs11a = xs ++ List('x', 'y') // Cons('x', Cons('y', Nil))
+ val ys11a: String = xs11a
+ val xs12 = xs ++ Nil
+ val ys12: String = xs12
+ val xs13 = Nil ++ xs.iterator
+ val ys13: List[Char] = xs13
+ val xs14 = xs ++ Cons("xyz", Nil)
+ val ys14: Seq[Any] = xs14
+ val xs15 = xs.zip(xs9)
+ val ys15: Seq[(Char, Int)] = xs15
+ println("-------")
+ println(x1)
+ println(x2)
+ println(x3)
+ println(x4)
+ println(x5)
+ println(xs6)
+ println(xs7)
+ println(xs8)
+ println(xs9)
+ println(xs9a)
+ println(xs10)
+ println(xs11)
+ println(xs11a)
+ println(xs12)
+ println(xs13)
+ println(xs14)
+ println(xs15)
+ }
+
+ def main(args: Array[String]) = {
+ val ints = Cons(1, Cons(2, Cons(3, Nil)))
+ val intsBuf = ints.to(ArrayBuffer)
+ val intsListBuf = ints.to(ListBuffer)
+ val intsView = ints.view
+ seqOps(ints)
+ seqOps(intsBuf)
+ seqOps(intsListBuf)
+ viewOps(intsView)
+ stringOps("abc")
+ }
+}
diff --git a/tests/run/colltest5.check b/tests/run/colltest5.check
new file mode 100644
index 000000000..984a0803b
--- /dev/null
+++ b/tests/run/colltest5.check
@@ -0,0 +1,85 @@
+-------
+123
+123
+1
+1
+Cons(1,Cons(2,Cons(3,Nil)))
+Cons(2,Nil)
+Cons(1,Cons(3,Nil))
+Cons(3,Nil)
+Cons(true,Cons(true,Cons(true,Nil)))
+Cons(1,Cons(-1,Cons(2,Cons(-2,Cons(3,Cons(-3,Nil))))))
+Cons(1,Cons(2,Cons(3,Cons(1,Cons(2,Cons(3,Nil))))))
+Cons(1,Cons(2,Cons(3,Nil)))
+Cons(1,Cons(2,Cons(3,Nil)))
+Cons(1,Cons(2,Cons(3,Cons(a,Nil))))
+Cons((1,true),Cons((2,true),Cons((3,true),Nil)))
+Cons(3,Cons(2,Cons(1,Nil)))
+-------
+123
+123
+1
+1
+Cons(1,Cons(2,Cons(3,Nil)))
+ArrayBuffer(2)
+ArrayBuffer(1, 3)
+ArrayBuffer(3)
+ArrayBuffer(true, true, true)
+ArrayBuffer(1, -1, 2, -2, 3, -3)
+ArrayBuffer(1, 2, 3, 1, 2, 3)
+ArrayBuffer(1, 2, 3)
+Cons(1,Cons(2,Cons(3,Nil)))
+ArrayBuffer(1, 2, 3, a)
+ArrayBuffer((1,true), (2,true), (3,true))
+ArrayBuffer(3, 2, 1)
+-------
+123
+123
+1
+1
+Cons(1,Cons(2,Cons(3,Nil)))
+ListBuffer(2)
+ListBuffer(1, 3)
+ListBuffer(3)
+ListBuffer(true, true, true)
+ListBuffer(1, -1, 2, -2, 3, -3)
+ListBuffer(1, 2, 3, 1, 2, 3)
+ListBuffer(1, 2, 3)
+Cons(1,Cons(2,Cons(3,Nil)))
+ListBuffer(1, 2, 3, a)
+ListBuffer((1,true), (2,true), (3,true))
+ListBuffer(3, 2, 1)
+-------
+123
+123
+1
+1
+Cons(1,Cons(2,Cons(3,Nil)))
+Cons(2,Nil)
+Cons(1,Cons(3,Nil))
+Cons(3,Nil)
+Cons(true,Cons(true,Cons(true,Nil)))
+Cons(1,Cons(-1,Cons(2,Cons(-2,Cons(3,Cons(-3,Nil))))))
+Cons(1,Cons(2,Cons(3,Cons(1,Cons(2,Cons(3,Nil))))))
+Cons(1,Cons(2,Cons(3,Nil)))
+Cons(1,Cons(2,Cons(3,Nil)))
+Cons(1,Cons(2,Cons(3,Cons(a,Nil))))
+Cons((1,true),Cons((2,true),Cons((3,true),Nil)))
+-------
+abc
+abc
+1
+a
+Cons(a,Cons(b,Cons(c,Nil)))
+b
+ac
+c
+Cons(98,Cons(99,Cons(100,Nil)))
+ABC
+a,ab,bc,c
+abcabc
+abcxy
+abc
+Cons(a,Cons(b,Cons(c,Nil)))
+Cons(a,Cons(b,Cons(c,Cons(xyz,Nil))))
+Cons((a,98),Cons((b,99),Cons((c,100),Nil)))
diff --git a/tests/run/colltest5/CollectionStrawMan5_1.scala b/tests/run/colltest5/CollectionStrawMan5_1.scala
new file mode 100644
index 000000000..1a89d9659
--- /dev/null
+++ b/tests/run/colltest5/CollectionStrawMan5_1.scala
@@ -0,0 +1,513 @@
+package strawman.collections
+
+import Predef.{augmentString => _, wrapString => _, _}
+import scala.reflect.ClassTag
+import annotation.unchecked.uncheckedVariance
+import annotation.tailrec
+
+/** A strawman architecture for new collections. It contains some
+ * example collection classes and methods with the intent to expose
+ * some key issues. It would be good to compare this to other
+ * implementations of the same functionality, to get an idea of the
+ * strengths and weaknesses of different collection architectures.
+ *
+ * For a test file, see tests/run/CollectionTests.scala.
+ */
+object CollectionStrawMan5 {
+
+ /* ------------ Base Traits -------------------------------- */
+
+ /** Iterator can be used only once */
+ trait IterableOnce[+A] {
+ def iterator: Iterator[A]
+ }
+
+ /** Base trait for instances that can construct a collection from an iterable */
+ trait FromIterable[+C[X] <: Iterable[X]] {
+ def fromIterable[B](it: Iterable[B]): C[B]
+ }
+
+ /** Base trait for companion objects of collections */
+ trait IterableFactory[+C[X] <: Iterable[X]] extends FromIterable[C] {
+ def empty[X]: C[X] = fromIterable(View.Empty)
+ def apply[A](xs: A*): C[A] = fromIterable(View.Elems(xs: _*))
+ }
+
+ /** Base trait for generic collections */
+ trait Iterable[+A] extends IterableOnce[A] with IterableLike[A, Iterable] {
+ protected def coll: Iterable[A] = this
+ def knownLength: Int = -1
+ }
+
+ /** Base trait for sequence collections */
+ trait Seq[+A] extends Iterable[A] with SeqLike[A, Seq] {
+ def apply(i: Int): A
+ def length: Int
+ }
+
+ /** Base trait for strict collections */
+ trait Buildable[+A, +To <: Iterable[A]] extends Iterable[A] {
+ protected[this] def newBuilder: Builder[A, To]
+ override def partition(p: A => Boolean): (To, To) = {
+ val l, r = newBuilder
+ iterator.foreach(x => (if (p(x)) l else r) += x)
+ (l.result, r.result)
+ }
+ // one might also override other transforms here to avoid generating
+ // iterators if it helps efficiency.
+ }
+
+ /** Base trait for collection builders */
+ trait Builder[-A, +To] {
+ def +=(x: A): this.type
+ def result: To
+
+ def ++=(xs: IterableOnce[A]): this.type = {
+ xs.iterator.foreach(+=)
+ this
+ }
+ }
+
+ /* ------------ Operations ----------------------------------- */
+
+ /** Base trait for Iterable operations
+ *
+ * VarianceNote
+ * ============
+ *
+ * We require that for all child classes of Iterable the variance of
+ * the child class and the variance of the `C` parameter passed to `IterableLike`
+ * are the same. We cannot express this since we lack variance polymorphism. That's
+ * why we have to resort at some places to write `C[A @uncheckedVariance]`.
+ *
+ */
+ trait IterableLike[+A, +C[X] <: Iterable[X]]
+ extends FromIterable[C]
+ with IterableOps[A]
+ with IterableMonoTransforms[A, C[A @uncheckedVariance]] // sound bcs of VarianceNote
+ with IterablePolyTransforms[A, C] {
+ protected[this] def fromLikeIterable(coll: Iterable[A]): C[A] = fromIterable(coll)
+ }
+
+ /** Base trait for Seq operations */
+ trait SeqLike[+A, +C[X] <: Seq[X]]
+ extends IterableLike[A, C] with SeqMonoTransforms[A, C[A @uncheckedVariance]] // sound bcs of VarianceNote
+
+ trait IterableOps[+A] extends Any {
+ def iterator: Iterator[A]
+ def foreach(f: A => Unit): Unit = iterator.foreach(f)
+ def foldLeft[B](z: B)(op: (B, A) => B): B = iterator.foldLeft(z)(op)
+ def foldRight[B](z: B)(op: (A, B) => B): B = iterator.foldRight(z)(op)
+ def indexWhere(p: A => Boolean): Int = iterator.indexWhere(p)
+ def isEmpty: Boolean = !iterator.hasNext
+ def head: A = iterator.next
+ def view: View[A] = View.fromIterator(iterator)
+ }
+
+ trait IterableMonoTransforms[+A, +Repr] extends Any {
+ protected def coll: Iterable[A]
+ protected[this] def fromLikeIterable(coll: Iterable[A]): Repr
+ def filter(p: A => Boolean): Repr = fromLikeIterable(View.Filter(coll, p))
+ def partition(p: A => Boolean): (Repr, Repr) = {
+ val pn = View.Partition(coll, p)
+ (fromLikeIterable(pn.left), fromLikeIterable(pn.right))
+ }
+ def drop(n: Int): Repr = fromLikeIterable(View.Drop(coll, n))
+ def to[C[X] <: Iterable[X]](fi: FromIterable[C]): C[A @uncheckedVariance] =
+ // variance seems sound because `to` could just as well have been added
+ // as a decorator. We should investigate this further to be sure.
+ fi.fromIterable(coll)
+ }
+
+ trait IterablePolyTransforms[+A, +C[A]] extends Any {
+ protected def coll: Iterable[A]
+ def fromIterable[B](coll: Iterable[B]): C[B]
+ def map[B](f: A => B): C[B] = fromIterable(View.Map(coll, f))
+ def flatMap[B](f: A => IterableOnce[B]): C[B] = fromIterable(View.FlatMap(coll, f))
+ def ++[B >: A](xs: IterableOnce[B]): C[B] = fromIterable(View.Concat(coll, xs))
+ def zip[B](xs: IterableOnce[B]): C[(A @uncheckedVariance, B)] = fromIterable(View.Zip(coll, xs))
+ // sound bcs of VarianceNote
+ }
+
+ trait SeqMonoTransforms[+A, +Repr] extends Any with IterableMonoTransforms[A, Repr] {
+ def reverse: Repr = {
+ var xs: List[A] = Nil
+ var it = coll.iterator
+ while (it.hasNext) xs = new Cons(it.next, xs)
+ fromLikeIterable(xs)
+ }
+ }
+
+ /* --------- Concrete collection types ------------------------------- */
+
+ /** Concrete collection type: List */
+ sealed trait List[+A] extends Seq[A] with SeqLike[A, List] with Buildable[A, List[A]] { self =>
+ def isEmpty: Boolean
+ def head: A
+ def tail: List[A]
+ def iterator = new Iterator[A] {
+ private[this] var current = self
+ def hasNext = !current.isEmpty
+ def next = { val r = current.head; current = current.tail; r }
+ }
+ def fromIterable[B](c: Iterable[B]): List[B] = List.fromIterable(c)
+ def apply(i: Int): A = {
+ require(!isEmpty)
+ if (i == 0) head else tail.apply(i - 1)
+ }
+ def length: Int =
+ if (isEmpty) 0 else 1 + tail.length
+ protected[this] def newBuilder = new ListBuffer[A]
+ def ++:[B >: A](prefix: List[B]): List[B] =
+ if (prefix.isEmpty) this
+ else Cons(prefix.head, prefix.tail ++: this)
+ override def ++[B >: A](xs: IterableOnce[B]): List[B] = xs match {
+ case xs: List[B] => this ++: xs
+ case _ => super.++(xs)
+ }
+ @tailrec final override def drop(n: Int) =
+ if (n > 0) tail.drop(n - 1) else this
+ }
+
+ case class Cons[+A](x: A, private[collections] var next: List[A @uncheckedVariance]) // sound because `next` is used only locally
+ extends List[A] {
+ override def isEmpty = false
+ override def head = x
+ def tail = next
+ }
+
+ case object Nil extends List[Nothing] {
+ override def isEmpty = true
+ override def head = ???
+ def tail = ???
+ }
+
+ object List extends IterableFactory[List] {
+ def fromIterable[B](coll: Iterable[B]): List[B] = coll match {
+ case coll: List[B] => coll
+ case _ => ListBuffer.fromIterable(coll).result
+ }
+ }
+
+ /** Concrete collection type: ListBuffer */
+ class ListBuffer[A] extends Seq[A] with SeqLike[A, ListBuffer] with Builder[A, List[A]] {
+ private var first, last: List[A] = Nil
+ private var aliased = false
+ def iterator = first.iterator
+ def fromIterable[B](coll: Iterable[B]) = ListBuffer.fromIterable(coll)
+ def apply(i: Int) = first.apply(i)
+ def length = first.length
+
+ private def copyElems(): Unit = {
+ val buf = ListBuffer.fromIterable(result)
+ first = buf.first
+ last = buf.last
+ aliased = false
+ }
+ def result = {
+ aliased = true
+ first
+ }
+ def +=(elem: A) = {
+ if (aliased) copyElems()
+ val last1 = Cons(elem, Nil)
+ last match {
+ case last: Cons[A] => last.next = last1
+ case _ => first = last1
+ }
+ last = last1
+ this
+ }
+ override def toString: String =
+ if (first.isEmpty) "ListBuffer()"
+ else {
+ val b = new StringBuilder("ListBuffer(").append(first.head)
+ first.tail.foldLeft(b)(_.append(", ").append(_)).append(")").toString
+ }
+ }
+
+ object ListBuffer extends IterableFactory[ListBuffer] {
+ def fromIterable[B](coll: Iterable[B]): ListBuffer[B] = new ListBuffer[B] ++= coll
+ }
+
+ /** Concrete collection type: ArrayBuffer */
+ class ArrayBuffer[A] private (initElems: Array[AnyRef], initLength: Int)
+ extends Seq[A] with SeqLike[A, ArrayBuffer] with Builder[A, ArrayBuffer[A]] {
+ def this() = this(new Array[AnyRef](16), 0)
+ private var elems: Array[AnyRef] = initElems
+ private var start = 0
+ private var end = initLength
+ def apply(n: Int) = elems(start + n).asInstanceOf[A]
+ def length = end - start
+ override def knownLength = length
+ override def view = new ArrayBufferView(elems, start, end)
+ def iterator = view.iterator
+ def fromIterable[B](it: Iterable[B]): ArrayBuffer[B] =
+ ArrayBuffer.fromIterable(it)
+ def +=(elem: A): this.type = {
+ if (end == elems.length) {
+ if (start > 0) {
+ Array.copy(elems, start, elems, 0, length)
+ end -= start
+ start = 0
+ }
+ else {
+ val newelems = new Array[AnyRef](end * 2)
+ Array.copy(elems, 0, newelems, 0, end)
+ elems = newelems
+ }
+ }
+ elems(end) = elem.asInstanceOf[AnyRef]
+ end += 1
+ this
+ }
+ def result = this
+ def trimStart(n: Int): Unit = start += (n max 0)
+ override def ++[B >: A](xs: IterableOnce[B]): ArrayBuffer[B] = xs match {
+ case xs: ArrayBuffer[B] =>
+ val elems = new Array[AnyRef](length + xs.length)
+ Array.copy(this.elems, this.start, elems, 0, this.length)
+ Array.copy(xs.elems, xs.start, elems, this.length, xs.length)
+ new ArrayBuffer(elems, elems.length)
+ case _ => super.++(xs)
+ }
+
+ override def toString = s"ArrayBuffer(${elems.slice(start, end).mkString(", ")})"
+ }
+
+ object ArrayBuffer extends IterableFactory[ArrayBuffer] {
+ def fromIterable[B](coll: Iterable[B]): ArrayBuffer[B] =
+ if (coll.knownLength >= 0) {
+ val elems = new Array[AnyRef](coll.knownLength)
+ val it = coll.iterator
+ for (i <- 0 until elems.length) elems(i) = it.next().asInstanceOf[AnyRef]
+ new ArrayBuffer[B](elems, elems.length)
+ }
+ else {
+ val buf = new ArrayBuffer[B]
+ val it = coll.iterator
+ while (it.hasNext) buf += it.next()
+ buf
+ }
+ }
+
+ class ArrayBufferView[A](val elems: Array[AnyRef], val start: Int, val end: Int) extends RandomAccessView[A] {
+ def apply(n: Int) = elems(start + n).asInstanceOf[A]
+ }
+
+ /** Concrete collection type: String */
+ implicit class StringOps(val s: String)
+ extends AnyVal with IterableOps[Char]
+ with SeqMonoTransforms[Char, String]
+ with IterablePolyTransforms[Char, List] {
+ protected def coll = new StringView(s)
+ def iterator = coll.iterator
+ protected def fromLikeIterable(coll: Iterable[Char]): String = {
+ val sb = new StringBuilder
+ for (ch <- coll) sb.append(ch)
+ sb.toString
+ }
+ def fromIterable[B](coll: Iterable[B]): List[B] = List.fromIterable(coll)
+ def map(f: Char => Char): String = {
+ val sb = new StringBuilder
+ for (ch <- s) sb.append(f(ch))
+ sb.toString
+ }
+ def flatMap(f: Char => String): String = {
+ val sb = new StringBuilder
+ for (ch <- s) sb.append(f(ch))
+ sb.toString
+ }
+ def ++(xs: IterableOnce[Char]): String = {
+ val sb = new StringBuilder(s)
+ for (ch <- xs.iterator) sb.append(ch)
+ sb.toString
+ }
+ def ++(xs: String): String = s + xs
+ }
+
+ case class StringView(s: String) extends RandomAccessView[Char] {
+ val start = 0
+ val end = s.length
+ def apply(n: Int) = s.charAt(n)
+ }
+
+/* ---------- Views -------------------------------------------------------*/
+
+ /** Concrete collection type: View */
+ trait View[+A] extends Iterable[A] with IterableLike[A, View] {
+ override def view = this
+ override def fromIterable[B](c: Iterable[B]): View[B] = c match {
+ case c: View[B] => c
+ case _ => View.fromIterator(c.iterator)
+ }
+ }
+
+ /** View defined in terms of indexing a range */
+ trait RandomAccessView[+A] extends View[A] {
+ def start: Int
+ def end: Int
+ def apply(i: Int): A
+ def iterator: Iterator[A] = new Iterator[A] {
+ private var current = start
+ def hasNext = current < end
+ def next: A = {
+ val r = apply(current)
+ current += 1
+ r
+ }
+ }
+ override def knownLength = end - start max 0
+ }
+
+ object View {
+ def fromIterator[A](it: => Iterator[A]): View[A] = new View[A] {
+ def iterator = it
+ }
+ case object Empty extends View[Nothing] {
+ def iterator = Iterator.empty
+ override def knownLength = 0
+ }
+ case class Elems[A](xs: A*) extends View[A] {
+ def iterator = Iterator(xs: _*)
+ override def knownLength = xs.length
+ }
+ case class Filter[A](val underlying: Iterable[A], p: A => Boolean) extends View[A] {
+ def iterator = underlying.iterator.filter(p)
+ }
+ case class Partition[A](val underlying: Iterable[A], p: A => Boolean) {
+ val left = Partitioned(this, true)
+ val right = Partitioned(this, false)
+ }
+ case class Partitioned[A](partition: Partition[A], cond: Boolean) extends View[A] {
+ def iterator = partition.underlying.iterator.filter(x => partition.p(x) == cond)
+ }
+ case class Drop[A](underlying: Iterable[A], n: Int) extends View[A] {
+ def iterator = underlying.iterator.drop(n)
+ override def knownLength =
+ if (underlying.knownLength >= 0) underlying.knownLength - n max 0 else -1
+ }
+ case class Map[A, B](underlying: Iterable[A], f: A => B) extends View[B] {
+ def iterator = underlying.iterator.map(f)
+ override def knownLength = underlying.knownLength
+ }
+ case class FlatMap[A, B](underlying: Iterable[A], f: A => IterableOnce[B]) extends View[B] {
+ def iterator = underlying.iterator.flatMap(f)
+ }
+ case class Concat[A](underlying: Iterable[A], other: IterableOnce[A]) extends View[A] {
+ def iterator = underlying.iterator ++ other
+ override def knownLength = other match {
+ case other: Iterable[_] if underlying.knownLength >= 0 && other.knownLength >= 0 =>
+ underlying.knownLength + other.knownLength
+ case _ =>
+ -1
+ }
+ }
+ case class Zip[A, B](underlying: Iterable[A], other: IterableOnce[B]) extends View[(A, B)] {
+ def iterator = underlying.iterator.zip(other)
+ override def knownLength = other match {
+ case other: Iterable[_] if underlying.knownLength >= 0 && other.knownLength >= 0 =>
+ underlying.knownLength min other.knownLength
+ case _ =>
+ -1
+ }
+ }
+ }
+
+/* ---------- Iterators ---------------------------------------------------*/
+
+ /** A core Iterator class */
+ trait Iterator[+A] extends IterableOnce[A] { self =>
+ def hasNext: Boolean
+ def next(): A
+ def iterator = this
+ def foldLeft[B](z: B)(op: (B, A) => B): B =
+ if (hasNext) foldLeft(op(z, next))(op) else z
+ def foldRight[B](z: B)(op: (A, B) => B): B =
+ if (hasNext) op(next(), foldRight(z)(op)) else z
+ def foreach(f: A => Unit): Unit =
+ while (hasNext) f(next())
+ def indexWhere(p: A => Boolean): Int = {
+ var i = 0
+ while (hasNext) {
+ if (p(next())) return i
+ i += 1
+ }
+ -1
+ }
+ def filter(p: A => Boolean): Iterator[A] = new Iterator[A] {
+ private var hd: A = _
+ private var hdDefined: Boolean = false
+
+ def hasNext: Boolean = hdDefined || {
+ do {
+ if (!self.hasNext) return false
+ hd = self.next()
+ } while (!p(hd))
+ hdDefined = true
+ true
+ }
+
+ def next() =
+ if (hasNext) {
+ hdDefined = false
+ hd
+ }
+ else Iterator.empty.next()
+ }
+
+ def map[B](f: A => B): Iterator[B] = new Iterator[B] {
+ def hasNext = self.hasNext
+ def next() = f(self.next())
+ }
+
+ def flatMap[B](f: A => IterableOnce[B]): Iterator[B] = new Iterator[B] {
+ private var myCurrent: Iterator[B] = Iterator.empty
+ private def current = {
+ while (!myCurrent.hasNext && self.hasNext)
+ myCurrent = f(self.next()).iterator
+ myCurrent
+ }
+ def hasNext = current.hasNext
+ def next() = current.next()
+ }
+ def ++[B >: A](xs: IterableOnce[B]): Iterator[B] = new Iterator[B] {
+ private var myCurrent: Iterator[B] = self
+ private var first = true
+ private def current = {
+ if (!myCurrent.hasNext && first) {
+ myCurrent = xs.iterator
+ first = false
+ }
+ myCurrent
+ }
+ def hasNext = current.hasNext
+ def next() = current.next()
+ }
+ def drop(n: Int): Iterator[A] = {
+ var i = 0
+ while (i < n && hasNext) {
+ next()
+ i += 1
+ }
+ this
+ }
+ def zip[B](that: IterableOnce[B]): Iterator[(A, B)] = new Iterator[(A, B)] {
+ val thatIterator = that.iterator
+ def hasNext = self.hasNext && thatIterator.hasNext
+ def next() = (self.next(), thatIterator.next())
+ }
+ }
+
+ object Iterator {
+ val empty: Iterator[Nothing] = new Iterator[Nothing] {
+ def hasNext = false
+ def next = throw new NoSuchElementException("next on empty iterator")
+ }
+ def apply[A](xs: A*): Iterator[A] = new RandomAccessView[A] {
+ val start = 0
+ val end = xs.length
+ def apply(n: Int) = xs(n)
+ }.iterator
+ }
+}
diff --git a/tests/run/colltest5/CollectionTests_2.scala b/tests/run/colltest5/CollectionTests_2.scala
new file mode 100644
index 000000000..0961180ed
--- /dev/null
+++ b/tests/run/colltest5/CollectionTests_2.scala
@@ -0,0 +1,172 @@
+import Predef.{augmentString => _, wrapString => _, _}
+import scala.reflect.ClassTag
+
+object Test {
+ import strawman.collections._
+ import CollectionStrawMan5._
+
+ def seqOps(xs: Seq[Int]) = {
+ val x1 = xs.foldLeft("")(_ + _)
+ val y1: String = x1
+ val x2 = xs.foldRight("")(_ + _)
+ val y2: String = x2
+ val x3 = xs.indexWhere(_ % 2 == 0)
+ val y3: Int = x3
+ val x4 = xs.head
+ val y4: Int = x4
+ val x5 = xs.to(List)
+ val y5: List[Int] = x5
+ val (xs6, xs7) = xs.partition(_ % 2 == 0)
+ val ys6: Seq[Int] = xs6
+ val ys7: Seq[Int] = xs7
+ val xs8 = xs.drop(2)
+ val ys8: Seq[Int] = xs8
+ val xs9 = xs.map(_ >= 0)
+ val ys9: Seq[Boolean] = xs9
+ val xs10 = xs.flatMap(x => Cons(x, Cons(-x, Nil)))
+ val ys10: Seq[Int] = xs10
+ val xs11 = xs ++ xs
+ val ys11: Seq[Int] = xs11
+ val xs12 = xs ++ Nil
+ val ys12: Seq[Int] = xs12
+ val xs13 = Nil ++ xs
+ val ys13: Seq[Int] = xs13
+ val xs14 = xs ++ Cons("a", Nil)
+ val ys14: Seq[Any] = xs14
+ val xs15 = xs.zip(xs9)
+ val ys15: Seq[(Int, Boolean)] = xs15
+ val xs16 = xs.reverse
+ val ys16: Seq[Int] = xs16
+ println("-------")
+ println(x1)
+ println(x2)
+ println(x3)
+ println(x4)
+ println(x5)
+ println(xs6)
+ println(xs7)
+ println(xs8)
+ println(xs9)
+ println(xs10)
+ println(xs11)
+ println(xs12)
+ println(xs13)
+ println(xs14)
+ println(xs15)
+ println(xs16)
+ }
+
+ def viewOps(xs: View[Int]) = {
+ val x1 = xs.foldLeft("")(_ + _)
+ val y1: String = x1
+ val x2 = xs.foldRight("")(_ + _)
+ val y2: String = x2
+ val x3 = xs.indexWhere(_ % 2 == 0)
+ val y3: Int = x3
+ val x4 = xs.head
+ val y4: Int = x4
+ val x5 = xs.to(List)
+ val y5: List[Int] = x5
+ val (xs6, xs7) = xs.partition(_ % 2 == 0)
+ val ys6: View[Int] = xs6
+ val ys7: View[Int] = xs7
+ val xs8 = xs.drop(2)
+ val ys8: View[Int] = xs8
+ val xs9 = xs.map(_ >= 0)
+ val ys9: View[Boolean] = xs9
+ val xs10 = xs.flatMap(x => Cons(x, Cons(-x, Nil)))
+ val ys10: View[Int] = xs10
+ val xs11 = xs ++ xs
+ val ys11: View[Int] = xs11
+ val xs12 = xs ++ Nil
+ val ys12: View[Int] = xs12
+ val xs13 = Nil ++ xs
+ val ys13: List[Int] = xs13
+ val xs14 = xs ++ Cons("a", Nil)
+ val ys14: View[Any] = xs14
+ val xs15 = xs.zip(xs9)
+ val ys15: View[(Int, Boolean)] = xs15
+ println("-------")
+ println(x1)
+ println(x2)
+ println(x3)
+ println(x4)
+ println(x5)
+ println(xs6.to(List))
+ println(xs7.to(List))
+ println(xs8.to(List))
+ println(xs9.to(List))
+ println(xs10.to(List))
+ println(xs11.to(List))
+ println(xs12.to(List))
+ println(xs13.to(List))
+ println(xs14.to(List))
+ println(xs15.to(List))
+ }
+
+ def stringOps(xs: String) = {
+ val x1 = xs.foldLeft("")(_ + _)
+ val y1: String = x1
+ val x2 = xs.foldRight("")(_ + _)
+ val y2: String = x2
+ val x3 = xs.indexWhere(_ % 2 == 0)
+ val y3: Int = x3
+ val x4 = xs.head
+ val y4: Int = x4
+ val x5 = xs.to(List)
+ val y5: List[Char] = x5
+ val (xs6, xs7) = xs.partition(_ % 2 == 0)
+ val ys6: String = xs6
+ val ys7: String = xs7
+ val xs8 = xs.drop(2)
+ val ys8: String = xs8
+ val xs9 = xs.map(_ + 1) // !!! need a language change to make this work without the : Char
+ val ys9: Seq[Int] = xs9
+ val xs9a = xs.map(_.toUpper) // !!! need a language change to make this work without the : Char
+ val ys9a: String = xs9a
+ val xs10 = xs.flatMap((x: Char) => s"$x,$x")
+ val ys10: String = xs10
+ val xs11 = xs ++ xs
+ val ys11: String = xs11
+ val xs11a = xs ++ List('x', 'y') // Cons('x', Cons('y', Nil))
+ val ys11a: String = xs11a
+ val xs12 = xs ++ Nil
+ val ys12: String = xs12
+ val xs13 = Nil ++ xs.iterator
+ val ys13: List[Char] = xs13
+ val xs14 = xs ++ Cons("xyz", Nil)
+ val ys14: Seq[Any] = xs14
+ val xs15 = xs.zip(xs9)
+ val ys15: Seq[(Char, Int)] = xs15
+ println("-------")
+ println(x1)
+ println(x2)
+ println(x3)
+ println(x4)
+ println(x5)
+ println(xs6)
+ println(xs7)
+ println(xs8)
+ println(xs9)
+ println(xs9a)
+ println(xs10)
+ println(xs11)
+ println(xs11a)
+ println(xs12)
+ println(xs13)
+ println(xs14)
+ println(xs15)
+ }
+
+ def main(args: Array[String]) = {
+ val ints = Cons(1, Cons(2, Cons(3, Nil)))
+ val intsBuf = ints.to(ArrayBuffer)
+ val intsListBuf = ints.to(ListBuffer)
+ val intsView = ints.view
+ seqOps(ints)
+ seqOps(intsBuf)
+ seqOps(intsListBuf)
+ viewOps(intsView)
+ stringOps("abc")
+ }
+}