From 78239daf86cf6d45fec46f259e55e1fdbd963046 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 23 Jul 2015 17:13:35 +0200 Subject: [PATCH 01/22] Generalize rules for isSubType between NamedTypes. Can assume P#T <: Q#T if P <: Q. This follows from the rules how we expand # to existentials. --- src/dotty/tools/dotc/core/TypeComparer.scala | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index e3e5f3960eec..d98ff1f03afc 100644 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -154,24 +154,10 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { case tp1: NamedType => val sym1 = tp1.symbol (if ((sym1 ne NoSymbol) && (sym1 eq tp2.symbol)) - ctx.erasedTypes || sym1.isStaticOwner || - { // Implements: A # X <: B # X - // if either A =:= B (i.e. A <: B and B <: A), or the following three conditions hold: - // 1. X is a class type, - // 2. B is a class type without abstract type members. - // 3. A <: B. - // Dealiasing is taken care of elsewhere. - val pre1 = tp1.prefix - val pre2 = tp2.prefix - isSameType(pre1, pre2) || - sym1.isClass && - pre2.classSymbol.exists && - pre2.abstractTypeMembers.isEmpty && - isSubType(pre1, pre2) - } + ctx.erasedTypes || sym1.isStaticOwner || isSubType(tp1.prefix, tp2.prefix) else (tp1.name eq tp2.name) && - isSameType(tp1.prefix, tp2.prefix) && + isSubType(tp1.prefix, tp2.prefix) && (tp1.signature == tp2.signature) && !tp1.isInstanceOf[WithFixedSym] && !tp2.isInstanceOf[WithFixedSym] From fe714ed2b6aaca41f1e6681b217022b301f3049d Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 23 Jul 2015 17:42:55 +0200 Subject: [PATCH 02/22] Change some occurrences of <:< to frozen_<:< Some subtype tests should not instantiate type variables, in particular those having to do with & and |. --- src/dotty/tools/dotc/core/Denotations.scala | 4 ++-- .../tools/dotc/core/TypeApplications.scala | 4 ++-- src/dotty/tools/dotc/core/Types.scala | 17 +++++++++++------ 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/dotty/tools/dotc/core/Denotations.scala b/src/dotty/tools/dotc/core/Denotations.scala index fff4478037a4..b223a80865e3 100644 --- a/src/dotty/tools/dotc/core/Denotations.scala +++ b/src/dotty/tools/dotc/core/Denotations.scala @@ -339,8 +339,8 @@ object Denotations { val info1 = denot1.info val info2 = denot2.info val sameSym = sym1 eq sym2 - if (sameSym && info1 <:< info2) denot2 - else if (sameSym && info2 <:< info1) denot1 + if (sameSym && (info1 frozen_<:< info2)) denot2 + else if (sameSym && (info2 frozen_<:< info1)) denot1 else { val jointSym = if (sameSym) sym1 diff --git a/src/dotty/tools/dotc/core/TypeApplications.scala b/src/dotty/tools/dotc/core/TypeApplications.scala index 927c4fcc5522..47935f79e190 100644 --- a/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/src/dotty/tools/dotc/core/TypeApplications.scala @@ -497,7 +497,7 @@ class TypeApplications(val self: Type) extends AnyVal { val boundss = new mutable.ListBuffer[TypeBounds] for (sym <- boundSyms) { val bounds = sym.info.bounds - if (!(TypeBounds.empty <:< bounds)) { + if (!(TypeBounds.empty frozen_<:< bounds)) { boundNames += sym.name boundss += bounds } @@ -574,7 +574,7 @@ class TypeApplications(val self: Type) extends AnyVal { // we have a binding T = Lambda$XYZ{...}.this.hk$i where hk$i names the current `tparam`. val pcore = etaCore(tp.parent, otherParams) val hkBounds = self.member(rname).info.bounds - if (TypeBounds.empty <:< hkBounds) pcore + if (TypeBounds.empty frozen_<:< hkBounds) pcore else tp.derivedRefinedType(pcore, tp.refinedName, hkBounds) case _ => val pcore = etaCore(tp.parent, tparams) diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index bce57ae23757..8ad694ea2c12 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -595,6 +595,11 @@ object Types { ctx.typeComparer.topLevelSubType(this, that) } + /** Is this type a subtype of that type? */ + final def frozen_<:<(that: Type)(implicit ctx: Context): Boolean = track("frozen_<:<") { + ctx.typeComparer.isSubTypeWhenFrozen(this, that) + } + /** Is this type the same as that type? * This is the case iff `this <:< that` and `that <:< this`. */ @@ -625,9 +630,9 @@ object Types { case ExprType(_) | MethodType(Nil, _) => tp.resultType case _ => tp } - this <:< that || { + (this frozen_<:< that) || { val rthat = result(that) - (rthat ne that) && result(this) <:< rthat + (rthat ne that) && (result(this) frozen_<:< rthat) } } @@ -2652,13 +2657,13 @@ object Types { } def & (that: TypeBounds)(implicit ctx: Context): TypeBounds = - if (this.lo <:< that.lo && that.hi <:< this.hi) that - else if (that.lo <:< this.lo && this.hi <:< that.hi) this + if ((this.lo frozen_<:< that.lo) && (that.hi frozen_<:< this.hi)) that + else if ((that.lo frozen_<:< this.lo) && (this.hi frozen_<:< that.hi)) this else TypeBounds(this.lo | that.lo, this.hi & that.hi) def | (that: TypeBounds)(implicit ctx: Context): TypeBounds = - if (this.lo <:< that.lo && that.hi <:< this.hi) this - else if (that.lo <:< this.lo && this.hi <:< that.hi) that + if ((this.lo frozen_<:< that.lo) && (that.hi frozen_<:< this.hi)) this + else if ((that.lo frozen_<:< this.lo) && (this.hi frozen_<:< that.hi)) that else TypeBounds(this.lo & that.lo, this.hi | that.hi) override def & (that: Type)(implicit ctx: Context) = that match { From 3604a738654effc20be1d31b0e7af3d0847114b4 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 21 Sep 2015 22:14:36 +0200 Subject: [PATCH 03/22] Replace withMode by addMode withMode sets the whole mode, nit an individual bits. This was used wrongly in several places. Make this less of a trap by renaming withMode -> withModeBits. --- src/dotty/tools/dotc/ast/tpd.scala | 4 ++-- src/dotty/tools/dotc/core/Contexts.scala | 8 ++++---- src/dotty/tools/dotc/printing/PlainPrinter.scala | 2 +- src/dotty/tools/dotc/transform/TreeChecker.scala | 2 +- test/test/DeSugarTest.scala | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index 29a31375b0c9..14a36f39848c 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -626,7 +626,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { loop(from.owner, from :: froms, to :: tos) else { //println(i"change owner ${from :: froms}%, % ==> $tos of $tree") - new TreeTypeMap(oldOwners = from :: froms, newOwners = tos)(ctx.withMode(Mode.FutureDefsOK)).apply(tree) + new TreeTypeMap(oldOwners = from :: froms, newOwners = tos)(ctx.addMode(Mode.FutureDefsOK)).apply(tree) } } loop(from, Nil, to :: Nil) @@ -652,7 +652,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { traverseChildren(tree) } } - traverser.traverse(tree)(ctx.withMode(Mode.FutureDefsOK)) + traverser.traverse(tree)(ctx.addMode(Mode.FutureDefsOK)) tree } diff --git a/src/dotty/tools/dotc/core/Contexts.scala b/src/dotty/tools/dotc/core/Contexts.scala index 206ef9d8b9d4..f9d64b2cc56f 100644 --- a/src/dotty/tools/dotc/core/Contexts.scala +++ b/src/dotty/tools/dotc/core/Contexts.scala @@ -439,12 +439,12 @@ object Contexts { } implicit class ModeChanges(val c: Context) extends AnyVal { - final def withMode(mode: Mode): Context = + final def withModeBits(mode: Mode): Context = if (mode != c.mode) c.fresh.setMode(mode) else c - final def addMode(mode: Mode): Context = withMode(c.mode | mode) - final def maskMode(mode: Mode): Context = withMode(c.mode & mode) - final def retractMode(mode: Mode): Context = withMode(c.mode &~ mode) + final def addMode(mode: Mode): Context = withModeBits(c.mode | mode) + final def maskMode(mode: Mode): Context = withModeBits(c.mode & mode) + final def retractMode(mode: Mode): Context = withModeBits(c.mode &~ mode) } implicit class FreshModeChanges(val c: FreshContext) extends AnyVal { diff --git a/src/dotty/tools/dotc/printing/PlainPrinter.scala b/src/dotty/tools/dotc/printing/PlainPrinter.scala index 45928af4b7bd..5daef9fb8657 100644 --- a/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -166,7 +166,7 @@ class PlainPrinter(_ctx: Context) extends Printer { else { val constr = ctx.typerState.constraint val bounds = - if (constr.contains(tp)) constr.fullBounds(tp.origin) + if (constr.contains(tp)) constr.fullBounds(tp.origin)(ctx.addMode(Mode.Printing)) else TypeBounds.empty "(" ~ toText(tp.origin) ~ "?" ~ toText(bounds) ~ ")" } diff --git a/src/dotty/tools/dotc/transform/TreeChecker.scala b/src/dotty/tools/dotc/transform/TreeChecker.scala index f046226d10b8..2296ae65881b 100644 --- a/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -66,7 +66,7 @@ class TreeChecker extends Phase with SymTransformer { val cur = symd.linkedClass val prev = ctx.atPhase(ctx.phase.prev) { ct => { - implicit val ctx: Context = ct.withMode(Mode.FutureDefsOK) + implicit val ctx: Context = ct.addMode(Mode.FutureDefsOK) symd.symbol.linkedClass } } diff --git a/test/test/DeSugarTest.scala b/test/test/DeSugarTest.scala index 2c74abae73ad..44360d657cdc 100644 --- a/test/test/DeSugarTest.scala +++ b/test/test/DeSugarTest.scala @@ -35,7 +35,7 @@ class DeSugarTest extends ParserTest { def transform(trees: List[Tree], mode: Mode)(implicit ctx: Context): List[Tree] = withMode(mode) { transform(trees) } override def transform(tree: Tree)(implicit ctx: Context): Tree = { - val tree1 = desugar(tree)(ctx.withMode(curMode)) + val tree1 = desugar(tree)(ctx.withModeBits(curMode)) tree1 match { case TypedSplice(t) => tree1 From e14f23bfea5d3211d89a827567759350e1b79a03 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 21 Sep 2015 22:20:02 +0200 Subject: [PATCH 04/22] Fix compilation error. --- src/dotty/tools/dotc/core/ConstraintHandling.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/core/ConstraintHandling.scala b/src/dotty/tools/dotc/core/ConstraintHandling.scala index 544304e8a18d..6f0377a4d8de 100644 --- a/src/dotty/tools/dotc/core/ConstraintHandling.scala +++ b/src/dotty/tools/dotc/core/ConstraintHandling.scala @@ -104,7 +104,7 @@ trait ConstraintHandling { up.forall(addOneBound(_, lo, isUpper = false)) } - protected final def isSubTypeWhenFrozen(tp1: Type, tp2: Type): Boolean = { + final def isSubTypeWhenFrozen(tp1: Type, tp2: Type): Boolean = { val saved = frozenConstraint frozenConstraint = true try isSubType(tp1, tp2) From 22417e217d7e5656af3ded79411db29589239f27 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 21 Sep 2015 22:21:06 +0200 Subject: [PATCH 05/22] Don't log when in Printing mode. Logging while printing messes up the recursion counts. --- src/dotty/tools/dotc/reporting/Reporter.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/reporting/Reporter.scala b/src/dotty/tools/dotc/reporting/Reporter.scala index 7adeeac1cb7c..766009a8b33f 100644 --- a/src/dotty/tools/dotc/reporting/Reporter.scala +++ b/src/dotty/tools/dotc/reporting/Reporter.scala @@ -159,7 +159,8 @@ trait Reporting { this: Context => logctx.log(s"${base.indentTab * base.indent}${trailing(result)}$note") finalized = true } - try { + if (ctx.mode.is(Mode.Printing)) op + else try { logctx.log(s"${base.indentTab * base.indent}$leading") base.indent += 1 val res = op From 7352fc4a6fd7b3637bb7eb5f79f8a14f4e46a2b1 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 24 Sep 2015 16:16:22 +0200 Subject: [PATCH 06/22] underlyingChain -> underlyingIterator make successive underlying values an iterator (so that we do not run into a stackoverflow in case of cycles). --- src/dotty/tools/dotc/core/Types.scala | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 8ad694ea2c12..e4f228915a58 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -806,12 +806,18 @@ object Types { case _ => NoType } - /** The chain of underlying types as long as type is a TypeProxy. + /** The iterator of underlying types as long as type is a TypeProxy. * Useful for diagnostics */ - def underlyingChain(implicit ctx: Context): List[Type] = this match { - case tp: TypeProxy => tp :: tp.underlying.underlyingChain - case _ => Nil + def underlyingIterator(implicit ctx: Context): Iterator[Type] = new Iterator[Type] { + var current = Type.this + var hasNext = true + def next = { + val res = current + hasNext = current.isInstanceOf[TypeProxy] + if (hasNext) current = current.asInstanceOf[TypeProxy].underlying + res + } } /** A prefix-less refined this or a termRef to a new skolem symbol From c6b21267c1f96cf8708689319f884d7d5cc04284 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 24 Sep 2015 16:20:31 +0200 Subject: [PATCH 07/22] Restore state when detecting a baseTypeRef cycle. --- src/dotty/tools/dotc/core/SymDenotations.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index 2ce2b8d20db3..d5ab09930011 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1587,6 +1587,7 @@ object SymDenotations { if (isCachable(tp)) baseTypeRefCache.put(tp, basetp) else baseTypeRefCache.remove(tp) } else if (basetp == NoPrefix) { + baseTypeRefCache.put(tp, null) throw CyclicReference(this) } basetp From 3f4c6a2b63bf94526792d108eaffc761d7d19ee5 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 24 Sep 2015 16:20:57 +0200 Subject: [PATCH 08/22] Slight code refactoring Avoid redundant computations when already in printing mode. --- src/dotty/tools/dotc/reporting/Reporter.scala | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/dotty/tools/dotc/reporting/Reporter.scala b/src/dotty/tools/dotc/reporting/Reporter.scala index 766009a8b33f..086575fb4da0 100644 --- a/src/dotty/tools/dotc/reporting/Reporter.scala +++ b/src/dotty/tools/dotc/reporting/Reporter.scala @@ -149,18 +149,19 @@ trait Reporting { this: Context => else traceIndented[T](s"==> $question?", (res: Any) => s"<== $question = ${resStr(res)}")(op) } - def traceIndented[T](leading: => String, trailing: Any => String)(op: => T): T = { - var finalized = false - var logctx = this - while (logctx.reporter.isInstanceOf[StoreReporter]) logctx = logctx.outer - def finalize(result: Any, note: String) = - if (!finalized) { - base.indent -= 1 - logctx.log(s"${base.indentTab * base.indent}${trailing(result)}$note") - finalized = true - } + def traceIndented[T](leading: => String, trailing: Any => String)(op: => T): T = if (ctx.mode.is(Mode.Printing)) op - else try { + else { + var finalized = false + var logctx = this + while (logctx.reporter.isInstanceOf[StoreReporter]) logctx = logctx.outer + def finalize(result: Any, note: String) = + if (!finalized) { + base.indent -= 1 + logctx.log(s"${base.indentTab * base.indent}${trailing(result)}$note") + finalized = true + } + try { logctx.log(s"${base.indentTab * base.indent}$leading") base.indent += 1 val res = op From 6288a1bb35f8167ad3ba8784fb9ac95e7fbde7e7 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 24 Sep 2015 16:23:12 +0200 Subject: [PATCH 09/22] Implement toString for OrderingConstraint We had problems printing constraints which are ill-formed, because the basic operations & , | cause exceptions themselves. toString serves as a fallback if show does not work. --- .../tools/dotc/core/OrderingConstraint.scala | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/core/OrderingConstraint.scala b/src/dotty/tools/dotc/core/OrderingConstraint.scala index 7e27ee628174..e818862cbbad 100644 --- a/src/dotty/tools/dotc/core/OrderingConstraint.scala +++ b/src/dotty/tools/dotc/core/OrderingConstraint.scala @@ -129,7 +129,6 @@ class OrderingConstraint(private val boundsMap: ParamBounds, type This = OrderingConstraint - // ----------- Basic indices -------------------------------------------------- /** The number of type parameters in the given entry array */ @@ -576,4 +575,22 @@ class OrderingConstraint(private val boundsMap: ParamBounds, } Text.lines(List(header, uninstVarsText, constrainedText, boundsText, orderingText, ")")) } + + override def toString: String = { + def entryText(tp: Type): String = tp match { + case tp: TypeBounds => tp.toString + case _ =>" := " + tp + } + val constrainedText = + " constrained types = " + domainPolys.mkString("\n") + val boundsText = + " bounds = " + { + val assocs = + for (param <- domainParams) + yield + param.binder.paramNames(param.paramNum) + ": " + entryText(entry(param)) + assocs.mkString("\n") + } + constrainedText + "\n" + boundsText + } } From 424fab79e56d376acbc2cdb5a203a527d0b2248d Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 24 Sep 2015 16:42:14 +0200 Subject: [PATCH 10/22] Opportunistically merge A & A -> A and B | B -> B in <:< Cases like these (in fact one of the operads was a type variable the other its underlying polyparam) arose in pos/overloads.scala and caused deep subtype recursions. --- src/dotty/tools/dotc/core/TypeComparer.scala | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index d98ff1f03afc..0c736078b797 100644 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -215,6 +215,9 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { compareSuper case AndType(tp21, tp22) => isSubType(tp1, tp21) && isSubType(tp1, tp22) + case OrType(tp21, tp22) => + if (tp21.stripTypeVar eq tp22.stripTypeVar) isSubType(tp1, tp21) + else thirdTry(tp1, tp2) case TypeErasure.ErasedValueType(cls2, underlying2) => def compareErasedValueType = tp1 match { case TypeErasure.ErasedValueType(cls1, underlying1) => @@ -277,6 +280,9 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { isSubType(tp1.ref, tp2) case tp1: AnnotatedType => isSubType(tp1.tpe, tp2) + case AndType(tp11, tp12) => + if (tp11.stripTypeVar eq tp12.stripTypeVar) isSubType(tp11, tp2) + else thirdTry(tp1, tp2) case OrType(tp11, tp12) => isSubType(tp11, tp2) && isSubType(tp12, tp2) case ErrorType => From 2d6d432001bb237897918a05bd3e82be87dc8a11 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 24 Sep 2015 17:48:21 +0200 Subject: [PATCH 11/22] Fix typo in previous commit. --- src/dotty/tools/dotc/core/TypeComparer.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index 0c736078b797..8b2eb9cbffb2 100644 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -217,7 +217,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { isSubType(tp1, tp21) && isSubType(tp1, tp22) case OrType(tp21, tp22) => if (tp21.stripTypeVar eq tp22.stripTypeVar) isSubType(tp1, tp21) - else thirdTry(tp1, tp2) + else secondTry(tp1, tp2) case TypeErasure.ErasedValueType(cls2, underlying2) => def compareErasedValueType = tp1 match { case TypeErasure.ErasedValueType(cls1, underlying1) => From 241065192926524292f950288518533429297666 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 24 Sep 2015 16:43:40 +0200 Subject: [PATCH 12/22] Freeze constraints when checking parameter matching and subsumption. Checking whether two alternatives are the same should not unify them by instantiating type variables. --- src/dotty/tools/dotc/core/ConstraintHandling.scala | 8 ++++++++ src/dotty/tools/dotc/core/TypeComparer.scala | 6 +++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/dotty/tools/dotc/core/ConstraintHandling.scala b/src/dotty/tools/dotc/core/ConstraintHandling.scala index 6f0377a4d8de..577b958c848e 100644 --- a/src/dotty/tools/dotc/core/ConstraintHandling.scala +++ b/src/dotty/tools/dotc/core/ConstraintHandling.scala @@ -23,6 +23,7 @@ trait ConstraintHandling { implicit val ctx: Context protected def isSubType(tp1: Type, tp2: Type): Boolean + protected def isSameType(tp1: Type, tp2: Type): Boolean val state: TyperState import state.constraint @@ -111,6 +112,13 @@ trait ConstraintHandling { finally frozenConstraint = saved } + final def isSameTypeWhenFrozen(tp1: Type, tp2: Type): Boolean = { + val saved = frozenConstraint + frozenConstraint = true + try isSameType(tp1, tp2) + finally frozenConstraint = saved + } + /** Test whether the lower bounds of all parameters in this * constraint are a solution to the constraint. */ diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index 8b2eb9cbffb2..59d4898e616f 100644 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -685,7 +685,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { case formal1 :: rest1 => formals2 match { case formal2 :: rest2 => - (isSameType(formal1, formal2) + (isSameTypeWhenFrozen(formal1, formal2) || isJava1 && (formal2 isRef ObjectClass) && (formal1 isRef AnyClass) || isJava2 && (formal1 isRef ObjectClass) && (formal2 isRef AnyClass)) && matchingParams(rest1, rest2, isJava1, isJava2) @@ -1059,7 +1059,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { case tp1: ClassInfo => tp2 match { case tp2: ClassInfo => - isSubType(tp1.prefix, tp2.prefix) || (tp1.cls.owner derivesFrom tp2.cls.owner) + isSubTypeWhenFrozen(tp1.prefix, tp2.prefix) || (tp1.cls.owner derivesFrom tp2.cls.owner) case _ => false } @@ -1075,7 +1075,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { tp2 match { case tp2: MethodType => def asGoodParams(formals1: List[Type], formals2: List[Type]) = - (formals2 corresponds formals1)(isSubType) + (formals2 corresponds formals1)(isSubTypeWhenFrozen) asGoodParams(tp1.paramTypes, tp2.paramTypes) && (!asGoodParams(tp2.paramTypes, tp1.paramTypes) || isAsGood(tp1.resultType, tp2.resultType)) From cc942e6a44951ea49601f822baeb38bbf5536294 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 25 Sep 2015 14:04:21 +0200 Subject: [PATCH 13/22] Reshuffle nested and/or calls. Overall goal: Push backtracking deeper into the tree. --- src/dotty/tools/dotc/core/TypeComparer.scala | 24 ++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index 59d4898e616f..5555d13ba0a2 100644 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -345,6 +345,18 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { } compareRefined case OrType(tp21, tp22) => + // Rewrite T1 <: (T211 & T212) | T22 to T1 <: (T211 | T22) and T1 <: (T212 | T22) + // and analogously for T1 <: T21 | (T221 & T222) + tp21 match { + case AndType(tp211, tp212) => + return isSubType(tp1, OrType(tp211, tp22)) && isSubType(tp1, OrType(tp212, tp22)) + case _ => + } + tp22 match { + case AndType(tp221, tp222) => + return isSubType(tp1, OrType(tp21, tp221)) && isSubType(tp1, OrType(tp21, tp222)) + case _ => + } eitherIsSubType(tp1, tp21, tp1, tp22) || fourthTry(tp1, tp2) case tp2 @ MethodType(_, formals2) => def compareMethod = tp1 match { @@ -446,6 +458,18 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { isNewSubType(tp1.parent, tp2) || needsEtaLift(tp2, tp1) && tp2.testLifted(tp1.typeParams, isSubType(tp1, _), Nil) case AndType(tp11, tp12) => + // Rewrite (T111 | T112) & T12 <: T2 to (T111 & T12) <: T2 and (T112 | T12) <: T2 + // and analogously for T11 & (T121 | T122) & T12 <: T2 + tp11 match { + case OrType(tp111, tp112) => + return isSubType(AndType(tp111, tp12), tp2) && isSubType(AndType(tp112, tp12), tp2) + case _ => + } + tp12 match { + case OrType(tp121, tp122) => + return isSubType(AndType(tp11, tp121), tp2) && isSubType(AndType(tp11, tp122), tp2) + case _ => + } eitherIsSubType(tp11, tp2, tp12, tp2) case JavaArrayType(elem1) => def compareJavaArray = tp2 match { From d8351e90a3f4c96a0c08990225948e90bf9c6338 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 25 Sep 2015 16:15:30 +0200 Subject: [PATCH 14/22] Add test case Iter2.scala fails with 6 errors, but succeeds once lubs and glbs do not try to unify under invariant type constructors. --- tests/pos/Iter2.scala | 245 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 245 insertions(+) create mode 100644 tests/pos/Iter2.scala diff --git a/tests/pos/Iter2.scala b/tests/pos/Iter2.scala new file mode 100644 index 000000000000..b9dc5d625611 --- /dev/null +++ b/tests/pos/Iter2.scala @@ -0,0 +1,245 @@ +package dotty1.collections +package immutable + +import annotation.unchecked.uncheckedVariance + +object Iter2 { + + trait Iterator[+A] extends IterableOnce[A] { + def hasNext: Boolean + def next: A + def iterator = this + def foreach(f: A => Unit): Unit = ??? + def map[B](f: A => B): Iterator[B] = ??? + def flatMap[B](f: A => IterableOnce[B]): Iterator[B] = ??? + def ++[B >: A](xs: IterableOnce[B]): Iterator[B] = ??? + def drop(n: Int): Iterator[A] = ??? + def indexWhere(p: A => Boolean): Int = { + var i = 0 + while (hasNext) { + if (p(next)) return i + i += 1 + } + -1 + } + def zip[B](that: Iterator[B]): Iterator[(A, B)] = ??? + def copy: Iterator[A] = ??? + } +/* + trait View[+A] { self => + def iterator: Iterator[A] + def foreach(f: A => Unit): Unit = iterator.foreach(f) + def map[B](f: A => B): View[B] = + new View[B] { def iterator = self.iterator.map(f) } + def flatMap[B](f: A => IterableOnce[B]): View[B] = + new View[B] { def iterator = self.iterator.flatMap(f) } + def ++[B >: A](xs: IterableOnce[B]): View[B] = + new View[B] { def iteratpr = self.iterator ++ xs } + def drop(n: Int): View[A] = + new View[A] { def iteratpr = self.iterator.drop(n) } + def indexWhere(p: A => Boolean): Int = { + var i = 0 + while (hasNext) { + if (p(next)) return i + i += 1 + } + -1 + } + def zip[B](that: Iterator[B]): Iterator[(A, B)] = ??? + } +*/ + + trait IterableOnce[+A] { + def iterator: Iterator[A] + def buildIterator: Iterator[A] = iterator + } + + trait FromIterator[+C[X] <: Iterable[X]] { + def fromIterator[B](it: Iterator[B]): C[B] + } + + trait Iterable[+IA] extends IterableOnce[IA] with FromIterator[Iterable] { + def view: View[IA] = new View(iterator) + } + + trait Seq[+AA] extends Iterable[AA] with FromIterator[Seq] { + def apply(i: Int): AA + def length: Int + } + + sealed trait List[+A] extends Seq[A] with FromIterator[List] { + def isEmpty: Boolean + def head: A + def tail: List[A] + def iterator = new ListIterator[A](this) + def fromIterator[B](it: Iterator[B]): List[B] = it match { + case ListIterator(xs) => xs + case _ => if (it.hasNext) Cons(it.next, fromIterator(it)) else Nil + } + 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 + } + + class View[+A](it: Iterator[A]) extends Iterable[A] with FromIterator[View] { + def iterator: Iterator[A] = it.copy + def fromIterator[B](it: Iterator[B]): View[B] = new View(it) + } + + case class Cons[+A](x: A, xs: List[A]) extends List[A] { + def isEmpty = false + def head = x + def tail = xs + } + + case object Nil extends List[Nothing] { + def isEmpty = true + def head = ??? + def tail = ??? + } + + class ArrayBuffer[A] private (initElems: Array[AnyRef], initLen: Int) extends Seq[A] with FromIterator[ArrayBuffer] { + def this() = this(new Array[AnyRef](16), 0) + def this(it: ArrayIterator[A]) = this(it.elems, it.len) + private var elems: Array[AnyRef] = initElems + private var len = 0 + def iterator = + elems.iterator.take(len).asInstanceOf[Iterator[A]] + override def buildIterator = + new ArrayIterator(elems, len).asInstanceOf[Iterator[A]] + def fromIterator[B](it: Iterator[B]): ArrayBuffer[B] = + new ArrayBuffer(ArrayIterator.fromIterator(it)) + def apply(i: Int) = elems(i).asInstanceOf[A] + def length = len + } +/* + class SeqView[A](itf: () => Iterator) extends Seq[A] with FromIterator[SeqView] { + def iterator = it + def buildIterator = it + def fromIterator[B](it: Iterator[B]) = it match { + case ViewIterator(itf) => SeqView(itf) + } + } +*/ + implicit class IterableTransforms[A, C[X] <: Iterable[X]](val c: Iterable[A] with FromIterator[C]) extends AnyVal { + def map[B](f: A => B): C[B] = c.fromIterator(c.buildIterator.map(f)) + def flatMap[B](f: A => IterableOnce[B]): C[B] = c.fromIterator(c.buildIterator.flatMap(f(_).buildIterator)) + def ++[B >: A](xs: IterableOnce[B]): C[B] = c.fromIterator(c.buildIterator ++ xs.buildIterator) + def drop(n: Int): C[A] = c.fromIterator(c.buildIterator.drop(n)) + def head: A = c.iterator.next + def zip[B](xs: IterableOnce[B]): C[(A, B)] = c.fromIterator(c.iterator.zip(xs.iterator)) + } + + implicit class SeqTransforms[SA, C[X] <: Seq[X]](val c: Seq[SA] with FromIterator[C]) extends AnyVal { + def reverse: C[SA] = { + val elems = new Array[AnyRef](c.length) + var i = elems.length + val it = c.iterator + while (it.hasNext) { + i -= 1 + elems(i) = it.next.asInstanceOf[AnyRef] + } + val xzz = c.fromIterator(ArrayIterator[SA](elems, c.length)) + xzz + } + def indexWhere(p: SA => Boolean): Int = c.iterator.indexWhere(p) + } +/* + case class ViewIterator[+A](itf: () => Iterator) extends Iterator[A] { + + def hasNext = it.hasNext + def next + def map(f: A => B): ViewIterator[B] = ViewIterator(() => it().map(f)) + def + } +*/ + case class ListIterator[+A](xs: List[A]) extends Iterator[A] { + private[this] var current: List[A] = xs + def hasNext = !current.isEmpty + def next = { val res = current.head; current = current.tail; res } + } + + case class ArrayIterator[+A](elems: Array[AnyRef], len: Int) extends Iterator[A] { + import ArrayIterator._ + + private def elem(i: Int) = elems(i).asInstanceOf[A] + + private var cur = 0 + + def hasNext = cur < len + def next = { val res = elem(cur); cur += 1; res } + + override def foreach(f: A => Unit): Unit = + for (i <- 0 until len) f(elem(i)) + + override def map[B](f: A => B): ArrayIterator[B] = { + var mapped = elems + for (i <- 0 until len) { + val x = elem(i) + val y = widen(f(x)) + if (widen(x) ne y) { + if (mapped eq elems) mapped = new Array[AnyRef](len) + mapped(i) = y + } + } + if (mapped eq elems) this.asInstanceOf[ArrayIterator[B]] + else new ArrayIterator(mapped, len) + } + + override def flatMap[B](f: A => IterableOnce[B]): ArrayIterator[B] = + flatten(map(f(_).buildIterator)) + + override def ++[B >: A](that: IterableOnce[B]): ArrayIterator[B] = { + val thatIterator @ ArrayIterator(elems2, len2) = fromIterator(that.iterator) + if (len == 0) thatIterator + else if (len2 == 0) this + else { + val resLen = len + len2 + val resElems = new Array[AnyRef](resLen) + Array.copy(elems, 0, resElems, 0, len) + Array.copy(elems2, 0, resElems, len, len2) + new ArrayIterator(resElems, resLen) + } + } + } + + object ArrayIterator { + private def widen(x: Any): AnyRef = x.asInstanceOf[AnyRef] + + def fromIterator[A](it: Iterator[A]): ArrayIterator[A] = it match { + case it: ArrayIterator[A] => it + case _ => + var elems = new Array[AnyRef](32) + var len = 0 + def ensureCapacity() = { + while (len > elems.length) { + val newElems = new Array[AnyRef](elems.length * 2) + Array.copy(elems, 0, newElems, 0, elems.length) + elems = newElems + } + } + while (it.hasNext) { + len += 1 + ensureCapacity() + elems(len - 1) = widen(it.next) + } + ArrayIterator(elems, len) + } + + def flatten[A](its: ArrayIterator[Iterator[A]]): ArrayIterator[A] = { + var arrayIts = its.map(fromIterator) + var totalLen = 0 + arrayIts.foreach(totalLen += _.len) + val allElems = new Array[AnyRef](totalLen) + var j = 0 + arrayIts.foreach { it => + Array.copy(it.elems, 0, allElems, j, it.len) + j += it.len + } + new ArrayIterator(allElems, totalLen) + } + } +} From 6497d02675e651a79383c43f6ef38be918be1867 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 25 Sep 2015 16:29:21 +0200 Subject: [PATCH 15/22] Move failing test to pending There is a diff, but a minor one. Instead of (T? >: Int <: Int) we get (T? = Int) after pickling. --- tests/{ => pending}/pickling/i94-nada.scala | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/{ => pending}/pickling/i94-nada.scala (100%) diff --git a/tests/pickling/i94-nada.scala b/tests/pending/pickling/i94-nada.scala similarity index 100% rename from tests/pickling/i94-nada.scala rename to tests/pending/pickling/i94-nada.scala From 0838b81bea4d579f812c02fafdc50e2b1920c059 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 2 Oct 2015 19:02:20 +0200 Subject: [PATCH 16/22] Add explanations for rewritings --- src/dotty/tools/dotc/core/TypeComparer.scala | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index 5555d13ba0a2..5fbffe6e95ab 100644 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -347,6 +347,9 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { case OrType(tp21, tp22) => // Rewrite T1 <: (T211 & T212) | T22 to T1 <: (T211 | T22) and T1 <: (T212 | T22) // and analogously for T1 <: T21 | (T221 & T222) + // `|' types to the right of <: are problematic, because + // we have to choose one constraint set or another, which might cut off + // solutions. The rewriting delays the point where we have to choose. tp21 match { case AndType(tp211, tp212) => return isSubType(tp1, OrType(tp211, tp22)) && isSubType(tp1, OrType(tp212, tp22)) @@ -460,6 +463,9 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { case AndType(tp11, tp12) => // Rewrite (T111 | T112) & T12 <: T2 to (T111 & T12) <: T2 and (T112 | T12) <: T2 // and analogously for T11 & (T121 | T122) & T12 <: T2 + // `&' types to the left of <: are problematic, because + // we have to choose one constraint set or another, which might cut off + // solutions. The rewriting delays the point where we have to choose. tp11 match { case OrType(tp111, tp112) => return isSubType(AndType(tp111, tp12), tp2) && isSubType(AndType(tp112, tp12), tp2) From 0a48b2a6d3516f1f7d10d9a2a594a744ef98d5e8 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 6 Oct 2015 13:47:43 +0200 Subject: [PATCH 17/22] Make prefix covariant in type maps. According to the new subtyping rules, T <: U ==> T#X <: U#X and T#x <: U#x Therefore type maps should treat the prefix of a named type covariantly. --- src/dotty/tools/dotc/core/Types.scala | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index e4f228915a58..1d438daf0506 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -2884,13 +2884,7 @@ object Types { tp match { case tp: NamedType => if (stopAtStatic && tp.symbol.isStatic) tp - else { - val saved = variance - variance = 0 - val result = tp.derivedSelect(this(tp.prefix)) - variance = saved - result - } + else tp.derivedSelect(this(tp.prefix)) case _: ThisType | _: BoundType From 868ae2acb4ef66fa6c32b45e10ba9940ba7340ad Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 2 Oct 2015 18:34:18 +0200 Subject: [PATCH 18/22] Add collection strawman and its tests This compiles only after the changes in this branch. --- .../collections/CollectionStrawMan1.scala | 330 ++++++++++++++++++ tests/run/CollectionTests.check | 51 +++ tests/run/CollectionTests.scala | 168 +++++++++ 3 files changed, 549 insertions(+) create mode 100644 src/dotty/collections/CollectionStrawMan1.scala create mode 100644 tests/run/CollectionTests.check create mode 100644 tests/run/CollectionTests.scala diff --git a/src/dotty/collections/CollectionStrawMan1.scala b/src/dotty/collections/CollectionStrawMan1.scala new file mode 100644 index 000000000000..211c8d7daaf9 --- /dev/null +++ b/src/dotty/collections/CollectionStrawMan1.scala @@ -0,0 +1,330 @@ +package strawman.collections + +import Predef.{augmentString => _, wrapString => _, _} +import scala.reflect.ClassTag +import collection.mutable.ListBuffer + +/** 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 CollectionStrawMan1 { + + /* ------------ Base Traits -------------------------------- */ + + /** Replaces TraversableOnce */ + trait CanIterate[+A] { + def iterator: Iterator[A] + } + + /** Iterator guaranteed to be usable multiple times */ + trait HasIterator[+A] extends CanIterate[A] + + /** Base trait for instances that can construct a collection from an iterator */ + trait FromIterator[+C[X] <: Iterable[X]] { + def fromIterator[B](it: Iterator[B]): C[B] + } + + /** Base trait for companion objects of collections */ + trait IterableFactory[+C[X] <: Iterable[X]] extends FromIterator[C] { + def empty[X]: C[X] = fromIterator(Iterator.empty) + def apply[A](xs: A*): C[A] = fromIterator(Iterator(xs: _*)) + } + + /** Base trait for generic collections */ + trait Iterable[+IA] extends HasIterator[IA] with FromIterator[Iterable] { + def buildIterator: Iterator[IA] = iterator + } + + /** Base trait for sequence collections */ + trait Seq[+AA] extends Iterable[AA] with FromIterator[Seq] { + def apply(i: Int): AA + def length: Int + } + + /* ------------ Operations ----------------------------------- */ + + /** Operations returning types unrelated to current collection */ + trait Ops[A] extends Any { + def toIterator: Iterator[A] + def foreach(f: A => Unit): Unit = toIterator.foreach(f) + def foldLeft[B](z: B)(op: (B, A) => B): B = toIterator.foldLeft(z)(op) + def foldRight[B](z: B)(op: (A, B) => B): B = toIterator.foldRight(z)(op) + def indexWhere(p: A => Boolean): Int = toIterator.indexWhere(p) + def head: A = toIterator.next + def view: View[A] = new View(toIterator) + def collect[C[X] <: Iterable[X]](fi: FromIterator[C]): C[A] = fi.fromIterator(toIterator) + } + + /** Transforms returning same collection type */ + trait MonoTransforms[A, Repr] extends Any { + def toIterator: Iterator[A] + def fromIterator(it: => Iterator[A]): Repr + def partition(p: A => Boolean): (Repr, Repr) = { + val (xs, ys) = toIterator.partition(p) + (fromIterator(xs), fromIterator(ys)) + } + def drop(n: Int): Repr = fromIterator(toIterator.drop(n)) + } + + /** Transforms returning same collection type constructor */ + trait PolyTransforms[A, C[X]] extends Any { + def toIterator: Iterator[A] + def fromIterator[B](it: => Iterator[B]): C[B] + def map[B](f: A => B): C[B] = fromIterator(toIterator.map(f)) + def flatMap[B](f: A => CanIterate[B]): C[B] = fromIterator(toIterator.flatMap(f(_))) + def ++[B >: A](xs: CanIterate[B]): C[B] = fromIterator(toIterator ++ xs) + def zip[B](xs: CanIterate[B]): C[(A, B)] = fromIterator(toIterator.zip(xs.iterator)) + } + + /** Transforms that only apply to Seq */ + trait MonoTransformsOfSeqs[A, Repr] extends Any with MonoTransforms[A, Repr] { + def reverse: Repr = fromIterator(toIterator.reverse) + } + + /** Implementation of Ops for all generic collections */ + implicit class IterableOps[A](val c: Iterable[A]) + extends AnyVal with Ops[A] { + def toIterator = c.iterator + } + + /** Implementation of MonoTransforms for all generic collections */ + implicit class IterableMonoTransforms[A, C[X] <: Iterable[X]](val c: Iterable[A] with FromIterator[C]) + extends AnyVal with MonoTransforms[A, C[A]] { + def toIterator = c.buildIterator + def fromIterator(it: => Iterator[A]): C[A] = c.fromIterator(it) + } + + /** Implementation of PolyTransforms for all generic collections */ + implicit class IterablePolyTransforms[A, C[X] <: Iterable[X]](val c: Iterable[A] with FromIterator[C]) + extends AnyVal with PolyTransforms[A, C] { + def toIterator = c.buildIterator + def fromIterator[B](it: => Iterator[B]) = c.fromIterator(it) + } + + /** Implementation of MonoTransformsForSeqs for all generic collections */ + implicit class SeqMonoTransforms[A, C[X] <: Seq[X]](val c: Seq[A] with FromIterator[C]) + extends AnyVal with MonoTransformsOfSeqs[A, C[A]] { + def toIterator = c.buildIterator + def fromIterator(it: => Iterator[A]) = c.fromIterator(it) + } + + /* --------- Concrete collection types ------------------------------- */ + + /** Concrete collection type: List */ + sealed trait List[+A] extends Seq[A] with FromIterator[List] { + def isEmpty: Boolean + def head: A + def tail: List[A] + def iterator = new ListIterator[A](this) + def fromIterator[B](it: Iterator[B]): List[B] = List.fromIterator(it) + 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 + } + + case class Cons[+A](x: A, xs: List[A]) extends List[A] { + def isEmpty = false + def head = x + def tail = xs + } + + case object List extends IterableFactory[List] { + def fromIterator[B](it: Iterator[B]): List[B] = it match { + case it: ListIterator[B] => it.toList + case _ => if (it.hasNext) Cons(it.next, fromIterator(it)) else Nil + } + } + + class ListIterator[+A](xs: List[A]) extends Iterator[A] { + private[this] var current = xs + def hasNext = !current.isEmpty + def next = { val r = current.head; current = current.tail; r } + def toList = current + } + + case object Nil extends List[Nothing] { + def isEmpty = true + def head = ??? + def tail = ??? + } + + /** Concrete collection type: View */ + class View[+A](it: => Iterator[A]) extends HasIterator[A] { + def iterator = it + } + + implicit class ViewOps[A](val v: View[A]) extends AnyVal with Ops[A] { + def toIterator = v.iterator + def cache = collect(List).view + } + + implicit class ViewMonoTransforms[A](val v: View[A]) + extends AnyVal with MonoTransforms[A, View[A]] { + def toIterator = v.iterator + def fromIterator(it: => Iterator[A]): View[A] = new View(it) + } + + implicit class ViewPolyTransforms[A](val v: View[A]) + extends AnyVal with PolyTransforms[A, View] { + def toIterator = v.iterator + def fromIterator[B](it: => Iterator[B]) = new View(it) + } + + /** Concrete collection type: String */ + implicit class StringOps(val s: String) extends AnyVal with Ops[Char] { + def iterator: Iterator[Char] = new RandomAccessIterator[Char] { + def length = s.length + def apply(n: Int) = s.charAt(n) + } + def toIterator = iterator + } + + implicit class StringMonoTransforms(val s: String) + extends AnyVal with MonoTransformsOfSeqs[Char, String] { + def toIterator = s.iterator + def fromIterator(it: => Iterator[Char]) = { + val sb = new StringBuilder + for (ch <- it) sb.append(ch) + sb.toString + } + } + + implicit class StringPolyTransforms(val s: String) + extends AnyVal with PolyTransforms[Char, Seq] { + def toIterator = s.iterator + def fromIterator[B](it: => Iterator[B]) = List.fromIterator(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: CanIterate[Char]): String = { + val sb = new StringBuilder(s) + for (ch <- xs.iterator) sb.append(ch) + sb.toString + } + def ++(xs: String): String = s + xs + } + +/* ---------- Iterators --------------------------------------------------- */ + + /** A core Iterator class */ + trait Iterator[+A] extends CanIterate[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 map[B](f: A => B): Iterator[B] = new Iterator[B] { + def hasNext = self.hasNext + def next = f(self.next) + } + def flatMap[B](f: A => CanIterate[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: CanIterate[B]): Iterator[B] = new Iterator[B] { + private var myCurrent: Iterator[B] = self + private def current = { + if (!myCurrent.hasNext && myCurrent.eq(self)) myCurrent = xs.iterator + myCurrent + } + def hasNext = current.hasNext + def next = current.next + } + def partition(p: A => Boolean): (Iterator[A], Iterator[A]) = { + val lookaheadTrue, lookaheadFalse = new ListBuffer[A] + class PartIterator[+A](buf: ListBuffer[A]) extends Iterator[A] { + final def hasNext: Boolean = + buf.nonEmpty || + self.hasNext && { + val elem = self.next + (if (p(elem)) lookaheadTrue else lookaheadFalse) += elem + hasNext + } + final def next = + if (hasNext) { + val r = buf.head + buf.trimStart(1) + r + } + else Iterator.nextOnEmpty + } + (new PartIterator(lookaheadTrue), new PartIterator(lookaheadFalse)) + } + def drop(n: Int): Iterator[A] = { + if (n <= 0) this + else { + next + drop(n - 1) + } + } + def zip[B](that: CanIterate[B]): Iterator[(A, B)] = new Iterator[(A, B)] { + val ti = that.iterator + def hasNext = self.hasNext && ti.hasNext + def next = (self.next, ti.next) + } + def reverse: Iterator[A] = { + var elems: List[A] = Nil + while (hasNext) elems = Cons(next, elems) + elems.iterator + } + } + + object Iterator { + val empty: Iterator[Nothing] = new Iterator[Nothing] { + def hasNext = false + def next = ??? + } + def apply[A](xs: A*): Iterator[A] = new RandomAccessIterator[A] { + def length = xs.length + def apply(n: Int) = xs(n) + } + def nextOnEmpty = throw new NoSuchElementException("next on empty iterator") + } + + trait RandomAccessIterator[+A] extends Iterator[A] { self => + def apply(n: Int): A + def length: Int + protected val len = length + private var cur = 0 + def hasNext = cur < len + def next: A = { val r = this(cur); cur += 1; r } + override def drop(n: Int): Iterator[A] = { cur += (n max 0); this } + override def reverse = new RandomAccessIterator[A] { + def length = self.length + def apply(n: Int) = apply(len - 1 - n) + } + } +} + diff --git a/tests/run/CollectionTests.check b/tests/run/CollectionTests.check new file mode 100644 index 000000000000..3afe62813eb2 --- /dev/null +++ b/tests/run/CollectionTests.check @@ -0,0 +1,51 @@ +------- +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))) +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/CollectionTests.scala b/tests/run/CollectionTests.scala new file mode 100644 index 000000000000..46cb63544038 --- /dev/null +++ b/tests/run/CollectionTests.scala @@ -0,0 +1,168 @@ +import Predef.{augmentString => _, wrapString => _, _} +import scala.reflect.ClassTag + +object Test { + import strawman.collections._ + import CollectionStrawMan1._ + + 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.collect(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.collect(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.collect(List)) + println(xs7.collect(List)) + println(xs8.collect(List)) + println(xs9.collect(List)) + println(xs10.collect(List)) + println(xs11.collect(List)) + println(xs12.collect(List)) + println(xs13.collect(List)) + println(xs14.collect(List)) + println(xs15.collect(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.collect(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((_: Char) + 1) // !!! need a language change to make this work without the : Char + val ys9: Seq[Int] = xs9 + val xs9a = xs.map((_: Char).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') + 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 intsView = ints.view + seqOps(ints) + viewOps(intsView) + stringOps("abc") + } +} From 135b7255b3e90117cca9d046a89ef779adbef783 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 4 Oct 2015 10:33:54 +0200 Subject: [PATCH 19/22] Add ArrayBuffer as another Seq class. Make iterators inspectable. --- .../collections/CollectionStrawMan1.scala | 255 +++++++++++------- tests/run/CollectionTests.check | 17 ++ tests/run/CollectionTests.scala | 2 + 3 files changed, 173 insertions(+), 101 deletions(-) diff --git a/src/dotty/collections/CollectionStrawMan1.scala b/src/dotty/collections/CollectionStrawMan1.scala index 211c8d7daaf9..ea26eb932190 100644 --- a/src/dotty/collections/CollectionStrawMan1.scala +++ b/src/dotty/collections/CollectionStrawMan1.scala @@ -36,9 +36,7 @@ object CollectionStrawMan1 { } /** Base trait for generic collections */ - trait Iterable[+IA] extends HasIterator[IA] with FromIterator[Iterable] { - def buildIterator: Iterator[IA] = iterator - } + trait Iterable[+IA] extends HasIterator[IA] with FromIterator[Iterable] /** Base trait for sequence collections */ trait Seq[+AA] extends Iterable[AA] with FromIterator[Seq] { @@ -50,67 +48,67 @@ object CollectionStrawMan1 { /** Operations returning types unrelated to current collection */ trait Ops[A] extends Any { - def toIterator: Iterator[A] - def foreach(f: A => Unit): Unit = toIterator.foreach(f) - def foldLeft[B](z: B)(op: (B, A) => B): B = toIterator.foldLeft(z)(op) - def foldRight[B](z: B)(op: (A, B) => B): B = toIterator.foldRight(z)(op) - def indexWhere(p: A => Boolean): Int = toIterator.indexWhere(p) - def head: A = toIterator.next - def view: View[A] = new View(toIterator) - def collect[C[X] <: Iterable[X]](fi: FromIterator[C]): C[A] = fi.fromIterator(toIterator) + 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 head: A = iterator.next + def view: View[A] = new View(iterator) + def collect[C[X] <: Iterable[X]](fi: FromIterator[C]): C[A] = fi.fromIterator(iterator) } /** Transforms returning same collection type */ trait MonoTransforms[A, Repr] extends Any { - def toIterator: Iterator[A] - def fromIterator(it: => Iterator[A]): Repr + protected def iter: Iterator[A] + protected def fromIter(it: => Iterator[A]): Repr def partition(p: A => Boolean): (Repr, Repr) = { - val (xs, ys) = toIterator.partition(p) - (fromIterator(xs), fromIterator(ys)) + val (xs, ys) = iter.partition(p) + (fromIter(xs), fromIter(ys)) } - def drop(n: Int): Repr = fromIterator(toIterator.drop(n)) + def drop(n: Int): Repr = fromIter(iter.drop(n)) } /** Transforms returning same collection type constructor */ trait PolyTransforms[A, C[X]] extends Any { - def toIterator: Iterator[A] - def fromIterator[B](it: => Iterator[B]): C[B] - def map[B](f: A => B): C[B] = fromIterator(toIterator.map(f)) - def flatMap[B](f: A => CanIterate[B]): C[B] = fromIterator(toIterator.flatMap(f(_))) - def ++[B >: A](xs: CanIterate[B]): C[B] = fromIterator(toIterator ++ xs) - def zip[B](xs: CanIterate[B]): C[(A, B)] = fromIterator(toIterator.zip(xs.iterator)) + protected def iter: Iterator[A] + protected def fromIter[B](it: => Iterator[B]): C[B] + def map[B](f: A => B): C[B] = fromIter(iter.map(f)) + def flatMap[B](f: A => CanIterate[B]): C[B] = fromIter(iter.flatMap(f(_))) + def ++[B >: A](xs: CanIterate[B]): C[B] = fromIter(iter ++ xs) + def zip[B](xs: CanIterate[B]): C[(A, B)] = fromIter(iter.zip(xs.iterator)) } /** Transforms that only apply to Seq */ trait MonoTransformsOfSeqs[A, Repr] extends Any with MonoTransforms[A, Repr] { - def reverse: Repr = fromIterator(toIterator.reverse) + def reverse: Repr = fromIter(iter.reverse) } /** Implementation of Ops for all generic collections */ implicit class IterableOps[A](val c: Iterable[A]) extends AnyVal with Ops[A] { - def toIterator = c.iterator + def iterator = c.iterator } /** Implementation of MonoTransforms for all generic collections */ implicit class IterableMonoTransforms[A, C[X] <: Iterable[X]](val c: Iterable[A] with FromIterator[C]) extends AnyVal with MonoTransforms[A, C[A]] { - def toIterator = c.buildIterator - def fromIterator(it: => Iterator[A]): C[A] = c.fromIterator(it) + protected def iter = c.iterator + protected def fromIter(it: => Iterator[A]): C[A] = c.fromIterator(it) } /** Implementation of PolyTransforms for all generic collections */ implicit class IterablePolyTransforms[A, C[X] <: Iterable[X]](val c: Iterable[A] with FromIterator[C]) extends AnyVal with PolyTransforms[A, C] { - def toIterator = c.buildIterator - def fromIterator[B](it: => Iterator[B]) = c.fromIterator(it) + protected def iter = c.iterator + protected def fromIter[B](it: => Iterator[B]) = c.fromIterator(it) } /** Implementation of MonoTransformsForSeqs for all generic collections */ implicit class SeqMonoTransforms[A, C[X] <: Seq[X]](val c: Seq[A] with FromIterator[C]) extends AnyVal with MonoTransformsOfSeqs[A, C[A]] { - def toIterator = c.buildIterator - def fromIterator(it: => Iterator[A]) = c.fromIterator(it) + protected def iter = c.iterator + protected def fromIter(it: => Iterator[A]) = c.fromIterator(it) } /* --------- Concrete collection types ------------------------------- */ @@ -156,41 +154,80 @@ object CollectionStrawMan1 { def tail = ??? } + /** Concrete collection type: ArrayBuffer */ + class ArrayBuffer[A] private (initElems: Array[AnyRef], initLen: Int = 0) extends Seq[A] with FromIterator[ArrayBuffer] { + def this() = this(new Array[AnyRef](16)) + private var elems: Array[AnyRef] = initElems + private var len = 0 + def apply(i: Int) = elems(i).asInstanceOf[A] + def length = len + def iterator = new RandomAccessIterator[A] { + override val knownLength = len + def apply(n: Int) = elems(n).asInstanceOf[A] + } + def fromIterator[B](it: Iterator[B]): ArrayBuffer[B] = + ArrayBuffer.fromIterator(it) + def +=(elem: A): this.type = { + if (len == elems.length) { + val newelems = new Array[AnyRef](len * 2) + Array.copy(elems, 0, newelems, 0, len) + elems = newelems + } + elems(len) = elem.asInstanceOf[AnyRef] + len += 1 + this + } + override def toString = elems.take(len).deep.toString + } + + object ArrayBuffer extends IterableFactory[ArrayBuffer] { + def fromIterator[B](it: Iterator[B]): ArrayBuffer[B] = + if (it.knownLength == 0) { + val elems = new Array[AnyRef](it.knownLength) + for (i <- 0 until elems.length) elems(i) = it.next.asInstanceOf[AnyRef] + new ArrayBuffer(elems) + } + else { + val buf = new ArrayBuffer[B] + while (it.hasNext) buf += it.next + buf + } + } + /** Concrete collection type: View */ class View[+A](it: => Iterator[A]) extends HasIterator[A] { def iterator = it } implicit class ViewOps[A](val v: View[A]) extends AnyVal with Ops[A] { - def toIterator = v.iterator - def cache = collect(List).view + def iterator = v.iterator + def cache = collect(ArrayBuffer).view } implicit class ViewMonoTransforms[A](val v: View[A]) extends AnyVal with MonoTransforms[A, View[A]] { - def toIterator = v.iterator - def fromIterator(it: => Iterator[A]): View[A] = new View(it) + protected def iter = v.iterator + protected def fromIter(it: => Iterator[A]): View[A] = new View(it) } implicit class ViewPolyTransforms[A](val v: View[A]) extends AnyVal with PolyTransforms[A, View] { - def toIterator = v.iterator - def fromIterator[B](it: => Iterator[B]) = new View(it) + protected def iter = v.iterator + protected def fromIter[B](it: => Iterator[B]) = new View(it) } /** Concrete collection type: String */ implicit class StringOps(val s: String) extends AnyVal with Ops[Char] { def iterator: Iterator[Char] = new RandomAccessIterator[Char] { - def length = s.length + override val knownLength = s.length def apply(n: Int) = s.charAt(n) } - def toIterator = iterator } implicit class StringMonoTransforms(val s: String) extends AnyVal with MonoTransformsOfSeqs[Char, String] { - def toIterator = s.iterator - def fromIterator(it: => Iterator[Char]) = { + protected def iter = StringOps(s).iterator + protected def fromIter(it: => Iterator[Char]) = { val sb = new StringBuilder for (ch <- it) sb.append(ch) sb.toString @@ -199,8 +236,8 @@ object CollectionStrawMan1 { implicit class StringPolyTransforms(val s: String) extends AnyVal with PolyTransforms[Char, Seq] { - def toIterator = s.iterator - def fromIterator[B](it: => Iterator[B]) = List.fromIterator(it) + protected def iter = StringOps(s).iterator + protected def fromIter[B](it: => Iterator[B]) = List.fromIterator(it) def map(f: Char => Char): String = { val sb = new StringBuilder for (ch <- s) sb.append(f(ch)) @@ -240,91 +277,107 @@ object CollectionStrawMan1 { } -1 } - 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 => CanIterate[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: CanIterate[B]): Iterator[B] = new Iterator[B] { - private var myCurrent: Iterator[B] = self - private def current = { - if (!myCurrent.hasNext && myCurrent.eq(self)) myCurrent = xs.iterator - myCurrent - } - def hasNext = current.hasNext - def next = current.next - } + def map[B](f: A => B): Iterator[B] = Iterator.Map(this, f) + def flatMap[B](f: A => CanIterate[B]): Iterator[B] = Iterator.FlatMap(this, f) + def ++[B >: A](xs: CanIterate[B]): Iterator[B] = Iterator.Concat(this, xs.iterator) def partition(p: A => Boolean): (Iterator[A], Iterator[A]) = { val lookaheadTrue, lookaheadFalse = new ListBuffer[A] - class PartIterator[+A](buf: ListBuffer[A]) extends Iterator[A] { - final def hasNext: Boolean = - buf.nonEmpty || - self.hasNext && { - val elem = self.next - (if (p(elem)) lookaheadTrue else lookaheadFalse) += elem - hasNext - } - final def next = - if (hasNext) { - val r = buf.head - buf.trimStart(1) - r - } - else Iterator.nextOnEmpty - } - (new PartIterator(lookaheadTrue), new PartIterator(lookaheadFalse)) - } - def drop(n: Int): Iterator[A] = { - if (n <= 0) this - else { - next - drop(n - 1) - } - } - def zip[B](that: CanIterate[B]): Iterator[(A, B)] = new Iterator[(A, B)] { - val ti = that.iterator - def hasNext = self.hasNext && ti.hasNext - def next = (self.next, ti.next) + (Iterator.Partition(this, p, lookaheadTrue, lookaheadFalse), + Iterator.Partition[A](this, !p(_), lookaheadFalse, lookaheadTrue)) } + def drop(n: Int): Iterator[A] = Iterator.Drop(this, n) + def zip[B](that: CanIterate[B]): Iterator[(A, B)] = Iterator.Zip(this, that.iterator) def reverse: Iterator[A] = { var elems: List[A] = Nil while (hasNext) elems = Cons(next, elems) elems.iterator } + def underlying: Iterator[_] = this + def knownLength = -1 } object Iterator { val empty: Iterator[Nothing] = new Iterator[Nothing] { def hasNext = false def next = ??? + override val knownLength = 0 } def apply[A](xs: A*): Iterator[A] = new RandomAccessIterator[A] { - def length = xs.length + override val knownLength = xs.length def apply(n: Int) = xs(n) } def nextOnEmpty = throw new NoSuchElementException("next on empty iterator") + + case class Map[A, B](override val underlying: Iterator[A], f: A => B) extends Iterator[B] { + def hasNext = underlying.hasNext + def next = f(underlying.next) + override val knownLength = underlying.knownLength + } + case class FlatMap[A, B](override val underlying: Iterator[A], f: A => CanIterate[B]) extends Iterator[B] { + private var myCurrent: Iterator[B] = Iterator.empty + private def current = { + while (!myCurrent.hasNext && underlying.hasNext) + myCurrent = f(underlying.next).iterator + myCurrent + } + def hasNext = current.hasNext + def next = current.next + } + case class Concat[A](override val underlying: Iterator[A], other: Iterator[A]) extends Iterator[A] { + private var myCurrent = underlying + private def current = { + if (!myCurrent.hasNext && myCurrent.eq(underlying)) myCurrent = other + myCurrent + } + def hasNext = current.hasNext + def next = current.next + override val knownLength = + if (underlying.knownLength > 0 && other.knownLength > 0) + underlying.knownLength + other.knownLength + else -1 + } + case class Partition[A](override val underlying: Iterator[A], p: A => Boolean, lookahead: ListBuffer[A], dual: ListBuffer[A]) extends Iterator[A] { + final def hasNext: Boolean = + lookahead.nonEmpty || + underlying.hasNext && { + val elem = underlying.next + (if (p(elem)) lookahead else dual) += elem + hasNext + } + final def next = + if (hasNext) { + val r = lookahead.head + lookahead.trimStart(1) + r + } else Iterator.nextOnEmpty + } + case class Drop[A](override val underlying: Iterator[A], n: Int) extends Iterator[A] { + var toSkip = n + def hasNext: Boolean = underlying.hasNext && ( + toSkip == 0 || { underlying.next; toSkip -= 1; hasNext }) + def next = if (hasNext) underlying.next else nextOnEmpty + override val knownLength = (underlying.knownLength - n) max -1 + } + case class Zip[A, B](override val underlying: Iterator[A], other: Iterator[B]) extends Iterator[(A, B)] { + def hasNext = underlying.hasNext && other.hasNext + def next = (underlying.next, other.next) + override val knownLength = underlying.knownLength min other.knownLength + } + class Reverse[A](underlying: RandomAccessIterator[A]) extends RandomAccessIterator[A] { + override val knownLength = underlying.knownLength + def apply(n: Int) = underlying.apply(knownLength - 1 - n) + } } trait RandomAccessIterator[+A] extends Iterator[A] { self => def apply(n: Int): A - def length: Int - protected val len = length + def knownLength: Int + def length: Int = knownLength private var cur = 0 - def hasNext = cur < len + def hasNext = cur < knownLength def next: A = { val r = this(cur); cur += 1; r } override def drop(n: Int): Iterator[A] = { cur += (n max 0); this } - override def reverse = new RandomAccessIterator[A] { - def length = self.length - def apply(n: Int) = apply(len - 1 - n) - } + override def reverse: Iterator[A] = new Iterator.Reverse(this) } } diff --git a/tests/run/CollectionTests.check b/tests/run/CollectionTests.check index 3afe62813eb2..7c4f6e77a8a8 100644 --- a/tests/run/CollectionTests.check +++ b/tests/run/CollectionTests.check @@ -21,6 +21,23 @@ Cons(3,Cons(2,Cons(1,Nil))) 1 1 Cons(1,Cons(2,Cons(3,Nil))) +Array(2) +Array(1, 3) +Array(3) +Array(true, true, true) +Array(1, -1, 2, -2, 3, -3) +Array(1, 2, 3, 1, 2, 3) +Array(1, 2, 3) +Cons(1,Cons(2,Cons(3,Nil))) +Array(1, 2, 3, a) +Array((1,true), (2,true), (3,true)) +Array(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) diff --git a/tests/run/CollectionTests.scala b/tests/run/CollectionTests.scala index 46cb63544038..0421cfbe47ac 100644 --- a/tests/run/CollectionTests.scala +++ b/tests/run/CollectionTests.scala @@ -160,8 +160,10 @@ object Test { def main(args: Array[String]) = { val ints = Cons(1, Cons(2, Cons(3, Nil))) + val intsBuf = ints.collect(ArrayBuffer) val intsView = ints.view seqOps(ints) + seqOps(intsBuf) viewOps(intsView) stringOps("abc") } From 1521121b573c9e4766fd66c080d44c9af099b438 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 4 Oct 2015 20:08:03 +0200 Subject: [PATCH 20/22] Bugfixes and move CollectionStrawMan1 into the correct directory. --- .../collections/CollectionStrawMan1.scala | 147 ++++++++++-------- tests/run/CollectionTests.check | 20 +-- 2 files changed, 96 insertions(+), 71 deletions(-) rename src/{dotty => strawman}/collections/CollectionStrawMan1.scala (76%) diff --git a/src/dotty/collections/CollectionStrawMan1.scala b/src/strawman/collections/CollectionStrawMan1.scala similarity index 76% rename from src/dotty/collections/CollectionStrawMan1.scala rename to src/strawman/collections/CollectionStrawMan1.scala index ea26eb932190..93d4086f254b 100644 --- a/src/dotty/collections/CollectionStrawMan1.scala +++ b/src/strawman/collections/CollectionStrawMan1.scala @@ -2,7 +2,6 @@ package strawman.collections import Predef.{augmentString => _, wrapString => _, _} import scala.reflect.ClassTag -import collection.mutable.ListBuffer /** A strawman architecture for new collections. It contains some * example collection classes and methods with the intent to expose @@ -36,11 +35,11 @@ object CollectionStrawMan1 { } /** Base trait for generic collections */ - trait Iterable[+IA] extends HasIterator[IA] with FromIterator[Iterable] + trait Iterable[+A] extends HasIterator[A] with FromIterator[Iterable] /** Base trait for sequence collections */ - trait Seq[+AA] extends Iterable[AA] with FromIterator[Seq] { - def apply(i: Int): AA + trait Seq[+A] extends Iterable[A] with FromIterator[Seq] { + def apply(i: Int): A def length: Int } @@ -53,6 +52,7 @@ object CollectionStrawMan1 { 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] = new View(iterator) def collect[C[X] <: Iterable[X]](fi: FromIterator[C]): C[A] = fi.fromIterator(iterator) @@ -134,7 +134,13 @@ object CollectionStrawMan1 { def tail = xs } - case object List extends IterableFactory[List] { + 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] = it match { case it: ListIterator[B] => it.toList case _ => if (it.hasNext) Cons(it.next, fromIterator(it)) else Nil @@ -148,50 +154,62 @@ object CollectionStrawMan1 { def toList = current } - case object Nil extends List[Nothing] { - def isEmpty = true - def head = ??? - def tail = ??? - } - /** Concrete collection type: ArrayBuffer */ - class ArrayBuffer[A] private (initElems: Array[AnyRef], initLen: Int = 0) extends Seq[A] with FromIterator[ArrayBuffer] { - def this() = this(new Array[AnyRef](16)) + class ArrayBuffer[A] private (initElems: Array[AnyRef], initLength: Int) extends Seq[A] with FromIterator[ArrayBuffer] { + def this() = this(new Array[AnyRef](16), 0) private var elems: Array[AnyRef] = initElems - private var len = 0 - def apply(i: Int) = elems(i).asInstanceOf[A] - def length = len - def iterator = new RandomAccessIterator[A] { - override val knownLength = len - def apply(n: Int) = elems(n).asInstanceOf[A] - } + private var start = 0 + private var limit = initLength + def apply(i: Int) = elems(start + i).asInstanceOf[A] + def length = limit - start + def iterator = new ArrayBufferIterator[A](elems, start, length) def fromIterator[B](it: Iterator[B]): ArrayBuffer[B] = ArrayBuffer.fromIterator(it) def +=(elem: A): this.type = { - if (len == elems.length) { - val newelems = new Array[AnyRef](len * 2) - Array.copy(elems, 0, newelems, 0, len) - elems = newelems + if (limit == elems.length) { + if (start > 0) { + Array.copy(elems, start, elems, 0, length) + limit -= start + start = 0 + } + else { + val newelems = new Array[AnyRef](limit * 2) + Array.copy(elems, 0, newelems, 0, limit) + elems = newelems + } } - elems(len) = elem.asInstanceOf[AnyRef] - len += 1 + elems(limit) = elem.asInstanceOf[AnyRef] + limit += 1 this } - override def toString = elems.take(len).deep.toString + def trimStart(n: Int): Unit = start += (n max 0) + override def toString = s"ArrayBuffer(${elems.slice(start, limit).mkString(", ")})" } object ArrayBuffer extends IterableFactory[ArrayBuffer] { - def fromIterator[B](it: Iterator[B]): ArrayBuffer[B] = - if (it.knownLength == 0) { - val elems = new Array[AnyRef](it.knownLength) + def fromIterator[B](it: Iterator[B]): ArrayBuffer[B] = it match { + case Iterator.Concat(fst: ArrayBufferIterator[_], snd: ArrayBufferIterator[_]) => + val elems = new Array[AnyRef](fst.remaining + snd.remaining) + Array.copy(fst.elems, fst.start, elems, 0, fst.remaining) + Array.copy(snd.elems, snd.start, elems, fst.remaining, snd.remaining) + new ArrayBuffer(elems, elems.length) + case it @ Iterator.Partition(underlying, _, buf, _) => + while (underlying.hasNext) it.distribute() + buf.asInstanceOf[ArrayBuffer[B]] + case it if it.remaining >= 0 => + val elems = new Array[AnyRef](it.remaining) for (i <- 0 until elems.length) elems(i) = it.next.asInstanceOf[AnyRef] - new ArrayBuffer(elems) - } - else { + new ArrayBuffer[B](elems, elems.length) + case _ => val buf = new ArrayBuffer[B] while (it.hasNext) buf += it.next buf - } + } + } + + class ArrayBufferIterator[A](val elems: Array[AnyRef], initStart: Int, length: Int) extends RandomAccessIterator[A] { + val limit = length + def apply(n: Int) = elems(initStart + n).asInstanceOf[A] } /** Concrete collection type: View */ @@ -219,7 +237,7 @@ object CollectionStrawMan1 { /** Concrete collection type: String */ implicit class StringOps(val s: String) extends AnyVal with Ops[Char] { def iterator: Iterator[Char] = new RandomAccessIterator[Char] { - override val knownLength = s.length + override val limit = s.length def apply(n: Int) = s.charAt(n) } } @@ -281,7 +299,7 @@ object CollectionStrawMan1 { def flatMap[B](f: A => CanIterate[B]): Iterator[B] = Iterator.FlatMap(this, f) def ++[B >: A](xs: CanIterate[B]): Iterator[B] = Iterator.Concat(this, xs.iterator) def partition(p: A => Boolean): (Iterator[A], Iterator[A]) = { - val lookaheadTrue, lookaheadFalse = new ListBuffer[A] + val lookaheadTrue, lookaheadFalse = new ArrayBuffer[A] (Iterator.Partition(this, p, lookaheadTrue, lookaheadFalse), Iterator.Partition[A](this, !p(_), lookaheadFalse, lookaheadTrue)) } @@ -292,18 +310,26 @@ object CollectionStrawMan1 { while (hasNext) elems = Cons(next, elems) elems.iterator } + + /** If this iterator results from applying a transfomation to another iterator, + * that other iterator, otherwise the iterator itself. + */ def underlying: Iterator[_] = this - def knownLength = -1 + + /** If the number of elements still to be returned by this iterator is known, + * that number, otherwise -1. + */ + def remaining = -1 } object Iterator { val empty: Iterator[Nothing] = new Iterator[Nothing] { def hasNext = false def next = ??? - override val knownLength = 0 + override def remaining = 0 } def apply[A](xs: A*): Iterator[A] = new RandomAccessIterator[A] { - override val knownLength = xs.length + override val limit = xs.length def apply(n: Int) = xs(n) } def nextOnEmpty = throw new NoSuchElementException("next on empty iterator") @@ -311,7 +337,7 @@ object CollectionStrawMan1 { case class Map[A, B](override val underlying: Iterator[A], f: A => B) extends Iterator[B] { def hasNext = underlying.hasNext def next = f(underlying.next) - override val knownLength = underlying.knownLength + override def remaining = underlying.remaining } case class FlatMap[A, B](override val underlying: Iterator[A], f: A => CanIterate[B]) extends Iterator[B] { private var myCurrent: Iterator[B] = Iterator.empty @@ -331,19 +357,18 @@ object CollectionStrawMan1 { } def hasNext = current.hasNext def next = current.next - override val knownLength = - if (underlying.knownLength > 0 && other.knownLength > 0) - underlying.knownLength + other.knownLength + override def remaining = + if (underlying.remaining >= 0 && other.remaining >= 0) + underlying.remaining + other.remaining else -1 } - case class Partition[A](override val underlying: Iterator[A], p: A => Boolean, lookahead: ListBuffer[A], dual: ListBuffer[A]) extends Iterator[A] { + case class Partition[A](override val underlying: Iterator[A], p: A => Boolean, lookahead: ArrayBuffer[A], dual: ArrayBuffer[A]) extends Iterator[A] { + def distribute() = { + val elem = underlying.next + (if (p(elem)) lookahead else dual) += elem + } final def hasNext: Boolean = - lookahead.nonEmpty || - underlying.hasNext && { - val elem = underlying.next - (if (p(elem)) lookahead else dual) += elem - hasNext - } + !lookahead.isEmpty || underlying.hasNext && { distribute(); hasNext } final def next = if (hasNext) { val r = lookahead.head @@ -356,27 +381,27 @@ object CollectionStrawMan1 { def hasNext: Boolean = underlying.hasNext && ( toSkip == 0 || { underlying.next; toSkip -= 1; hasNext }) def next = if (hasNext) underlying.next else nextOnEmpty - override val knownLength = (underlying.knownLength - n) max -1 + override def remaining = (underlying.remaining - toSkip) max -1 } case class Zip[A, B](override val underlying: Iterator[A], other: Iterator[B]) extends Iterator[(A, B)] { def hasNext = underlying.hasNext && other.hasNext def next = (underlying.next, other.next) - override val knownLength = underlying.knownLength min other.knownLength + override def remaining = underlying.remaining min other.remaining } - class Reverse[A](underlying: RandomAccessIterator[A]) extends RandomAccessIterator[A] { - override val knownLength = underlying.knownLength - def apply(n: Int) = underlying.apply(knownLength - 1 - n) + case class Reverse[A](override val underlying: RandomAccessIterator[A]) extends RandomAccessIterator[A] { + def apply(n: Int) = underlying.apply(underlying.limit - 1 - n) + def limit = underlying.remaining } } trait RandomAccessIterator[+A] extends Iterator[A] { self => def apply(n: Int): A - def knownLength: Int - def length: Int = knownLength - private var cur = 0 - def hasNext = cur < knownLength - def next: A = { val r = this(cur); cur += 1; r } - override def drop(n: Int): Iterator[A] = { cur += (n max 0); this } + def limit: Int + var start = 0 + override def remaining = (limit - start) max 0 + def hasNext = start < limit + def next: A = { val r = this(start); start += 1; r } + override def drop(n: Int): Iterator[A] = { start += (n max 0); this } override def reverse: Iterator[A] = new Iterator.Reverse(this) } } diff --git a/tests/run/CollectionTests.check b/tests/run/CollectionTests.check index 7c4f6e77a8a8..6c61d8821444 100644 --- a/tests/run/CollectionTests.check +++ b/tests/run/CollectionTests.check @@ -21,17 +21,17 @@ Cons(3,Cons(2,Cons(1,Nil))) 1 1 Cons(1,Cons(2,Cons(3,Nil))) -Array(2) -Array(1, 3) -Array(3) -Array(true, true, true) -Array(1, -1, 2, -2, 3, -3) -Array(1, 2, 3, 1, 2, 3) -Array(1, 2, 3) +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))) -Array(1, 2, 3, a) -Array((1,true), (2,true), (3,true)) -Array(3, 2, 1) +ArrayBuffer(1, 2, 3, a) +ArrayBuffer((1,true), (2,true), (3,true)) +ArrayBuffer(3, 2, 1) ------- 123 123 From d42f4fee8717a91c845b6b8e1eaa053927f4b7e9 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 5 Oct 2015 17:43:55 +0200 Subject: [PATCH 21/22] Tweaks to conform with API set out in #818. --- .../collections/CollectionStrawMan1.scala | 11 +++----- tests/run/CollectionTests.scala | 28 +++++++++---------- 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/src/strawman/collections/CollectionStrawMan1.scala b/src/strawman/collections/CollectionStrawMan1.scala index 93d4086f254b..2e9e5080ea03 100644 --- a/src/strawman/collections/CollectionStrawMan1.scala +++ b/src/strawman/collections/CollectionStrawMan1.scala @@ -20,9 +20,6 @@ object CollectionStrawMan1 { def iterator: Iterator[A] } - /** Iterator guaranteed to be usable multiple times */ - trait HasIterator[+A] extends CanIterate[A] - /** Base trait for instances that can construct a collection from an iterator */ trait FromIterator[+C[X] <: Iterable[X]] { def fromIterator[B](it: Iterator[B]): C[B] @@ -35,7 +32,7 @@ object CollectionStrawMan1 { } /** Base trait for generic collections */ - trait Iterable[+A] extends HasIterator[A] with FromIterator[Iterable] + trait Iterable[+A] extends CanIterate[A] with FromIterator[Iterable] /** Base trait for sequence collections */ trait Seq[+A] extends Iterable[A] with FromIterator[Seq] { @@ -55,7 +52,7 @@ object CollectionStrawMan1 { def isEmpty: Boolean = !iterator.hasNext def head: A = iterator.next def view: View[A] = new View(iterator) - def collect[C[X] <: Iterable[X]](fi: FromIterator[C]): C[A] = fi.fromIterator(iterator) + def collectAs[C[X] <: Iterable[X]](fi: FromIterator[C]): C[A] = fi.fromIterator(iterator) } /** Transforms returning same collection type */ @@ -213,13 +210,13 @@ object CollectionStrawMan1 { } /** Concrete collection type: View */ - class View[+A](it: => Iterator[A]) extends HasIterator[A] { + class View[+A](it: => Iterator[A]) extends CanIterate[A] { def iterator = it } implicit class ViewOps[A](val v: View[A]) extends AnyVal with Ops[A] { def iterator = v.iterator - def cache = collect(ArrayBuffer).view + def cache = collectAs(ArrayBuffer).view } implicit class ViewMonoTransforms[A](val v: View[A]) diff --git a/tests/run/CollectionTests.scala b/tests/run/CollectionTests.scala index 0421cfbe47ac..3cf611f490b9 100644 --- a/tests/run/CollectionTests.scala +++ b/tests/run/CollectionTests.scala @@ -14,7 +14,7 @@ object Test { val y3: Int = x3 val x4 = xs.head val y4: Int = x4 - val x5 = xs.collect(List) + val x5 = xs.collectAs(List) val y5: List[Int] = x5 val (xs6, xs7) = xs.partition(_ % 2 == 0) val ys6: Seq[Int] = xs6 @@ -65,7 +65,7 @@ object Test { val y3: Int = x3 val x4 = xs.head val y4: Int = x4 - val x5 = xs.collect(List) + val x5 = xs.collectAs(List) val y5: List[Int] = x5 val (xs6, xs7) = xs.partition(_ % 2 == 0) val ys6: View[Int] = xs6 @@ -92,16 +92,16 @@ object Test { println(x3) println(x4) println(x5) - println(xs6.collect(List)) - println(xs7.collect(List)) - println(xs8.collect(List)) - println(xs9.collect(List)) - println(xs10.collect(List)) - println(xs11.collect(List)) - println(xs12.collect(List)) - println(xs13.collect(List)) - println(xs14.collect(List)) - println(xs15.collect(List)) + println(xs6.collectAs(List)) + println(xs7.collectAs(List)) + println(xs8.collectAs(List)) + println(xs9.collectAs(List)) + println(xs10.collectAs(List)) + println(xs11.collectAs(List)) + println(xs12.collectAs(List)) + println(xs13.collectAs(List)) + println(xs14.collectAs(List)) + println(xs15.collectAs(List)) } def stringOps(xs: String) = { @@ -113,7 +113,7 @@ object Test { val y3: Int = x3 val x4 = xs.head val y4: Int = x4 - val x5 = xs.collect(List) + val x5 = xs.collectAs(List) val y5: List[Char] = x5 val (xs6, xs7) = xs.partition(_ % 2 == 0) val ys6: String = xs6 @@ -160,7 +160,7 @@ object Test { def main(args: Array[String]) = { val ints = Cons(1, Cons(2, Cons(3, Nil))) - val intsBuf = ints.collect(ArrayBuffer) + val intsBuf = ints.collectAs(ArrayBuffer) val intsView = ints.view seqOps(ints) seqOps(intsBuf) From 4bc469388a4d870f412932d380c3e04741de5575 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 6 Oct 2015 13:46:01 +0200 Subject: [PATCH 22/22] Rename collectAs -> to --- .../collections/CollectionStrawMan1.scala | 4 +-- tests/run/CollectionTests.scala | 28 +++++++++---------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/strawman/collections/CollectionStrawMan1.scala b/src/strawman/collections/CollectionStrawMan1.scala index 2e9e5080ea03..c127757ad539 100644 --- a/src/strawman/collections/CollectionStrawMan1.scala +++ b/src/strawman/collections/CollectionStrawMan1.scala @@ -52,7 +52,7 @@ object CollectionStrawMan1 { def isEmpty: Boolean = !iterator.hasNext def head: A = iterator.next def view: View[A] = new View(iterator) - def collectAs[C[X] <: Iterable[X]](fi: FromIterator[C]): C[A] = fi.fromIterator(iterator) + def to[C[X] <: Iterable[X]](fi: FromIterator[C]): C[A] = fi.fromIterator(iterator) } /** Transforms returning same collection type */ @@ -216,7 +216,7 @@ object CollectionStrawMan1 { implicit class ViewOps[A](val v: View[A]) extends AnyVal with Ops[A] { def iterator = v.iterator - def cache = collectAs(ArrayBuffer).view + def cache = to(ArrayBuffer).view } implicit class ViewMonoTransforms[A](val v: View[A]) diff --git a/tests/run/CollectionTests.scala b/tests/run/CollectionTests.scala index 3cf611f490b9..bcef547618aa 100644 --- a/tests/run/CollectionTests.scala +++ b/tests/run/CollectionTests.scala @@ -14,7 +14,7 @@ object Test { val y3: Int = x3 val x4 = xs.head val y4: Int = x4 - val x5 = xs.collectAs(List) + val x5 = xs.to(List) val y5: List[Int] = x5 val (xs6, xs7) = xs.partition(_ % 2 == 0) val ys6: Seq[Int] = xs6 @@ -65,7 +65,7 @@ object Test { val y3: Int = x3 val x4 = xs.head val y4: Int = x4 - val x5 = xs.collectAs(List) + val x5 = xs.to(List) val y5: List[Int] = x5 val (xs6, xs7) = xs.partition(_ % 2 == 0) val ys6: View[Int] = xs6 @@ -92,16 +92,16 @@ object Test { println(x3) println(x4) println(x5) - println(xs6.collectAs(List)) - println(xs7.collectAs(List)) - println(xs8.collectAs(List)) - println(xs9.collectAs(List)) - println(xs10.collectAs(List)) - println(xs11.collectAs(List)) - println(xs12.collectAs(List)) - println(xs13.collectAs(List)) - println(xs14.collectAs(List)) - println(xs15.collectAs(List)) + 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) = { @@ -113,7 +113,7 @@ object Test { val y3: Int = x3 val x4 = xs.head val y4: Int = x4 - val x5 = xs.collectAs(List) + val x5 = xs.to(List) val y5: List[Char] = x5 val (xs6, xs7) = xs.partition(_ % 2 == 0) val ys6: String = xs6 @@ -160,7 +160,7 @@ object Test { def main(args: Array[String]) = { val ints = Cons(1, Cons(2, Cons(3, Nil))) - val intsBuf = ints.collectAs(ArrayBuffer) + val intsBuf = ints.to(ArrayBuffer) val intsView = ints.view seqOps(ints) seqOps(intsBuf)