From 6ec48133af6fd323f9d05c1b728b5f116cd35f20 Mon Sep 17 00:00:00 2001 From: Julien Richard-Foy Date: Wed, 21 Feb 2018 16:46:27 +0100 Subject: [PATCH 01/12] Introduce HasSeqOps HasSeqOps is a type class whose instances let users retrieve the associated collection type and collection type constructor of any type that can be seen as a sequence. --- .../collection/decorators/SeqDecorator.scala | 30 +++++++------- .../collection/decorators/package.scala | 5 ++- .../collection/decorators/views.scala | 6 ++- .../decorators/SeqDecoratorTest.scala | 19 ++++++++- .../collection/generic/HasSeqOps.scala | 40 +++++++++++++++++++ 5 files changed, 80 insertions(+), 20 deletions(-) create mode 100644 collections/src/main/scala/strawman/collection/generic/HasSeqOps.scala diff --git a/collections-contrib/src/main/scala/strawman/collection/decorators/SeqDecorator.scala b/collections-contrib/src/main/scala/strawman/collection/decorators/SeqDecorator.scala index 87480e10d5..efc582c343 100644 --- a/collections-contrib/src/main/scala/strawman/collection/decorators/SeqDecorator.scala +++ b/collections-contrib/src/main/scala/strawman/collection/decorators/SeqDecorator.scala @@ -1,30 +1,30 @@ package strawman.collection package decorators +import strawman.collection.generic.HasSeqOps + /** - * @param `this` the decorated collection - * @tparam A the type of elements - * @tparam CC the collection type constructor - * - * @define coll sequence - * @define Coll `Seq` + * @param coll the decorated collection + * @param seq evidence that type `C` is a sequence */ -class SeqDecorator[A, CC[X] <: SeqOps[X, CC, _]](`this`: CC[A]) { +class SeqDecorator[C](coll: C)(implicit val seq: HasSeqOps[C]) { + + protected val `this` = seq.conversion(coll) /** Adds the element `sep` between each element of the sequence. * If the sequence has less than two elements, the collection is unchanged. * * @param sep the element to intersperse - * @tparam B the element type of the returned $coll - * @return a new collection of type `$Coll` consisting of all elements of this $coll + * @tparam B the element type of the returned collection + * @return a new collection consisting of all elements of this collection * interspersed with the element `sep` * * @example {{{ * List(1, 2, 3, 4).intersperse(0) = List(1, 0, 2, 0, 3, 0, 4) * }}} */ - def intersperse[B >: A](sep: B): CC[B] = - `this`.iterableFactory.from(new View.Intersperse(`this`.toIterable, sep)) + def intersperse[B >: seq.A](sep: B): seq.CC[B] = + `this`.iterableFactory.from(new View.Intersperse(`this`, sep)) /** Adds the element `sep` between each element of the sequence, * prepending `start` and appending `end`. @@ -33,15 +33,15 @@ class SeqDecorator[A, CC[X] <: SeqOps[X, CC, _]](`this`: CC[A]) { * @param start the element to prepend * @param sep the element to intersperse * @param end the element to append - * @tparam B the element type of the returned $coll - * @return a new collection of type `$Coll` consisting of all elements of this $coll + * @tparam B the element type of the returned collection + * @return a new collection consisting of all elements of this collection * interspersed with the element `sep`, beginning with `start` and ending with `end` * * @example {{{ * List(1, 2, 3, 4).intersperse(-1, 0, 5) => List(-1, 1, 0, 2, 0, 3, 0, 4, 5) * }}} */ - def intersperse[B >: A, C](start: B, sep: B, end: B): CC[B] = - `this`.iterableFactory.from(new View.IntersperseSurround(`this`.toIterable, start, sep, end)) + def intersperse[B >: seq.A](start: B, sep: B, end: B): seq.CC[B] = + `this`.iterableFactory.from(new View.IntersperseSurround(`this`, start, sep, end)) } diff --git a/collections-contrib/src/main/scala/strawman/collection/decorators/package.scala b/collections-contrib/src/main/scala/strawman/collection/decorators/package.scala index f54af8a393..96eefe3977 100644 --- a/collections-contrib/src/main/scala/strawman/collection/decorators/package.scala +++ b/collections-contrib/src/main/scala/strawman/collection/decorators/package.scala @@ -1,5 +1,7 @@ package strawman.collection +import strawman.collection.generic.HasSeqOps + import scala.language.implicitConversions package object decorators { @@ -10,8 +12,7 @@ package object decorators { implicit def iterableDecorator[A](it: Iterable[A]): IterableDecorator[A] = new IterableDecorator(it) - implicit def SeqDecorator[A, CC[X] <: SeqOps[X, CC, _]](seq: CC[A]): SeqDecorator[A, CC] = - new SeqDecorator[A, CC](seq) + implicit def SeqDecorator[A, C](seq: C)(implicit ev: HasSeqOps[C]): SeqDecorator[C] = new SeqDecorator[C](seq) implicit def MapDecorator[K, V](map: Map[K, V]): MapDecorator[K, V] { val `this`: map.type } = new MapDecorator[K, V] { val `this`: map.type = map } diff --git a/collections-contrib/src/main/scala/strawman/collection/decorators/views.scala b/collections-contrib/src/main/scala/strawman/collection/decorators/views.scala index 467739f992..25c581db19 100644 --- a/collections-contrib/src/main/scala/strawman/collection/decorators/views.scala +++ b/collections-contrib/src/main/scala/strawman/collection/decorators/views.scala @@ -4,13 +4,15 @@ package decorators /** Views used by decorators */ object View { - class Intersperse[A](underlying: Iterable[A], sep: A) extends View[A] { + type SomeIterableOps[+A] = IterableOps[A, AnyConstr, _] + + class Intersperse[A](underlying: SomeIterableOps[A], sep: A) extends View[A] { def iterator(): Iterator[A] = underlying.iterator().intersperse(sep) override def knownSize: Int = if (underlying.knownSize > 0) (2 * underlying.knownSize - 1) else underlying.knownSize } - class IntersperseSurround[A](underlying: Iterable[A], start: A, sep: A, end: A) extends View[A] { + class IntersperseSurround[A](underlying: SomeIterableOps[A], start: A, sep: A, end: A) extends View[A] { def iterator(): Iterator[A] = underlying.iterator().intersperse(start, sep, end) override def knownSize: Int = diff --git a/collections-contrib/src/test/scala/strawman/collection/decorators/SeqDecoratorTest.scala b/collections-contrib/src/test/scala/strawman/collection/decorators/SeqDecoratorTest.scala index 7ed1896d94..124422c863 100644 --- a/collections-contrib/src/test/scala/strawman/collection/decorators/SeqDecoratorTest.scala +++ b/collections-contrib/src/test/scala/strawman/collection/decorators/SeqDecoratorTest.scala @@ -3,8 +3,9 @@ package decorators import org.junit.Assert.assertEquals import org.junit.Test +import strawman.collection.immutable._ -import strawman.collection.immutable.List +import scala.Predef.{intArrayOps => _, genericArrayOps => _, wrapIntArray => _, genericWrapArray => _, $conforms} class SeqDecoratorTest { @@ -17,4 +18,20 @@ class SeqDecoratorTest { assertEquals(List(0, 1, 2), List(1).intersperse(0, 5, 2)) } + // This test just checks that there is no compilation error + @Test def genericDecorator(): Unit = { + val list = List(1, 2, 3) + val vector = Vector(1, 2, 3) + val array = Array(1, 2, 3) + val string = "foo" + list.intersperse(0) + list.view.intersperse(0) + vector.intersperse(0) + vector.view.intersperse(0) + array.intersperse(0) + array.view.intersperse(0) + string.intersperse(' ') + string.view.intersperse(' ') + } + } diff --git a/collections/src/main/scala/strawman/collection/generic/HasSeqOps.scala b/collections/src/main/scala/strawman/collection/generic/HasSeqOps.scala new file mode 100644 index 0000000000..cc6f29644e --- /dev/null +++ b/collections/src/main/scala/strawman/collection/generic/HasSeqOps.scala @@ -0,0 +1,40 @@ +package strawman.collection +package generic + +/** Type class witnessing that a collection representation type `Repr` has + * elements of type `A` and has a conversion to `SeqOps[A, CC, C]`. + * + * This type enables simple enrichment of `Seq`s with extension methods which + * can make full use of the mechanics of the Scala collections framework in + * their implementation. + * + * @see [[scala.collection.generic.HasIterableOps]] + */ +trait HasSeqOps[Repr] { + /** The type of elements we can traverse over. */ + type A + + /** The type constructor returned by transformation operations that might change the type of elements */ + type CC[_] + + /** The type returned by transformation operations that keep the same type of elements */ + type C + + /** A conversion from the representation type `Repr` to `SeqOps[A, CC, C]`. */ + val conversion: Repr => SeqOps[A, CC, C] +} + +object HasSeqOps { + import scala.language.higherKinds + + // implicit instance that lets the type system unify `C` with `SeqOps[A0, CC0, C0]` + implicit def seqOps[C, A0, CC0[_], C0](implicit + conv: C => SeqOps[A0, CC0, C0] + ): HasSeqOps[C] { type A = A0; type CC[A] = CC0[A]; type C = C0 } = + new HasSeqOps[C] { + type A = A0 + type CC[A] = CC0[A] + type C = C0 + val conversion = conv + } +} From 8cf2b049adbeca67f445bf3a6a941795ceda2247 Mon Sep 17 00:00:00 2001 From: Julien Richard-Foy Date: Thu, 22 Feb 2018 13:32:43 +0100 Subject: [PATCH 02/12] Preserve more type information --- .../collection/decorators/SeqDecorator.scala | 12 ++-- .../collection/decorators/package.scala | 6 +- .../decorators/SeqDecoratorTest.scala | 10 +++- .../collection/generic/HasSeqOps.scala | 56 ++++++++++++++++--- 4 files changed, 66 insertions(+), 18 deletions(-) diff --git a/collections-contrib/src/main/scala/strawman/collection/decorators/SeqDecorator.scala b/collections-contrib/src/main/scala/strawman/collection/decorators/SeqDecorator.scala index efc582c343..7f78584527 100644 --- a/collections-contrib/src/main/scala/strawman/collection/decorators/SeqDecorator.scala +++ b/collections-contrib/src/main/scala/strawman/collection/decorators/SeqDecorator.scala @@ -3,13 +3,13 @@ package decorators import strawman.collection.generic.HasSeqOps -/** - * @param coll the decorated collection - * @param seq evidence that type `C` is a sequence - */ -class SeqDecorator[C](coll: C)(implicit val seq: HasSeqOps[C]) { +trait SeqDecorator[C] { - protected val `this` = seq.conversion(coll) + /** Evidence that type `C` is a sequence */ + val seq: HasSeqOps[C] + + /** Decorated collection */ + val `this`: SeqOps[seq.A, seq.CC, seq.C] /** Adds the element `sep` between each element of the sequence. * If the sequence has less than two elements, the collection is unchanged. diff --git a/collections-contrib/src/main/scala/strawman/collection/decorators/package.scala b/collections-contrib/src/main/scala/strawman/collection/decorators/package.scala index 96eefe3977..6d1e1e2d97 100644 --- a/collections-contrib/src/main/scala/strawman/collection/decorators/package.scala +++ b/collections-contrib/src/main/scala/strawman/collection/decorators/package.scala @@ -12,7 +12,11 @@ package object decorators { implicit def iterableDecorator[A](it: Iterable[A]): IterableDecorator[A] = new IterableDecorator(it) - implicit def SeqDecorator[A, C](seq: C)(implicit ev: HasSeqOps[C]): SeqDecorator[C] = new SeqDecorator[C](seq) + implicit def SeqDecorator[C](coll: C)(implicit seq0: HasSeqOps[C]): SeqDecorator[C] { val seq: seq0.type } = + new SeqDecorator[C] { + val seq: seq0.type = seq0 + val `this`: SeqOps[seq.A, seq.CC, seq.C] = seq0.conversion(coll) + } implicit def MapDecorator[K, V](map: Map[K, V]): MapDecorator[K, V] { val `this`: map.type } = new MapDecorator[K, V] { val `this`: map.type = map } diff --git a/collections-contrib/src/test/scala/strawman/collection/decorators/SeqDecoratorTest.scala b/collections-contrib/src/test/scala/strawman/collection/decorators/SeqDecoratorTest.scala index 124422c863..02e0ed99f2 100644 --- a/collections-contrib/src/test/scala/strawman/collection/decorators/SeqDecoratorTest.scala +++ b/collections-contrib/src/test/scala/strawman/collection/decorators/SeqDecoratorTest.scala @@ -20,18 +20,22 @@ class SeqDecoratorTest { // This test just checks that there is no compilation error @Test def genericDecorator(): Unit = { - val list = List(1, 2, 3) val vector = Vector(1, 2, 3) val array = Array(1, 2, 3) val string = "foo" - list.intersperse(0) + val list = List(1, 2, 3) + val result = list.intersperse(0) + typed[List[Int]](result) list.view.intersperse(0) vector.intersperse(0) vector.view.intersperse(0) - array.intersperse(0) + val result2 = array.intersperse(0) + typed[IndexedSeq[Int]](result2) // Note that the result type is IndexedSeq here instead of Array array.view.intersperse(0) string.intersperse(' ') string.view.intersperse(' ') } + def typed[T](t: => T): Unit = () + } diff --git a/collections/src/main/scala/strawman/collection/generic/HasSeqOps.scala b/collections/src/main/scala/strawman/collection/generic/HasSeqOps.scala index cc6f29644e..16ea0596d7 100644 --- a/collections/src/main/scala/strawman/collection/generic/HasSeqOps.scala +++ b/collections/src/main/scala/strawman/collection/generic/HasSeqOps.scala @@ -1,6 +1,9 @@ package strawman.collection package generic +import scala.{ Array, Char } +import scala.Predef.String + /** Type class witnessing that a collection representation type `Repr` has * elements of type `A` and has a conversion to `SeqOps[A, CC, C]`. * @@ -27,14 +30,51 @@ trait HasSeqOps[Repr] { object HasSeqOps { import scala.language.higherKinds - // implicit instance that lets the type system unify `C` with `SeqOps[A0, CC0, C0]` - implicit def seqOps[C, A0, CC0[_], C0](implicit - conv: C => SeqOps[A0, CC0, C0] - ): HasSeqOps[C] { type A = A0; type CC[A] = CC0[A]; type C = C0 } = - new HasSeqOps[C] { + type Aux[Repr, A0, CC0[_], C0] = HasSeqOps[Repr] { type A = A0; type CC[X] = CC0[X]; type C = C0 } + + // we want to provide implicit instances that unify all possible types `X` with a `SeqOps[A, CC, C]` + // 1. Seq collections + implicit def seqHasSeqOps[CC0[X] <: SeqOps[X, CC0, CC0[X]], A0]: HasSeqOps.Aux[CC0[A0], A0, CC0, CC0[A0]] = + new HasSeqOps[CC0[A0]] { + type A = A0 + type CC[X] = CC0[X] + type C = CC0[A] + val conversion: CC0[A0] => SeqOps[A0, CC0, CC0[A0]] = c => c + } + + // 2. Seq views + implicit def viewHasSeqOps[CC0[X] <: SeqView[X], A0]: HasSeqOps.Aux[CC0[A0], A0, View, View[A0]] = + new HasSeqOps[CC0[A0]] { + type A = A0 + type CC[X] = View[X] + type C = View[A0] + val conversion: CC0[A0] => SeqOps[A0, View, View[A0]] = c => c + } + + // 3. String + implicit def stringHasSeqOps: HasSeqOps.Aux[String, Char, immutable.IndexedSeq, String] = + new HasSeqOps[String] { + type A = Char + type CC[X] = immutable.IndexedSeq[X] + type C = String + val conversion: String => SeqOps[Char, immutable.IndexedSeq, String] = stringToStringOps + } + + // 4. StringView + implicit def stringViewHasSeqOps: HasSeqOps.Aux[StringView, Char, View, View[Char]] = + new HasSeqOps[StringView] { + type A = Char + type CC[X] = View[X] + type C = View[Char] + val conversion: StringView => SeqOps[Char, View, View[Char]] = v => v + } + + // 5. Array + implicit def arrayHasSeqOps[A0]: HasSeqOps.Aux[Array[A0], A0, immutable.IndexedSeq, Array[A0]] = + new HasSeqOps[Array[A0]] { type A = A0 - type CC[A] = CC0[A] - type C = C0 - val conversion = conv + type CC[X] = immutable.IndexedSeq[X] + type C = Array[A0] + val conversion: Array[A0] => SeqOps[A0, immutable.IndexedSeq, Array[A0]] = arrayToArrayOps } } From 7f1e0e02f4106099c369ab379fab72ae0e42c20f Mon Sep 17 00:00:00 2001 From: Julien Richard-Foy Date: Thu, 22 Feb 2018 13:47:35 +0100 Subject: [PATCH 03/12] Use a class with a type parameter instead of a trait with an abstract val --- .../collection/decorators/SeqDecorator.scala | 13 +++++++------ .../strawman/collection/decorators/package.scala | 7 ++----- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/collections-contrib/src/main/scala/strawman/collection/decorators/SeqDecorator.scala b/collections-contrib/src/main/scala/strawman/collection/decorators/SeqDecorator.scala index 7f78584527..5cf5656b80 100644 --- a/collections-contrib/src/main/scala/strawman/collection/decorators/SeqDecorator.scala +++ b/collections-contrib/src/main/scala/strawman/collection/decorators/SeqDecorator.scala @@ -3,13 +3,14 @@ package decorators import strawman.collection.generic.HasSeqOps -trait SeqDecorator[C] { +/** + * @param coll the decorated collection + * @param seq evidence that type `C` is a sequencue + * @tparam C type of the decorated collection (e.g. `List[Int]`, `String`, etc.) + */ +class SeqDecorator[C, S <: HasSeqOps[C]](coll: C)(implicit val seq: S) { - /** Evidence that type `C` is a sequence */ - val seq: HasSeqOps[C] - - /** Decorated collection */ - val `this`: SeqOps[seq.A, seq.CC, seq.C] + private[this] val `this`: SeqOps[seq.A, seq.CC, seq.C] = seq.conversion(coll) /** Adds the element `sep` between each element of the sequence. * If the sequence has less than two elements, the collection is unchanged. diff --git a/collections-contrib/src/main/scala/strawman/collection/decorators/package.scala b/collections-contrib/src/main/scala/strawman/collection/decorators/package.scala index 6d1e1e2d97..25154a0514 100644 --- a/collections-contrib/src/main/scala/strawman/collection/decorators/package.scala +++ b/collections-contrib/src/main/scala/strawman/collection/decorators/package.scala @@ -12,11 +12,8 @@ package object decorators { implicit def iterableDecorator[A](it: Iterable[A]): IterableDecorator[A] = new IterableDecorator(it) - implicit def SeqDecorator[C](coll: C)(implicit seq0: HasSeqOps[C]): SeqDecorator[C] { val seq: seq0.type } = - new SeqDecorator[C] { - val seq: seq0.type = seq0 - val `this`: SeqOps[seq.A, seq.CC, seq.C] = seq0.conversion(coll) - } + implicit def SeqDecorator[C](coll: C)(implicit seq: HasSeqOps[C]): SeqDecorator[C, seq.type] = + new SeqDecorator[C, seq.type](coll)(seq) implicit def MapDecorator[K, V](map: Map[K, V]): MapDecorator[K, V] { val `this`: map.type } = new MapDecorator[K, V] { val `this`: map.type = map } From b7a0d2561a215ea2f67ee02887d2027a15748573 Mon Sep 17 00:00:00 2001 From: Julien Richard-Foy Date: Thu, 22 Feb 2018 17:04:16 +0100 Subject: [PATCH 04/12] Use BuildFrom to infer a more precise result type --- .../collection/decorators/SeqDecorator.scala | 12 +++++------- .../collection/decorators/SeqDecoratorTest.scala | 2 +- .../main/scala/strawman/collection/BuildFrom.scala | 6 ++++++ 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/collections-contrib/src/main/scala/strawman/collection/decorators/SeqDecorator.scala b/collections-contrib/src/main/scala/strawman/collection/decorators/SeqDecorator.scala index 5cf5656b80..35b0803819 100644 --- a/collections-contrib/src/main/scala/strawman/collection/decorators/SeqDecorator.scala +++ b/collections-contrib/src/main/scala/strawman/collection/decorators/SeqDecorator.scala @@ -8,9 +8,7 @@ import strawman.collection.generic.HasSeqOps * @param seq evidence that type `C` is a sequencue * @tparam C type of the decorated collection (e.g. `List[Int]`, `String`, etc.) */ -class SeqDecorator[C, S <: HasSeqOps[C]](coll: C)(implicit val seq: S) { - - private[this] val `this`: SeqOps[seq.A, seq.CC, seq.C] = seq.conversion(coll) +class SeqDecorator[C, S <: HasSeqOps[C]](coll: C)(implicit seq: S) { /** Adds the element `sep` between each element of the sequence. * If the sequence has less than two elements, the collection is unchanged. @@ -24,8 +22,8 @@ class SeqDecorator[C, S <: HasSeqOps[C]](coll: C)(implicit val seq: S) { * List(1, 2, 3, 4).intersperse(0) = List(1, 0, 2, 0, 3, 0, 4) * }}} */ - def intersperse[B >: seq.A](sep: B): seq.CC[B] = - `this`.iterableFactory.from(new View.Intersperse(`this`, sep)) + def intersperse[B >: seq.A, That](sep: B)(implicit bf: BuildFrom[C, B, That]): That = + bf.fromSpecificIterable(coll)(new View.Intersperse(seq.conversion(coll), sep)) /** Adds the element `sep` between each element of the sequence, * prepending `start` and appending `end`. @@ -42,7 +40,7 @@ class SeqDecorator[C, S <: HasSeqOps[C]](coll: C)(implicit val seq: S) { * List(1, 2, 3, 4).intersperse(-1, 0, 5) => List(-1, 1, 0, 2, 0, 3, 0, 4, 5) * }}} */ - def intersperse[B >: seq.A](start: B, sep: B, end: B): seq.CC[B] = - `this`.iterableFactory.from(new View.IntersperseSurround(`this`, start, sep, end)) + def intersperse[B >: seq.A, That](start: B, sep: B, end: B)(implicit bf: BuildFrom[C, B, That]): That = + bf.fromSpecificIterable(coll)(new View.IntersperseSurround(seq.conversion(coll), start, sep, end)) } diff --git a/collections-contrib/src/test/scala/strawman/collection/decorators/SeqDecoratorTest.scala b/collections-contrib/src/test/scala/strawman/collection/decorators/SeqDecoratorTest.scala index 02e0ed99f2..6b4391043f 100644 --- a/collections-contrib/src/test/scala/strawman/collection/decorators/SeqDecoratorTest.scala +++ b/collections-contrib/src/test/scala/strawman/collection/decorators/SeqDecoratorTest.scala @@ -30,7 +30,7 @@ class SeqDecoratorTest { vector.intersperse(0) vector.view.intersperse(0) val result2 = array.intersperse(0) - typed[IndexedSeq[Int]](result2) // Note that the result type is IndexedSeq here instead of Array + typed[Array[Int]](result2) array.view.intersperse(0) string.intersperse(' ') string.view.intersperse(' ') diff --git a/collections/src/main/scala/strawman/collection/BuildFrom.scala b/collections/src/main/scala/strawman/collection/BuildFrom.scala index 40edb7847c..119b774b17 100644 --- a/collections/src/main/scala/strawman/collection/BuildFrom.scala +++ b/collections/src/main/scala/strawman/collection/BuildFrom.scala @@ -58,6 +58,12 @@ object BuildFrom extends BuildFromLowPriority { def newBuilder(from: Array[_]): Builder[A, Array[A]] = Factory.arrayFactory[A].newBuilder() } + implicit def buildFrom[A, B]: BuildFrom[View[A], B, View[B]] = + new BuildFrom[View[A], B, View[B]] { + def fromSpecificIterable(from: View[A])(it: Iterable[B]): View[B] = View.from(it) + def newBuilder(from: View[A]): Builder[B, View[B]] = View.newBuilder() + } + } trait BuildFromLowPriority { From a8ce058ff0cd07e6f6c189d8a6edf91019b29eba Mon Sep 17 00:00:00 2001 From: Julien Richard-Foy Date: Tue, 27 Feb 2018 09:59:11 +0100 Subject: [PATCH 05/12] Introduce HasImmutableMapOps --- .../decorators/ImmutableMapDecorator.scala | 13 +++-- .../collection/decorators/SeqDecorator.scala | 2 +- .../collection/decorators/package.scala | 6 +- .../generic/HasImmutableMapOps.scala | 58 +++++++++++++++++++ .../collection/generic/HasSeqOps.scala | 1 + 5 files changed, 71 insertions(+), 9 deletions(-) create mode 100644 collections/src/main/scala/strawman/collection/generic/HasImmutableMapOps.scala diff --git a/collections-contrib/src/main/scala/strawman/collection/decorators/ImmutableMapDecorator.scala b/collections-contrib/src/main/scala/strawman/collection/decorators/ImmutableMapDecorator.scala index d0f24b3089..f045da4c85 100644 --- a/collections-contrib/src/main/scala/strawman/collection/decorators/ImmutableMapDecorator.scala +++ b/collections-contrib/src/main/scala/strawman/collection/decorators/ImmutableMapDecorator.scala @@ -2,7 +2,9 @@ package strawman package collection package decorators -class ImmutableMapDecorator[K, V, CC[X, +Y] <: immutable.Map[X, Y]](`this`: CC[K, V]) { +import strawman.collection.generic.HasImmutableMapOps + +class ImmutableMapDecorator[C, M <: HasImmutableMapOps[C]](coll: C)(implicit val map: M) { /** * Updates an existing binding or create a new one according to the @@ -23,15 +25,16 @@ class ImmutableMapDecorator[K, V, CC[X, +Y] <: immutable.Map[X, Y]](`this`: CC[K * * @return A new updated `Map` */ - def updatedWith[C](key: K)(f: PartialFunction[Option[V], Option[V]])(implicit bf: BuildFrom[CC[K, V], (K, V), C]): C = { + def updatedWith[That](key: map.K)(f: PartialFunction[Option[map.V], Option[map.V]])(implicit bf: BuildFrom[C, (map.K, map.V), That]): That = { val pf = f.lift + val `this` = map.conversion(coll) val previousValue = `this`.get(key) pf(previousValue) match { - case None => bf.fromSpecificIterable(`this`)(`this`) + case None => bf.fromSpecificIterable(coll)(`this`.toIterable) case Some(result) => result match { - case None => bf.fromSpecificIterable(`this`)(`this` - key) - case Some(v) => bf.fromSpecificIterable(`this`)(`this` + (key -> v)) + case None => bf.fromSpecificIterable(coll)((`this` - key).toIterable) + case Some(v) => bf.fromSpecificIterable(coll)((`this` + (key -> v)).toIterable) } } } diff --git a/collections-contrib/src/main/scala/strawman/collection/decorators/SeqDecorator.scala b/collections-contrib/src/main/scala/strawman/collection/decorators/SeqDecorator.scala index 35b0803819..8f931036d4 100644 --- a/collections-contrib/src/main/scala/strawman/collection/decorators/SeqDecorator.scala +++ b/collections-contrib/src/main/scala/strawman/collection/decorators/SeqDecorator.scala @@ -5,7 +5,7 @@ import strawman.collection.generic.HasSeqOps /** * @param coll the decorated collection - * @param seq evidence that type `C` is a sequencue + * @param seq evidence that type `C` is a sequence * @tparam C type of the decorated collection (e.g. `List[Int]`, `String`, etc.) */ class SeqDecorator[C, S <: HasSeqOps[C]](coll: C)(implicit seq: S) { diff --git a/collections-contrib/src/main/scala/strawman/collection/decorators/package.scala b/collections-contrib/src/main/scala/strawman/collection/decorators/package.scala index 25154a0514..c22903c5bc 100644 --- a/collections-contrib/src/main/scala/strawman/collection/decorators/package.scala +++ b/collections-contrib/src/main/scala/strawman/collection/decorators/package.scala @@ -1,6 +1,6 @@ package strawman.collection -import strawman.collection.generic.HasSeqOps +import strawman.collection.generic.{HasImmutableMapOps, HasSeqOps} import scala.language.implicitConversions @@ -18,8 +18,8 @@ package object decorators { implicit def MapDecorator[K, V](map: Map[K, V]): MapDecorator[K, V] { val `this`: map.type } = new MapDecorator[K, V] { val `this`: map.type = map } - implicit def ImmutableMapDecorator[K, V, CC[X, +Y] <: immutable.Map[X, Y]](map: CC[K, V]): ImmutableMapDecorator[K, V, CC] = - new ImmutableMapDecorator[K, V, CC](map) + implicit def ImmutableMapDecorator[C](coll: C)(implicit map: HasImmutableMapOps[C]): ImmutableMapDecorator[C, map.type] = + new ImmutableMapDecorator[C, map.type](coll)(map) implicit def MutableMapDecorator[K, V](map: mutable.Map[K, V]): MutableMapDecorator[K, V] = new MutableMapDecorator[K, V](map) diff --git a/collections/src/main/scala/strawman/collection/generic/HasImmutableMapOps.scala b/collections/src/main/scala/strawman/collection/generic/HasImmutableMapOps.scala new file mode 100644 index 0000000000..a723d88646 --- /dev/null +++ b/collections/src/main/scala/strawman/collection/generic/HasImmutableMapOps.scala @@ -0,0 +1,58 @@ +package strawman.collection +package generic + +/** + * Type class witnessing that a collection representation type `Repr` has + * a conversion to `immutable.MapOps[K, V, CC, C]`, for some type `K`, + * `V`, `CC[_]` and `C` + * @see [[scala.collection.generic.HasIterableOps]] + */ +trait HasImmutableMapOps[Repr] { + + /** The type of keys */ + type K + + /** The type of values */ + type V + + /** The type constructor returned by transformation operations that might change the types of key and value */ + type CC[X, +Y] <: immutable.MapOps[X, Y, CC, _] + + /** The type returned by transformation operations that keep the same type of elements */ + type C <: immutable.MapOps[K, V, CC, C] + + /** A conversion from the representation type `Repr` to `immutable.MapOps[K, V, CC, C]` */ + val conversion: Repr => immutable.MapOps[K, V, CC, C] +} + +object HasImmutableMapOps { + + /** A type alias for `HasImmutableMapOps` that moves type members to type parameters */ + type Aux[Repr, K0, V0, CC0[X, +Y] <: immutable.MapOps[X, Y, CC0, _], C0 <: immutable.MapOps[K0, V0, CC0, C0]] = HasImmutableMapOps[Repr] { + type K = K0 + type V = V0 + type CC[X, +Y] = CC0[X, Y] + type C = C0 + } + + // 1. Map collections + implicit def mapHasMapOps[CC0[X, +Y] <: immutable.MapOps[X, Y, CC0, CC0[X, Y]], K0, V0]: HasImmutableMapOps.Aux[CC0[K0, V0], K0, V0, CC0, CC0[K0, V0]] = + new HasImmutableMapOps[CC0[K0, V0]] { + type K = K0 + type V = V0 + type CC[X, +Y] = CC0[X, Y] + type C = CC0[K, V] + val conversion: CC0[K0, V0] => immutable.MapOps[K0, V0, CC0, CC0[K0, V0]] = c => c + } + + // 2. Sorted Map collections + implicit def sortedMapHasMapOps[CC0[X, +Y] <: immutable.Map[X, Y] with immutable.SortedMapOps[X, Y, CC0, CC0[X, Y]], K0, V0]: HasImmutableMapOps.Aux[CC0[K0, V0], K0, V0, immutable.Map, CC0[K0, V0]] = + new HasImmutableMapOps[CC0[K0, V0]] { + type K = K0 + type V = V0 + type CC[X, +Y] = immutable.Map[X, Y] + type C = CC0[K, V] + val conversion: CC0[K0, V0] => immutable.MapOps[K0, V0, immutable.Map, CC0[K0, V0]] = c => c + } + +} \ No newline at end of file diff --git a/collections/src/main/scala/strawman/collection/generic/HasSeqOps.scala b/collections/src/main/scala/strawman/collection/generic/HasSeqOps.scala index 16ea0596d7..8d018d93b3 100644 --- a/collections/src/main/scala/strawman/collection/generic/HasSeqOps.scala +++ b/collections/src/main/scala/strawman/collection/generic/HasSeqOps.scala @@ -30,6 +30,7 @@ trait HasSeqOps[Repr] { object HasSeqOps { import scala.language.higherKinds + /** A type alias for `HasSeqOps` that moves type members to type parameters */ type Aux[Repr, A0, CC0[_], C0] = HasSeqOps[Repr] { type A = A0; type CC[X] = CC0[X]; type C = C0 } // we want to provide implicit instances that unify all possible types `X` with a `SeqOps[A, CC, C]` From e3478c43532010434523699314aec8bd46581d38 Mon Sep 17 00:00:00 2001 From: Julien Richard-Foy Date: Tue, 27 Feb 2018 11:08:23 +0100 Subject: [PATCH 06/12] Introduce HasIterableOps --- .../decorators/IterableDecorator.scala | 11 +++-- .../collection/decorators/package.scala | 10 ++--- .../decorators/SeqDecoratorTest.scala | 7 ++- .../collection/generic/HasIterableOps.scala | 43 +++++++++++++++++++ .../collection/generic/HasSeqOps.scala | 14 +++++- 5 files changed, 73 insertions(+), 12 deletions(-) create mode 100644 collections/src/main/scala/strawman/collection/generic/HasIterableOps.scala diff --git a/collections-contrib/src/main/scala/strawman/collection/decorators/IterableDecorator.scala b/collections-contrib/src/main/scala/strawman/collection/decorators/IterableDecorator.scala index 5d9fc8942c..693c3db220 100644 --- a/collections-contrib/src/main/scala/strawman/collection/decorators/IterableDecorator.scala +++ b/collections-contrib/src/main/scala/strawman/collection/decorators/IterableDecorator.scala @@ -2,7 +2,9 @@ package strawman package collection package decorators -class IterableDecorator[A](val `this`: Iterable[A]) extends AnyVal { +import strawman.collection.generic.HasIterableOps + +class IterableDecorator[C, I <: HasIterableOps[C]](coll: C)(implicit val it: I) { /** * Left to right fold that stops if the combination function `op` @@ -14,8 +16,8 @@ class IterableDecorator[A](val `this`: Iterable[A]) extends AnyVal { * going left to right with the start value `z` on the left, and stopping when * all the elements have been traversed or earlier if the operator returns `None` */ - def foldSomeLeft[B](z: B)(op: (B, A) => Option[B]): B = - `this`.iterator().foldSomeLeft(z)(op) + def foldSomeLeft[B](z: B)(op: (B, it.A) => Option[B]): B = + it.conversion(coll).iterator().foldSomeLeft(z)(op) /** * Right to left fold that can be interrupted before traversing the whole collection. @@ -28,6 +30,7 @@ class IterableDecorator[A](val `this`: Iterable[A]) extends AnyVal { * then `result` is returned without iterating further; if it returns `Right(f)`, the function * `f` is applied to the previous result to produce the new result and the fold continues. */ - def lazyFoldRight[B](z: B)(op: A => Either[B, B => B]): B = `this`.iterator().lazyFoldRight(z)(op) + def lazyFoldRight[B](z: B)(op: it.A => Either[B, B => B]): B = + it.conversion(coll).iterator().lazyFoldRight(z)(op) } diff --git a/collections-contrib/src/main/scala/strawman/collection/decorators/package.scala b/collections-contrib/src/main/scala/strawman/collection/decorators/package.scala index c22903c5bc..e291b138df 100644 --- a/collections-contrib/src/main/scala/strawman/collection/decorators/package.scala +++ b/collections-contrib/src/main/scala/strawman/collection/decorators/package.scala @@ -1,6 +1,6 @@ package strawman.collection -import strawman.collection.generic.{HasImmutableMapOps, HasSeqOps} +import strawman.collection.generic.{HasImmutableMapOps, HasIterableOps, HasSeqOps} import scala.language.implicitConversions @@ -9,17 +9,17 @@ package object decorators { implicit def iteratorDecorator[A](it: Iterator[A]): IteratorDecorator[A] = new IteratorDecorator[A](it) - implicit def iterableDecorator[A](it: Iterable[A]): IterableDecorator[A] = - new IterableDecorator(it) + implicit def iterableDecorator[C](coll: C)(implicit it: HasIterableOps[C]): IterableDecorator[C, it.type] = + new IterableDecorator(coll)(it) implicit def SeqDecorator[C](coll: C)(implicit seq: HasSeqOps[C]): SeqDecorator[C, seq.type] = - new SeqDecorator[C, seq.type](coll)(seq) + new SeqDecorator(coll)(seq) implicit def MapDecorator[K, V](map: Map[K, V]): MapDecorator[K, V] { val `this`: map.type } = new MapDecorator[K, V] { val `this`: map.type = map } implicit def ImmutableMapDecorator[C](coll: C)(implicit map: HasImmutableMapOps[C]): ImmutableMapDecorator[C, map.type] = - new ImmutableMapDecorator[C, map.type](coll)(map) + new ImmutableMapDecorator(coll)(map) implicit def MutableMapDecorator[K, V](map: mutable.Map[K, V]): MutableMapDecorator[K, V] = new MutableMapDecorator[K, V](map) diff --git a/collections-contrib/src/test/scala/strawman/collection/decorators/SeqDecoratorTest.scala b/collections-contrib/src/test/scala/strawman/collection/decorators/SeqDecoratorTest.scala index 6b4391043f..87d7ac8b8e 100644 --- a/collections-contrib/src/test/scala/strawman/collection/decorators/SeqDecoratorTest.scala +++ b/collections-contrib/src/test/scala/strawman/collection/decorators/SeqDecoratorTest.scala @@ -21,16 +21,19 @@ class SeqDecoratorTest { // This test just checks that there is no compilation error @Test def genericDecorator(): Unit = { val vector = Vector(1, 2, 3) + val range = Range(0, 10) val array = Array(1, 2, 3) val string = "foo" val list = List(1, 2, 3) val result = list.intersperse(0) typed[List[Int]](result) list.view.intersperse(0) + val result2 = range.intersperse(0) + typed[IndexedSeq[Int]](result2) vector.intersperse(0) vector.view.intersperse(0) - val result2 = array.intersperse(0) - typed[Array[Int]](result2) + val result3 = array.intersperse(0) + typed[Array[Int]](result3) array.view.intersperse(0) string.intersperse(' ') string.view.intersperse(' ') diff --git a/collections/src/main/scala/strawman/collection/generic/HasIterableOps.scala b/collections/src/main/scala/strawman/collection/generic/HasIterableOps.scala new file mode 100644 index 0000000000..66c1eaa4c4 --- /dev/null +++ b/collections/src/main/scala/strawman/collection/generic/HasIterableOps.scala @@ -0,0 +1,43 @@ +package strawman.collection +package generic + +import scala.Int + +import strawman.collection.immutable.Range + +trait HasIterableOps[Repr] { + + type A + + type CC[_] + + type C + + val conversion: Repr => IterableOps[A, CC, C] + +} + +object HasIterableOps { + import scala.language.higherKinds + + type Aux[Repr, A0, CC0[_], C0] = HasIterableOps[Repr] { type A = A0; type CC[X] = CC0[X]; type C = C0 } + + // 1. Iterable collections + implicit def iterableHasIterableOps[CC0[X] <: IterableOps[X, CC0, CC0[X]], A0]: HasIterableOps.Aux[CC0[A0], A0, CC0, CC0[A0]] = + new HasIterableOps[CC0[A0]] { + type A = A0 + type CC[X] = CC0[X] + type C = CC0[A0] + val conversion: CC0[A0] => IterableOps[A0, CC0, CC0[A0]] = c => c + } + + // 2. Range collections + implicit def rangeHasSeqOps[Repr <: Range]: HasIterableOps.Aux[Repr, Int, immutable.IndexedSeq, immutable.IndexedSeq[Int]] = + new HasIterableOps[Repr] { + type A = Int + type CC[X] = immutable.IndexedSeq[X] + type C = immutable.IndexedSeq[Int] + val conversion: Repr => IterableOps[Int, immutable.IndexedSeq, immutable.IndexedSeq[Int]] = r => r + } + +} \ No newline at end of file diff --git a/collections/src/main/scala/strawman/collection/generic/HasSeqOps.scala b/collections/src/main/scala/strawman/collection/generic/HasSeqOps.scala index 8d018d93b3..b9415c5e15 100644 --- a/collections/src/main/scala/strawman/collection/generic/HasSeqOps.scala +++ b/collections/src/main/scala/strawman/collection/generic/HasSeqOps.scala @@ -1,9 +1,11 @@ package strawman.collection package generic -import scala.{ Array, Char } +import scala.{ Array, Char, Int } import scala.Predef.String +import strawman.collection.immutable.Range + /** Type class witnessing that a collection representation type `Repr` has * elements of type `A` and has a conversion to `SeqOps[A, CC, C]`. * @@ -78,4 +80,14 @@ object HasSeqOps { type C = Array[A0] val conversion: Array[A0] => SeqOps[A0, immutable.IndexedSeq, Array[A0]] = arrayToArrayOps } + + // 6. Range collections + implicit def rangeHasSeqOps[Repr <: Range]: HasSeqOps.Aux[Repr, Int, immutable.IndexedSeq, immutable.IndexedSeq[Int]] = + new HasSeqOps[Repr] { + type A = Int + type CC[X] = immutable.IndexedSeq[X] + type C = immutable.IndexedSeq[Int] + val conversion: Repr => SeqOps[Int, immutable.IndexedSeq, immutable.IndexedSeq[Int]] = r => r + } + } From fbc26d8a20b3b4a484d5f37dbeacfafab1832b85 Mon Sep 17 00:00:00 2001 From: Julien Richard-Foy Date: Tue, 27 Feb 2018 16:05:52 +0100 Subject: [PATCH 07/12] Add HasMapOps, and make HasXxxOps types a hierarchy so that instances can be reused --- .../collection/decorators/MapDecorator.scala | 70 ++++++++-------- .../collection/decorators/SeqDecorator.scala | 2 +- .../collection/decorators/package.scala | 8 +- .../decorators/IterableDecoratorTest.scala | 14 +++- .../decorators/MapDecoratorTest.scala | 13 +++ .../generic/HasImmutableMapOps.scala | 45 +++++------ .../collection/generic/HasIterableOps.scala | 52 ++++++++---- .../collection/generic/HasMapOps.scala | 79 +++++++++++++++++++ .../collection/generic/HasSeqOps.scala | 12 +-- 9 files changed, 201 insertions(+), 94 deletions(-) create mode 100644 collections/src/main/scala/strawman/collection/generic/HasMapOps.scala diff --git a/collections-contrib/src/main/scala/strawman/collection/decorators/MapDecorator.scala b/collections-contrib/src/main/scala/strawman/collection/decorators/MapDecorator.scala index f140642648..ff46b60360 100644 --- a/collections-contrib/src/main/scala/strawman/collection/decorators/MapDecorator.scala +++ b/collections-contrib/src/main/scala/strawman/collection/decorators/MapDecorator.scala @@ -1,28 +1,28 @@ package strawman.collection package decorators -trait MapDecorator[K, V] { +import strawman.collection.generic.HasMapOps - val `this`: Map[K, V] +class MapDecorator[C, M <: HasMapOps[C]](coll: C)(implicit val map: M) { /** * Combines entries of `this` Map with entries of `that` Map that have the same key, * using the combination function `f` * - * @param that Other Map + * @param other Other Map * @param f Combination function * @param bf Builder driven by the type of `this` Map * @tparam W Type of values of the other Map (e.g. `Int`, `String`) * @tparam X Type of values of the resulting Map - * @tparam C Type of the resulting Map + * @tparam That Type of the resulting Map * @return A Map that associates all the keys `k` contained by both `this` and `that` to the result of * the application of `f` to the values `v` and `w` respectively associated by `this` and `that` to `k` */ - def zipByKeyWith[W, X, C](that: Map[K, W])(f: (V, W) => X)(implicit bf: BuildFrom[`this`.type, (K, X), C]): C = { - val b = bf.newBuilder(`this`) + def zipByKeyWith[W, X, That](other: Map[map.K, W])(f: (map.V, W) => X)(implicit bf: BuildFrom[C, (map.K, X), That]): That = { + val b = bf.newBuilder(coll) for { - (k, v) <- `this` - w <- that.get(k) + (k, v) <- map.conversion(coll) + w <- other.get(k) } { b += k -> f(v, w) } @@ -33,42 +33,42 @@ trait MapDecorator[K, V] { * Combines the entries of `this` Map with the entries of `that` Map that have the same key, * tupling their values. `xs.zipByKey(ys)` is a shorthand for `xs.zipByKeyWith(ys)((_, _))`. * - * @param that Other Map + * @param other Other Map * @param bf Builder driven by the type of `this` Map * @tparam W Type of values of the other Map (e.g. `Int`, `String`) - * @tparam C Type of the result (e.g. `Map[Int, (String, Boolean)]`, `TreeMap[String, (Int, Int)]`) + * @tparam That Type of the result (e.g. `Map[Int, (String, Boolean)]`, `TreeMap[String, (Int, Int)]`) * @return A Map that associates all the keys `k` contained by both `this` and `that` to pairs `(v, w)` where `v` * is the value associated by `this` to `k` and `w` the value associated by `that` to `k` */ - def zipByKey[W, C](that: Map[K, W])(implicit bf: BuildFrom[`this`.type, (K, (V, W)), C]): C = - zipByKeyWith(that)((_, _)) + def zipByKey[W, That](other: Map[map.K, W])(implicit bf: BuildFrom[C, (map.K, (map.V, W)), That]): That = + zipByKeyWith(other)((_, _)) /** Alias for `zipByKey` */ - @`inline` final def join[W, C](that: Map[K, W])(implicit bf: BuildFrom[`this`.type, (K, (V, W)), C]): C = zipByKey(that) + @`inline` final def join[W, That](other: Map[map.K, W])(implicit bf: BuildFrom[C, (map.K, (map.V, W)), That]): That = zipByKey(other) /** * @return A Map associating all the keys from `this` and `that` with values returned by the partial function * `f`, when this one is defined. * - * @param that Map to merge + * @param other Map to merge * @param f Combination function * @param bf Builder driven by the type of `this` Map * @tparam W Type of values of the other Map (e.g. `Int`, `String`) * @tparam X Type of values of the resulting Map - * @tparam C Type of the result (e.g. `Map[Int, (String, Option[Boolean])]`) + * @tparam That Type of the result (e.g. `Map[Int, (String, Option[Boolean])]`) */ - def mergeByKeyWith[W, X, C](that: Map[K, W])(f: PartialFunction[(Option[V], Option[W]), X])(implicit bf: BuildFrom[`this`.type, (K, X), C]): C = { - val b = bf.newBuilder(`this`) + def mergeByKeyWith[W, X, That](other: Map[map.K, W])(f: PartialFunction[(Option[map.V], Option[W]), X])(implicit bf: BuildFrom[C, (map.K, X), That]): That = { + val b = bf.newBuilder(coll) val traversed = mutable.Set.empty[W] val pf = f.lift for { - (k, v) <- `this` - x <- pf(that.get(k).fold[(Option[V], Option[W])]((Some(v), None)){ w => traversed += w; (Some(v), Some(w)) }) + (k, v) <- map.conversion(coll) + x <- pf(other.get(k).fold[(Option[map.V], Option[W])]((Some(v), None)){ w => traversed += w; (Some(v), Some(w)) }) } { b += k -> x } for { - (k, w) <- that if !traversed(w) + (k, w) <- other if !traversed(w) x <- pf((None, Some(w))) } { b += k -> x @@ -83,17 +83,17 @@ trait MapDecorator[K, V] { * * @param bf Builder for the resulting collection * @tparam W Type of values of `that` (e.g. `String`) - * @tparam C Type of the resulting collection (e.g. `Map[Int, (Option[Boolean], Option[String])]`) + * @tparam That Type of the resulting collection (e.g. `Map[Int, (Option[Boolean], Option[String])]`) * @return A Map that associates all the keys `k` of `this` or `that` to pairs `(Some(v), Some(w))` if `this` * associates `k` to `v` and `that` associates `k` to `w`, or pairs `(None, Some(w))` if `this` doesn’t * contain `k`, or pairs `(Some(v), None)` if `that` doesn’t contain `k` */ - @`inline` final def mergeByKey[W, C](that: Map[K, W])(implicit bf: BuildFrom[`this`.type, (K, (Option[V], Option[W])), C]): C = - mergeByKeyWith(that) { case any => any } + @`inline` final def mergeByKey[W, That](other: Map[map.K, W])(implicit bf: BuildFrom[C, (map.K, (Option[map.V], Option[W])), That]): That = + mergeByKeyWith(other) { case any => any } /** Alias for `mergeByKey` */ - @`inline` final def fullOuterJoin[W, C](that: Map[K, W])(implicit bf: BuildFrom[`this`.type, (K, (Option[V], Option[W])), C]): C = - mergeByKey(that) + @`inline` final def fullOuterJoin[W, That](other: Map[map.K, W])(implicit bf: BuildFrom[C, (map.K, (Option[map.V], Option[W])), That]): That = + mergeByKey(other) /** * Perform a left outer join of `this` and `that`. @@ -102,14 +102,14 @@ trait MapDecorator[K, V] { * * @param bf Builder for the resulting collection * @tparam W Type of values of `that` - * @tparam C Type of the resulting collection + * @tparam That Type of the resulting collection * @return A Map that associates all the keys `k` of `this` to pairs `(v, Some(w))` if `that` associates `k` to `w`, * or `(v, None)` if `that` doesn’t contain `k` */ - def leftOuterJoin[W, C](that: Map[K, W])(implicit bf: BuildFrom[`this`.type, (K, (V, Option[W])), C]): C = { - val b = bf.newBuilder(`this`) - for ((k, v) <- `this`) { - b += k -> (v, that.get(k)) + def leftOuterJoin[W, That](other: Map[map.K, W])(implicit bf: BuildFrom[C, (map.K, (map.V, Option[W])), That]): That = { + val b = bf.newBuilder(coll) + for ((k, v) <- map.conversion(coll)) { + b += k -> (v, other.get(k)) } b.result() } @@ -121,14 +121,14 @@ trait MapDecorator[K, V] { * * @param bf Builder for the resulting collection * @tparam W Type of values of `that` (e.g. `String`) - * @tparam C Type of the resulting collection (e.g. `Map[Int, (Option[Boolean], String)]`) + * @tparam That Type of the resulting collection (e.g. `Map[Int, (Option[Boolean], String)]`) * @return A Map that associates all the keys `k` of `that` to pairs `(Some(v), w)` if `this` associates `k` to `v`, * or `(None, w)` if `this` doesn’t contain `k` */ - def rightOuterJoin[W, C](that: Map[K, W])(implicit bf: BuildFrom[`this`.type, (K, (Option[V], W)), C]): C = { - val b = bf.newBuilder(`this`) - for ((k, w) <- that) { - b += k -> (`this`.get(k), w) + def rightOuterJoin[W, That](other: Map[map.K, W])(implicit bf: BuildFrom[C, (map.K, (Option[map.V], W)), That]): That = { + val b = bf.newBuilder(coll) + for ((k, w) <- other) { + b += k -> (map.conversion(coll).get(k), w) } b.result() } diff --git a/collections-contrib/src/main/scala/strawman/collection/decorators/SeqDecorator.scala b/collections-contrib/src/main/scala/strawman/collection/decorators/SeqDecorator.scala index 8f931036d4..3152027bde 100644 --- a/collections-contrib/src/main/scala/strawman/collection/decorators/SeqDecorator.scala +++ b/collections-contrib/src/main/scala/strawman/collection/decorators/SeqDecorator.scala @@ -8,7 +8,7 @@ import strawman.collection.generic.HasSeqOps * @param seq evidence that type `C` is a sequence * @tparam C type of the decorated collection (e.g. `List[Int]`, `String`, etc.) */ -class SeqDecorator[C, S <: HasSeqOps[C]](coll: C)(implicit seq: S) { +class SeqDecorator[C, S <: HasSeqOps[C]](coll: C)(implicit val seq: S) { /** Adds the element `sep` between each element of the sequence. * If the sequence has less than two elements, the collection is unchanged. diff --git a/collections-contrib/src/main/scala/strawman/collection/decorators/package.scala b/collections-contrib/src/main/scala/strawman/collection/decorators/package.scala index e291b138df..9c48a33e25 100644 --- a/collections-contrib/src/main/scala/strawman/collection/decorators/package.scala +++ b/collections-contrib/src/main/scala/strawman/collection/decorators/package.scala @@ -1,6 +1,6 @@ package strawman.collection -import strawman.collection.generic.{HasImmutableMapOps, HasIterableOps, HasSeqOps} +import strawman.collection.generic.{HasImmutableMapOps, HasIterableOps, HasMapOps, HasSeqOps} import scala.language.implicitConversions @@ -9,14 +9,14 @@ package object decorators { implicit def iteratorDecorator[A](it: Iterator[A]): IteratorDecorator[A] = new IteratorDecorator[A](it) - implicit def iterableDecorator[C](coll: C)(implicit it: HasIterableOps[C]): IterableDecorator[C, it.type] = + implicit def IterableDecorator[C](coll: C)(implicit it: HasIterableOps[C]): IterableDecorator[C, it.type] = new IterableDecorator(coll)(it) implicit def SeqDecorator[C](coll: C)(implicit seq: HasSeqOps[C]): SeqDecorator[C, seq.type] = new SeqDecorator(coll)(seq) - implicit def MapDecorator[K, V](map: Map[K, V]): MapDecorator[K, V] { val `this`: map.type } = - new MapDecorator[K, V] { val `this`: map.type = map } + implicit def MapDecorator[C](coll: C)(implicit map: HasMapOps[C]): MapDecorator[C, map.type] = + new MapDecorator(coll)(map) implicit def ImmutableMapDecorator[C](coll: C)(implicit map: HasImmutableMapOps[C]): ImmutableMapDecorator[C, map.type] = new ImmutableMapDecorator(coll)(map) diff --git a/collections-contrib/src/test/scala/strawman/collection/decorators/IterableDecoratorTest.scala b/collections-contrib/src/test/scala/strawman/collection/decorators/IterableDecoratorTest.scala index 2ac0a18025..6c4fc00427 100644 --- a/collections-contrib/src/test/scala/strawman/collection/decorators/IterableDecoratorTest.scala +++ b/collections-contrib/src/test/scala/strawman/collection/decorators/IterableDecoratorTest.scala @@ -2,7 +2,7 @@ package strawman.collection package decorators import org.junit.{Assert, Test} -import strawman.collection.immutable.{LazyList, List, Range} +import strawman.collection.immutable.{LazyList, List, Range, Map} class IterableDecoratorTest { @@ -24,4 +24,16 @@ class IterableDecoratorTest { Assert.assertEquals(1 << 16, xs.lazyFoldRight(0)(chooseOne)) } + @Test + def hasIterableOpsWorksWithStringAndMap(): Unit = { + val result = "foo".foldSomeLeft(0) { case (_, 'o') => None case (n, _) => Some(n + 1) } + Assert.assertEquals(1, result) + + val result2 = + Map(1 -> "foo", 2 -> "bar").foldSomeLeft(0) { + case (n, (k, _)) => if (k == -1) None else Some(n + 1) + } + Assert.assertEquals(2, result2) + } + } diff --git a/collections-contrib/src/test/scala/strawman/collection/decorators/MapDecoratorTest.scala b/collections-contrib/src/test/scala/strawman/collection/decorators/MapDecoratorTest.scala index 320533fb49..785547990d 100644 --- a/collections-contrib/src/test/scala/strawman/collection/decorators/MapDecoratorTest.scala +++ b/collections-contrib/src/test/scala/strawman/collection/decorators/MapDecoratorTest.scala @@ -61,4 +61,17 @@ class MapDecoratorTest { } } + @Test + def mapDecoratorWorksWithViews/*AndMutableMaps*/(): Unit = { + val map1 = Map(1 -> "a", 2 -> "b") + val map2 = Map(2 -> "c") + val zipped = map1.view.zipByKeyWith(map2)(_ ++ _).to(Map) + val expected = Map(2 -> "bc") + Assert.assertEquals(expected, zipped) + +// val mutableMap1 = mutable.Map(1 -> "a", 2 -> "b") +// val zipped2 = mutableMap1.zipByKeyWith(map2)(_ ++ _).to(Map) +// Assert.assertEquals(expected, zipped2) + } + } diff --git a/collections/src/main/scala/strawman/collection/generic/HasImmutableMapOps.scala b/collections/src/main/scala/strawman/collection/generic/HasImmutableMapOps.scala index a723d88646..d2090cc279 100644 --- a/collections/src/main/scala/strawman/collection/generic/HasImmutableMapOps.scala +++ b/collections/src/main/scala/strawman/collection/generic/HasImmutableMapOps.scala @@ -3,56 +3,49 @@ package generic /** * Type class witnessing that a collection representation type `Repr` has - * a conversion to `immutable.MapOps[K, V, CC, C]`, for some type `K`, - * `V`, `CC[_]` and `C` + * a conversion to `immutable.MapOps[K, V, CC2, C]`, for some type `K`, + * `V`, `CC2[_, _]` and `C` * @see [[scala.collection.generic.HasIterableOps]] */ -trait HasImmutableMapOps[Repr] { +trait HasImmutableMapOps[Repr] extends HasMapOps[Repr] { - /** The type of keys */ - type K + type CC2[X, +Y] <: immutable.MapOps[X, Y, CC2, _] - /** The type of values */ - type V + type C <: immutable.MapOps[K, V, CC2, C] - /** The type constructor returned by transformation operations that might change the types of key and value */ - type CC[X, +Y] <: immutable.MapOps[X, Y, CC, _] + /** A conversion from the representation type `Repr` to `immutable.MapOps[K, V, CC2, C]` */ + val conversion: Repr => immutable.MapOps[K, V, CC2, C] - /** The type returned by transformation operations that keep the same type of elements */ - type C <: immutable.MapOps[K, V, CC, C] - - /** A conversion from the representation type `Repr` to `immutable.MapOps[K, V, CC, C]` */ - val conversion: Repr => immutable.MapOps[K, V, CC, C] } object HasImmutableMapOps { /** A type alias for `HasImmutableMapOps` that moves type members to type parameters */ - type Aux[Repr, K0, V0, CC0[X, +Y] <: immutable.MapOps[X, Y, CC0, _], C0 <: immutable.MapOps[K0, V0, CC0, C0]] = HasImmutableMapOps[Repr] { + type Aux[Repr, K0, V0, CC20[X, +Y] <: immutable.MapOps[X, Y, CC20, _], C0 <: immutable.MapOps[K0, V0, CC20, C0]] = HasImmutableMapOps[Repr] { type K = K0 type V = V0 - type CC[X, +Y] = CC0[X, Y] + type CC2[X, +Y] = CC20[X, Y] type C = C0 } // 1. Map collections - implicit def mapHasMapOps[CC0[X, +Y] <: immutable.MapOps[X, Y, CC0, CC0[X, Y]], K0, V0]: HasImmutableMapOps.Aux[CC0[K0, V0], K0, V0, CC0, CC0[K0, V0]] = - new HasImmutableMapOps[CC0[K0, V0]] { + implicit def mapHasMapOps[CC20[X, +Y] <: immutable.MapOps[X, Y, CC20, CC20[X, Y]], K0, V0]: HasImmutableMapOps.Aux[CC20[K0, V0], K0, V0, CC20, CC20[K0, V0]] = + new HasImmutableMapOps[CC20[K0, V0]] { type K = K0 type V = V0 - type CC[X, +Y] = CC0[X, Y] - type C = CC0[K, V] - val conversion: CC0[K0, V0] => immutable.MapOps[K0, V0, CC0, CC0[K0, V0]] = c => c + type CC2[X, +Y] = CC20[X, Y] + type C = CC20[K, V] + val conversion: CC20[K0, V0] => immutable.MapOps[K0, V0, CC20, CC20[K0, V0]] = c => c } // 2. Sorted Map collections - implicit def sortedMapHasMapOps[CC0[X, +Y] <: immutable.Map[X, Y] with immutable.SortedMapOps[X, Y, CC0, CC0[X, Y]], K0, V0]: HasImmutableMapOps.Aux[CC0[K0, V0], K0, V0, immutable.Map, CC0[K0, V0]] = - new HasImmutableMapOps[CC0[K0, V0]] { + implicit def sortedMapHasMapOps[CC20[X, +Y] <: immutable.Map[X, Y] with immutable.SortedMapOps[X, Y, CC20, CC20[X, Y]], K0, V0]: HasImmutableMapOps.Aux[CC20[K0, V0], K0, V0, immutable.Map, CC20[K0, V0]] = + new HasImmutableMapOps[CC20[K0, V0]] { type K = K0 type V = V0 - type CC[X, +Y] = immutable.Map[X, Y] - type C = CC0[K, V] - val conversion: CC0[K0, V0] => immutable.MapOps[K0, V0, immutable.Map, CC0[K0, V0]] = c => c + type CC2[X, +Y] = immutable.Map[X, Y] + type C = CC20[K, V] + val conversion: CC20[K0, V0] => immutable.MapOps[K0, V0, immutable.Map, CC20[K0, V0]] = c => c } } \ No newline at end of file diff --git a/collections/src/main/scala/strawman/collection/generic/HasIterableOps.scala b/collections/src/main/scala/strawman/collection/generic/HasIterableOps.scala index 66c1eaa4c4..075b8ce5fc 100644 --- a/collections/src/main/scala/strawman/collection/generic/HasIterableOps.scala +++ b/collections/src/main/scala/strawman/collection/generic/HasIterableOps.scala @@ -1,28 +1,33 @@ package strawman.collection package generic -import scala.Int - -import strawman.collection.immutable.Range - +/** + * Type class witnessing that a collection representation type `Repr` + * has elements of type `A` and has a conversion to `IterableOps[A, CC, C]` + * for some types `CC[_]` and `C`. + * + * This type enables simple enrichment of `Iterable`s with extension methods. + * + * @tparam Repr Collection representation type (e.g. `List[Int]`) + */ trait HasIterableOps[Repr] { + /** The type of elements (e.g. `Int`) */ type A + /** The type constructor returned by transformation operations that might change the element type (e.g. `List`) */ type CC[_] + /** The type returned by transformation operations that keep the same type of elements (e.g. `List[Int]`) */ type C + /** A conversion from the representation type `Repr` to `IterableOps[A, CC, C]` */ val conversion: Repr => IterableOps[A, CC, C] } -object HasIterableOps { - import scala.language.higherKinds - - type Aux[Repr, A0, CC0[_], C0] = HasIterableOps[Repr] { type A = A0; type CC[X] = CC0[X]; type C = C0 } +object HasIterableOps extends LowPriorityHasIterableOps { - // 1. Iterable collections implicit def iterableHasIterableOps[CC0[X] <: IterableOps[X, CC0, CC0[X]], A0]: HasIterableOps.Aux[CC0[A0], A0, CC0, CC0[A0]] = new HasIterableOps[CC0[A0]] { type A = A0 @@ -31,13 +36,28 @@ object HasIterableOps { val conversion: CC0[A0] => IterableOps[A0, CC0, CC0[A0]] = c => c } - // 2. Range collections - implicit def rangeHasSeqOps[Repr <: Range]: HasIterableOps.Aux[Repr, Int, immutable.IndexedSeq, immutable.IndexedSeq[Int]] = - new HasIterableOps[Repr] { - type A = Int - type CC[X] = immutable.IndexedSeq[X] - type C = immutable.IndexedSeq[Int] - val conversion: Repr => IterableOps[Int, immutable.IndexedSeq, immutable.IndexedSeq[Int]] = r => r + implicit def viewHasIterableOps[CC0[X] <: View[X], A0]: HasIterableOps.Aux[CC0[A0], A0, View, View[A0]] = + new HasIterableOps[CC0[A0]] { + type A = A0 + type CC[X] = View[X] + type C = View[A0] + val conversion: CC0[A0] => IterableOps[A0, View, View[A0]] = c => c } +} + +trait LowPriorityHasIterableOps { + + type Aux[Repr, A0, CC0[_], C0] = HasIterableOps[Repr] { type A = A0; type CC[X] = CC0[X]; type C = C0 } + + // Makes `HasSeqOps` instances visible in `HasIterableOps` companion + implicit def hasSeqOpsHasIterableOps[Repr, A, CC[_], C](implicit + hasSeqOps: HasSeqOps.Aux[Repr, A, CC, C] + ): HasIterableOps.Aux[Repr, A, CC, C] = hasSeqOps + + // Makes `HasMapOps` instances visible in `HasIterableOps` companion + implicit def hasMapOpsHasIterableOps[Repr, K0, V0, CC20[_, +_] <: IterableOps[_, AnyConstr, _], C0](implicit + hasMapOps: HasMapOps.Aux[Repr, K0, V0, CC20, C0] + ): HasIterableOps.Aux[Repr, (K0, V0), Iterable, C0] = hasMapOps + } \ No newline at end of file diff --git a/collections/src/main/scala/strawman/collection/generic/HasMapOps.scala b/collections/src/main/scala/strawman/collection/generic/HasMapOps.scala new file mode 100644 index 0000000000..1a78d0b5e2 --- /dev/null +++ b/collections/src/main/scala/strawman/collection/generic/HasMapOps.scala @@ -0,0 +1,79 @@ +package strawman.collection +package generic + +import scala.annotation.unchecked.uncheckedVariance + +/** + * Type class witnessing that a collection representation type `Repr` + * has elements of type `A` and has a conversion to `MapOps[A, CC2, C]` + * for some types `CC2[_, _]` and `C`. + * + * This type enables simple enrichment of `Map`s with extension methods. + * + * @tparam Repr Collection representation type (e.g. `Map[Int, String]`) + */ +trait HasMapOps[Repr] extends HasIterableOps[Repr] { + + /** The type of keys */ + type K + + /** The type of values */ + type V + + /** The type constructor returned by transformation operations that might change the types of key and value (e.g. `HashMap`) */ + type CC2[_, +_] <: IterableOps[_, AnyConstr, _] + + type A = (K, V) + + type CC[X] = Iterable[X] + + /** A conversion from the representation type `Repr` to `MapOps[K, V, CC2, C]` */ + val conversion: Repr => MapOps[K, V, CC2, C] + +} + +object HasMapOps extends LowPriorityHasMapOps { + + // 1. Map collections + implicit def mapHasMapOps[CC20[X, +Y] <: MapOps[X, Y, CC20, CC20[X, Y]], K0, V0]: HasMapOps.Aux[CC20[K0, V0], K0, V0, CC20, CC20[K0, V0]] = + new HasMapOps[CC20[K0, V0]] { + type K = K0 + type V = V0 + type CC2[X, +Y] = CC20[X, Y] + type C = CC20[K, V] + val conversion: CC20[K0, V0] => MapOps[K0, V0, CC20, CC20[K0, V0]] = c => c + } + + // 2. Sorted Map collections + implicit def sortedMapHasMapOps[CC20[X, +Y] <: Map[X, Y] with SortedMapOps[X, Y, CC20, CC20[X, Y]], K0, V0]: HasMapOps.Aux[CC20[K0, V0], K0, V0, Map, CC20[K0, V0]] = + new HasMapOps[CC20[K0, V0]] { + type K = K0 + type V = V0 + type CC2[X, +Y] = Map[X, Y] + type C = CC20[K, V] + val conversion: CC20[K0, V0] => MapOps[K0, V0, Map, CC20[K0, V0]] = c => c + } + + // 3. MapView + implicit def mapViewHasMapOps[CC20[X, +Y] <: MapView[X, Y], K0, V0]: HasMapOps.Aux[CC20[K0, V0], K0, V0, ({ type l[X, +Y] = View[(X, Y)] })#l, View[(K0, V0)]] = + new HasMapOps[CC20[K0, V0]] { + type K = K0 + type V = V0 + type CC2[X, +Y] = View[(X, Y)] + type C = View[(K0, V0)] + val conversion: CC20[K0, V0] => MapOps[K0, V0, ({ type l[X, Y] = View[(X, Y)] })#l, View[(K0, V0)]] = c => c + } + +} + +trait LowPriorityHasMapOps { + + type Aux[Repr, K0, V0, CC20[_, +_] <: IterableOps[_, AnyConstr, _], C0] = + HasMapOps[Repr] { type K = K0; type V = V0; type CC2[X, +Y] = CC20[X, Y]; type C = C0 } + + // Makes `HasImmutableMapOps` instances visible in `HasMapOps` companion + implicit def hasImmutableMapOpsHasMapOps[Repr, K0, V0, CC20[X, +Y] <: immutable.MapOps[X, Y, CC20, _], C0 <: immutable.MapOps[K0, V0, CC20, C0]](implicit + hasImmutableMapOps: HasImmutableMapOps.Aux[Repr, K0, V0, CC20, C0] + ): HasMapOps.Aux[Repr, K0, V0, CC20, C0] = hasImmutableMapOps + +} \ No newline at end of file diff --git a/collections/src/main/scala/strawman/collection/generic/HasSeqOps.scala b/collections/src/main/scala/strawman/collection/generic/HasSeqOps.scala index b9415c5e15..e31809df0d 100644 --- a/collections/src/main/scala/strawman/collection/generic/HasSeqOps.scala +++ b/collections/src/main/scala/strawman/collection/generic/HasSeqOps.scala @@ -15,22 +15,12 @@ import strawman.collection.immutable.Range * * @see [[scala.collection.generic.HasIterableOps]] */ -trait HasSeqOps[Repr] { - /** The type of elements we can traverse over. */ - type A - - /** The type constructor returned by transformation operations that might change the type of elements */ - type CC[_] - - /** The type returned by transformation operations that keep the same type of elements */ - type C - +trait HasSeqOps[Repr] extends HasIterableOps[Repr] { /** A conversion from the representation type `Repr` to `SeqOps[A, CC, C]`. */ val conversion: Repr => SeqOps[A, CC, C] } object HasSeqOps { - import scala.language.higherKinds /** A type alias for `HasSeqOps` that moves type members to type parameters */ type Aux[Repr, A0, CC0[_], C0] = HasSeqOps[Repr] { type A = A0; type CC[X] = CC0[X]; type C = C0 } From 5d27e4ed7ecb4f75d2a75b8d6c19e4c67376d10b Mon Sep 17 00:00:00 2001 From: Julien Richard-Foy Date: Tue, 27 Feb 2018 16:11:12 +0100 Subject: [PATCH 08/12] Move HasXxxOps traits into the collections-contrib module --- .../strawman/collection/decorators}/HasImmutableMapOps.scala | 4 ++-- .../strawman/collection/decorators}/HasIterableOps.scala | 2 +- .../scala/strawman/collection/decorators}/HasMapOps.scala | 2 +- .../scala/strawman/collection/decorators}/HasSeqOps.scala | 4 ++-- .../collection/decorators/ImmutableMapDecorator.scala | 2 -- .../strawman/collection/decorators/IterableDecorator.scala | 2 -- .../scala/strawman/collection/decorators/MapDecorator.scala | 2 -- .../scala/strawman/collection/decorators/SeqDecorator.scala | 2 -- .../main/scala/strawman/collection/decorators/package.scala | 2 -- 9 files changed, 6 insertions(+), 16 deletions(-) rename {collections/src/main/scala/strawman/collection/generic => collections-contrib/src/main/scala/strawman/collection/decorators}/HasImmutableMapOps.scala (95%) rename {collections/src/main/scala/strawman/collection/generic => collections-contrib/src/main/scala/strawman/collection/decorators}/HasIterableOps.scala (99%) rename {collections/src/main/scala/strawman/collection/generic => collections-contrib/src/main/scala/strawman/collection/decorators}/HasMapOps.scala (99%) rename {collections/src/main/scala/strawman/collection/generic => collections-contrib/src/main/scala/strawman/collection/decorators}/HasSeqOps.scala (97%) diff --git a/collections/src/main/scala/strawman/collection/generic/HasImmutableMapOps.scala b/collections-contrib/src/main/scala/strawman/collection/decorators/HasImmutableMapOps.scala similarity index 95% rename from collections/src/main/scala/strawman/collection/generic/HasImmutableMapOps.scala rename to collections-contrib/src/main/scala/strawman/collection/decorators/HasImmutableMapOps.scala index d2090cc279..3c48206cfc 100644 --- a/collections/src/main/scala/strawman/collection/generic/HasImmutableMapOps.scala +++ b/collections-contrib/src/main/scala/strawman/collection/decorators/HasImmutableMapOps.scala @@ -1,11 +1,11 @@ package strawman.collection -package generic +package decorators /** * Type class witnessing that a collection representation type `Repr` has * a conversion to `immutable.MapOps[K, V, CC2, C]`, for some type `K`, * `V`, `CC2[_, _]` and `C` - * @see [[scala.collection.generic.HasIterableOps]] + * @see [[scala.collection.decorators.HasIterableOps]] */ trait HasImmutableMapOps[Repr] extends HasMapOps[Repr] { diff --git a/collections/src/main/scala/strawman/collection/generic/HasIterableOps.scala b/collections-contrib/src/main/scala/strawman/collection/decorators/HasIterableOps.scala similarity index 99% rename from collections/src/main/scala/strawman/collection/generic/HasIterableOps.scala rename to collections-contrib/src/main/scala/strawman/collection/decorators/HasIterableOps.scala index 075b8ce5fc..e6399ff00d 100644 --- a/collections/src/main/scala/strawman/collection/generic/HasIterableOps.scala +++ b/collections-contrib/src/main/scala/strawman/collection/decorators/HasIterableOps.scala @@ -1,5 +1,5 @@ package strawman.collection -package generic +package decorators /** * Type class witnessing that a collection representation type `Repr` diff --git a/collections/src/main/scala/strawman/collection/generic/HasMapOps.scala b/collections-contrib/src/main/scala/strawman/collection/decorators/HasMapOps.scala similarity index 99% rename from collections/src/main/scala/strawman/collection/generic/HasMapOps.scala rename to collections-contrib/src/main/scala/strawman/collection/decorators/HasMapOps.scala index 1a78d0b5e2..bb10bbce4c 100644 --- a/collections/src/main/scala/strawman/collection/generic/HasMapOps.scala +++ b/collections-contrib/src/main/scala/strawman/collection/decorators/HasMapOps.scala @@ -1,5 +1,5 @@ package strawman.collection -package generic +package decorators import scala.annotation.unchecked.uncheckedVariance diff --git a/collections/src/main/scala/strawman/collection/generic/HasSeqOps.scala b/collections-contrib/src/main/scala/strawman/collection/decorators/HasSeqOps.scala similarity index 97% rename from collections/src/main/scala/strawman/collection/generic/HasSeqOps.scala rename to collections-contrib/src/main/scala/strawman/collection/decorators/HasSeqOps.scala index e31809df0d..dc6233b574 100644 --- a/collections/src/main/scala/strawman/collection/generic/HasSeqOps.scala +++ b/collections-contrib/src/main/scala/strawman/collection/decorators/HasSeqOps.scala @@ -1,5 +1,5 @@ package strawman.collection -package generic +package decorators import scala.{ Array, Char, Int } import scala.Predef.String @@ -13,7 +13,7 @@ import strawman.collection.immutable.Range * can make full use of the mechanics of the Scala collections framework in * their implementation. * - * @see [[scala.collection.generic.HasIterableOps]] + * @see [[scala.collection.decorators.HasIterableOps]] */ trait HasSeqOps[Repr] extends HasIterableOps[Repr] { /** A conversion from the representation type `Repr` to `SeqOps[A, CC, C]`. */ diff --git a/collections-contrib/src/main/scala/strawman/collection/decorators/ImmutableMapDecorator.scala b/collections-contrib/src/main/scala/strawman/collection/decorators/ImmutableMapDecorator.scala index f045da4c85..a5e13570cb 100644 --- a/collections-contrib/src/main/scala/strawman/collection/decorators/ImmutableMapDecorator.scala +++ b/collections-contrib/src/main/scala/strawman/collection/decorators/ImmutableMapDecorator.scala @@ -2,8 +2,6 @@ package strawman package collection package decorators -import strawman.collection.generic.HasImmutableMapOps - class ImmutableMapDecorator[C, M <: HasImmutableMapOps[C]](coll: C)(implicit val map: M) { /** diff --git a/collections-contrib/src/main/scala/strawman/collection/decorators/IterableDecorator.scala b/collections-contrib/src/main/scala/strawman/collection/decorators/IterableDecorator.scala index 693c3db220..bc46339582 100644 --- a/collections-contrib/src/main/scala/strawman/collection/decorators/IterableDecorator.scala +++ b/collections-contrib/src/main/scala/strawman/collection/decorators/IterableDecorator.scala @@ -2,8 +2,6 @@ package strawman package collection package decorators -import strawman.collection.generic.HasIterableOps - class IterableDecorator[C, I <: HasIterableOps[C]](coll: C)(implicit val it: I) { /** diff --git a/collections-contrib/src/main/scala/strawman/collection/decorators/MapDecorator.scala b/collections-contrib/src/main/scala/strawman/collection/decorators/MapDecorator.scala index ff46b60360..53007d46c3 100644 --- a/collections-contrib/src/main/scala/strawman/collection/decorators/MapDecorator.scala +++ b/collections-contrib/src/main/scala/strawman/collection/decorators/MapDecorator.scala @@ -1,8 +1,6 @@ package strawman.collection package decorators -import strawman.collection.generic.HasMapOps - class MapDecorator[C, M <: HasMapOps[C]](coll: C)(implicit val map: M) { /** diff --git a/collections-contrib/src/main/scala/strawman/collection/decorators/SeqDecorator.scala b/collections-contrib/src/main/scala/strawman/collection/decorators/SeqDecorator.scala index 3152027bde..726b119069 100644 --- a/collections-contrib/src/main/scala/strawman/collection/decorators/SeqDecorator.scala +++ b/collections-contrib/src/main/scala/strawman/collection/decorators/SeqDecorator.scala @@ -1,8 +1,6 @@ package strawman.collection package decorators -import strawman.collection.generic.HasSeqOps - /** * @param coll the decorated collection * @param seq evidence that type `C` is a sequence diff --git a/collections-contrib/src/main/scala/strawman/collection/decorators/package.scala b/collections-contrib/src/main/scala/strawman/collection/decorators/package.scala index 9c48a33e25..6c56f27bef 100644 --- a/collections-contrib/src/main/scala/strawman/collection/decorators/package.scala +++ b/collections-contrib/src/main/scala/strawman/collection/decorators/package.scala @@ -1,7 +1,5 @@ package strawman.collection -import strawman.collection.generic.{HasImmutableMapOps, HasIterableOps, HasMapOps, HasSeqOps} - import scala.language.implicitConversions package object decorators { From 45389080b8e7de52c0ac7ed9e757862abf18a818 Mon Sep 17 00:00:00 2001 From: Julien Richard-Foy Date: Tue, 27 Feb 2018 16:11:24 +0100 Subject: [PATCH 09/12] Fix code style --- .../src/main/scala/strawman/collection/Searching.scala | 4 ++-- .../scala/strawman/collection/generic/IsIterableLike.scala | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/collections/src/main/scala/strawman/collection/Searching.scala b/collections/src/main/scala/strawman/collection/Searching.scala index aa13d64311..c9aff953fc 100644 --- a/collections/src/main/scala/strawman/collection/Searching.scala +++ b/collections/src/main/scala/strawman/collection/Searching.scala @@ -25,9 +25,9 @@ object Searching { case class InsertionPoint(insertionPoint: Int) extends SearchResult @deprecated("Search methods are defined directly on SeqOps and do not require scala.collection.Searching any more", "2.13.0") - class SearchImpl[A, Repr](private val coll: SeqOps[A, Seq, Repr]) extends AnyVal + class SearchImpl[Repr, A](private val coll: SeqOps[A, AnyConstr, _]) extends AnyVal @deprecated("Search methods are defined directly on SeqOps and do not require scala.collection.Searching any more", "2.13.0") - implicit def search[Repr, A](coll: Repr)(implicit fr: IsSeqLike[Repr]): SearchImpl[fr.A, Repr] = + implicit def search[Repr, A](coll: Repr)(implicit fr: IsSeqLike[Repr]): SearchImpl[Repr, fr.A] = new SearchImpl(fr.conversion(coll)) } diff --git a/collections/src/main/scala/strawman/collection/generic/IsIterableLike.scala b/collections/src/main/scala/strawman/collection/generic/IsIterableLike.scala index d6e8413603..97ea55da8a 100644 --- a/collections/src/main/scala/strawman/collection/generic/IsIterableLike.scala +++ b/collections/src/main/scala/strawman/collection/generic/IsIterableLike.scala @@ -114,8 +114,8 @@ object IsIterableLike { implicit def arrayRepr[T](implicit conv: Array[T] => IterableOps[T, Iterable, Array[T]]): IsIterableLike[Array[T]] = new IsIterableLike[Array[T]] { - override type A = T - override val conversion: Array[T] => IterableOps[A, Iterable, Array[T]] = conv + type A = T + val conversion: Array[T] => IterableOps[A, Iterable, Array[T]] = conv } implicit def iterableRepr[C[X] <: Iterable[X], A0](implicit conv: C[A0] => IterableOps[A0, C, C[A0]]): IsIterableLike[C[A0]] { type A = A0 } = From 9af7f4e02ba19c0dbd46e35a96fcd427cf2258b8 Mon Sep 17 00:00:00 2001 From: Julien Richard-Foy Date: Tue, 27 Feb 2018 21:51:46 +0100 Subject: [PATCH 10/12] Remove type members representing collection types --- .../decorators/HasImmutableMapOps.scala | 46 ++++++------- .../decorators/HasIterableOps.scala | 49 +++++--------- .../collection/decorators/HasMapOps.scala | 57 ++++------------- .../collection/decorators/HasSeqOps.scala | 64 ++++++------------- 4 files changed, 67 insertions(+), 149 deletions(-) diff --git a/collections-contrib/src/main/scala/strawman/collection/decorators/HasImmutableMapOps.scala b/collections-contrib/src/main/scala/strawman/collection/decorators/HasImmutableMapOps.scala index 3c48206cfc..ee6e1cd9ea 100644 --- a/collections-contrib/src/main/scala/strawman/collection/decorators/HasImmutableMapOps.scala +++ b/collections-contrib/src/main/scala/strawman/collection/decorators/HasImmutableMapOps.scala @@ -2,50 +2,42 @@ package strawman.collection package decorators /** - * Type class witnessing that a collection representation type `Repr` has - * a conversion to `immutable.MapOps[K, V, CC2, C]`, for some type `K`, - * `V`, `CC2[_, _]` and `C` + * Type class witnessing that a collection type `C` has + * a conversion to `immutable.MapOps[K, V, _, _]`. + * * @see [[scala.collection.decorators.HasIterableOps]] */ -trait HasImmutableMapOps[Repr] extends HasMapOps[Repr] { +trait HasImmutableMapOps[C] extends HasMapOps[C] { - type CC2[X, +Y] <: immutable.MapOps[X, Y, CC2, _] + // Convenient intermediate type definitions to satisfy type bounds + protected type _CC[X, +Y] <: immutable.MapOps[X, Y, _CC, _] + protected type _C <: immutable.MapOps[K, V, _CC, _C] - type C <: immutable.MapOps[K, V, CC2, C] - - /** A conversion from the representation type `Repr` to `immutable.MapOps[K, V, CC2, C]` */ - val conversion: Repr => immutable.MapOps[K, V, CC2, C] + /** A conversion from the type `C` to `immutable.MapOps[K, V, _, _]` */ + val conversion: C => immutable.MapOps[K, V, _CC, _C] } object HasImmutableMapOps { - /** A type alias for `HasImmutableMapOps` that moves type members to type parameters */ - type Aux[Repr, K0, V0, CC20[X, +Y] <: immutable.MapOps[X, Y, CC20, _], C0 <: immutable.MapOps[K0, V0, CC20, C0]] = HasImmutableMapOps[Repr] { - type K = K0 - type V = V0 - type CC2[X, +Y] = CC20[X, Y] - type C = C0 - } - // 1. Map collections - implicit def mapHasMapOps[CC20[X, +Y] <: immutable.MapOps[X, Y, CC20, CC20[X, Y]], K0, V0]: HasImmutableMapOps.Aux[CC20[K0, V0], K0, V0, CC20, CC20[K0, V0]] = - new HasImmutableMapOps[CC20[K0, V0]] { + implicit def mapHasMapOps[CC[X, +Y] <: immutable.MapOps[X, Y, CC, CC[X, Y]], K0, V0]: HasImmutableMapOps[CC[K0, V0]] { type K = K0; type V = V0 } = + new HasImmutableMapOps[CC[K0, V0]] { type K = K0 type V = V0 - type CC2[X, +Y] = CC20[X, Y] - type C = CC20[K, V] - val conversion: CC20[K0, V0] => immutable.MapOps[K0, V0, CC20, CC20[K0, V0]] = c => c + type _CC[X, +Y] = CC[X, Y] + type _C = CC[K, V] + val conversion: CC[K0, V0] => immutable.MapOps[K0, V0, _CC, _C] = c => c } // 2. Sorted Map collections - implicit def sortedMapHasMapOps[CC20[X, +Y] <: immutable.Map[X, Y] with immutable.SortedMapOps[X, Y, CC20, CC20[X, Y]], K0, V0]: HasImmutableMapOps.Aux[CC20[K0, V0], K0, V0, immutable.Map, CC20[K0, V0]] = - new HasImmutableMapOps[CC20[K0, V0]] { + implicit def sortedMapHasMapOps[CC[X, +Y] <: immutable.Map[X, Y] with immutable.SortedMapOps[X, Y, CC, CC[X, Y]], K0, V0]: HasImmutableMapOps[CC[K0, V0]] { type K = K0; type V = V0 } = + new HasImmutableMapOps[CC[K0, V0]] { type K = K0 type V = V0 - type CC2[X, +Y] = immutable.Map[X, Y] - type C = CC20[K, V] - val conversion: CC20[K0, V0] => immutable.MapOps[K0, V0, immutable.Map, CC20[K0, V0]] = c => c + type _CC[X, +Y] = immutable.Map[X, Y] + type _C = _CC[K, V] + val conversion: CC[K0, V0] => immutable.MapOps[K0, V0, _CC, _C] = c => c } } \ No newline at end of file diff --git a/collections-contrib/src/main/scala/strawman/collection/decorators/HasIterableOps.scala b/collections-contrib/src/main/scala/strawman/collection/decorators/HasIterableOps.scala index e6399ff00d..8356b50683 100644 --- a/collections-contrib/src/main/scala/strawman/collection/decorators/HasIterableOps.scala +++ b/collections-contrib/src/main/scala/strawman/collection/decorators/HasIterableOps.scala @@ -2,62 +2,43 @@ package strawman.collection package decorators /** - * Type class witnessing that a collection representation type `Repr` - * has elements of type `A` and has a conversion to `IterableOps[A, CC, C]` - * for some types `CC[_]` and `C`. + * Type class witnessing that a collection type `C` + * has elements of type `A` and has a conversion to `IterableOps[A, _, _]`. * * This type enables simple enrichment of `Iterable`s with extension methods. * - * @tparam Repr Collection representation type (e.g. `List[Int]`) + * @tparam C Collection type (e.g. `List[Int]`) */ -trait HasIterableOps[Repr] { +trait HasIterableOps[C] { /** The type of elements (e.g. `Int`) */ type A - /** The type constructor returned by transformation operations that might change the element type (e.g. `List`) */ - type CC[_] - - /** The type returned by transformation operations that keep the same type of elements (e.g. `List[Int]`) */ - type C - - /** A conversion from the representation type `Repr` to `IterableOps[A, CC, C]` */ - val conversion: Repr => IterableOps[A, CC, C] + /** A conversion from the type `C` to `IterableOps[A, _, _]` */ + val conversion: C => IterableOps[A, AnyConstr, _] } object HasIterableOps extends LowPriorityHasIterableOps { - implicit def iterableHasIterableOps[CC0[X] <: IterableOps[X, CC0, CC0[X]], A0]: HasIterableOps.Aux[CC0[A0], A0, CC0, CC0[A0]] = - new HasIterableOps[CC0[A0]] { - type A = A0 - type CC[X] = CC0[X] - type C = CC0[A0] - val conversion: CC0[A0] => IterableOps[A0, CC0, CC0[A0]] = c => c - } - - implicit def viewHasIterableOps[CC0[X] <: View[X], A0]: HasIterableOps.Aux[CC0[A0], A0, View, View[A0]] = - new HasIterableOps[CC0[A0]] { + implicit def iterableHasIterableOps[CC[X] <: IterableOps[X, AnyConstr, _], A0]: HasIterableOps[CC[A0]] { type A = A0 } = + new HasIterableOps[CC[A0]] { type A = A0 - type CC[X] = View[X] - type C = View[A0] - val conversion: CC0[A0] => IterableOps[A0, View, View[A0]] = c => c + val conversion: CC[A0] => IterableOps[A0, AnyConstr, _] = c => c } } trait LowPriorityHasIterableOps { - type Aux[Repr, A0, CC0[_], C0] = HasIterableOps[Repr] { type A = A0; type CC[X] = CC0[X]; type C = C0 } - // Makes `HasSeqOps` instances visible in `HasIterableOps` companion - implicit def hasSeqOpsHasIterableOps[Repr, A, CC[_], C](implicit - hasSeqOps: HasSeqOps.Aux[Repr, A, CC, C] - ): HasIterableOps.Aux[Repr, A, CC, C] = hasSeqOps + implicit def hasSeqOpsHasIterableOps[C, A0](implicit + hasSeqOps: HasSeqOps[C] { type A = A0 } + ): HasIterableOps[C] { type A = A0 } = hasSeqOps // Makes `HasMapOps` instances visible in `HasIterableOps` companion - implicit def hasMapOpsHasIterableOps[Repr, K0, V0, CC20[_, +_] <: IterableOps[_, AnyConstr, _], C0](implicit - hasMapOps: HasMapOps.Aux[Repr, K0, V0, CC20, C0] - ): HasIterableOps.Aux[Repr, (K0, V0), Iterable, C0] = hasMapOps + implicit def hasMapOpsHasIterableOps[C, K0, V0](implicit + hasMapOps: HasMapOps[C] { type K = K0; type V = V0 } + ): HasIterableOps[C] { type A = (K0, V0) } = hasMapOps } \ No newline at end of file diff --git a/collections-contrib/src/main/scala/strawman/collection/decorators/HasMapOps.scala b/collections-contrib/src/main/scala/strawman/collection/decorators/HasMapOps.scala index bb10bbce4c..57c51b12bc 100644 --- a/collections-contrib/src/main/scala/strawman/collection/decorators/HasMapOps.scala +++ b/collections-contrib/src/main/scala/strawman/collection/decorators/HasMapOps.scala @@ -1,18 +1,15 @@ package strawman.collection package decorators -import scala.annotation.unchecked.uncheckedVariance - /** - * Type class witnessing that a collection representation type `Repr` - * has elements of type `A` and has a conversion to `MapOps[A, CC2, C]` - * for some types `CC2[_, _]` and `C`. + * Type class witnessing that a collection type `C` + * has keys of type `K`, values of type `V` and has a conversion to `MapOps[K, V, _, _]`. * * This type enables simple enrichment of `Map`s with extension methods. * - * @tparam Repr Collection representation type (e.g. `Map[Int, String]`) + * @tparam C Collection type (e.g. `Map[Int, String]`) */ -trait HasMapOps[Repr] extends HasIterableOps[Repr] { +trait HasMapOps[C] extends HasIterableOps[C] { /** The type of keys */ type K @@ -20,60 +17,30 @@ trait HasMapOps[Repr] extends HasIterableOps[Repr] { /** The type of values */ type V - /** The type constructor returned by transformation operations that might change the types of key and value (e.g. `HashMap`) */ - type CC2[_, +_] <: IterableOps[_, AnyConstr, _] - type A = (K, V) - type CC[X] = Iterable[X] - - /** A conversion from the representation type `Repr` to `MapOps[K, V, CC2, C]` */ - val conversion: Repr => MapOps[K, V, CC2, C] + /** A conversion from the type `C` to `MapOps[K, V, _, _]` */ + val conversion: C => MapOps[K, V, ({ type l[X, +Y] = IterableOps[_, AnyConstr, _] })#l, _] } object HasMapOps extends LowPriorityHasMapOps { // 1. Map collections - implicit def mapHasMapOps[CC20[X, +Y] <: MapOps[X, Y, CC20, CC20[X, Y]], K0, V0]: HasMapOps.Aux[CC20[K0, V0], K0, V0, CC20, CC20[K0, V0]] = - new HasMapOps[CC20[K0, V0]] { - type K = K0 - type V = V0 - type CC2[X, +Y] = CC20[X, Y] - type C = CC20[K, V] - val conversion: CC20[K0, V0] => MapOps[K0, V0, CC20, CC20[K0, V0]] = c => c - } - - // 2. Sorted Map collections - implicit def sortedMapHasMapOps[CC20[X, +Y] <: Map[X, Y] with SortedMapOps[X, Y, CC20, CC20[X, Y]], K0, V0]: HasMapOps.Aux[CC20[K0, V0], K0, V0, Map, CC20[K0, V0]] = - new HasMapOps[CC20[K0, V0]] { - type K = K0 - type V = V0 - type CC2[X, +Y] = Map[X, Y] - type C = CC20[K, V] - val conversion: CC20[K0, V0] => MapOps[K0, V0, Map, CC20[K0, V0]] = c => c - } - - // 3. MapView - implicit def mapViewHasMapOps[CC20[X, +Y] <: MapView[X, Y], K0, V0]: HasMapOps.Aux[CC20[K0, V0], K0, V0, ({ type l[X, +Y] = View[(X, Y)] })#l, View[(K0, V0)]] = - new HasMapOps[CC20[K0, V0]] { + implicit def mapHasMapOps[CC[X, +Y] <: MapOps[X, Y, ({ type l[X, +Y] = IterableOps[_, AnyConstr, _] })#l, _], K0, V0]: HasMapOps[CC[K0, V0]] { type K = K0; type V = V0 } = + new HasMapOps[CC[K0, V0]] { type K = K0 type V = V0 - type CC2[X, +Y] = View[(X, Y)] - type C = View[(K0, V0)] - val conversion: CC20[K0, V0] => MapOps[K0, V0, ({ type l[X, Y] = View[(X, Y)] })#l, View[(K0, V0)]] = c => c + val conversion: CC[K0, V0] => MapOps[K0, V0, ({ type l[X, +Y] = IterableOps[_, AnyConstr, _] })#l, _] = c => c } } trait LowPriorityHasMapOps { - type Aux[Repr, K0, V0, CC20[_, +_] <: IterableOps[_, AnyConstr, _], C0] = - HasMapOps[Repr] { type K = K0; type V = V0; type CC2[X, +Y] = CC20[X, Y]; type C = C0 } - // Makes `HasImmutableMapOps` instances visible in `HasMapOps` companion - implicit def hasImmutableMapOpsHasMapOps[Repr, K0, V0, CC20[X, +Y] <: immutable.MapOps[X, Y, CC20, _], C0 <: immutable.MapOps[K0, V0, CC20, C0]](implicit - hasImmutableMapOps: HasImmutableMapOps.Aux[Repr, K0, V0, CC20, C0] - ): HasMapOps.Aux[Repr, K0, V0, CC20, C0] = hasImmutableMapOps + implicit def hasImmutableMapOpsHasMapOps[C, K0, V0](implicit + hasImmutableMapOps: HasImmutableMapOps[C] { type K = K0; type V = V0 } + ): HasMapOps[C] { type K = K0; type V = V0 } = hasImmutableMapOps } \ No newline at end of file diff --git a/collections-contrib/src/main/scala/strawman/collection/decorators/HasSeqOps.scala b/collections-contrib/src/main/scala/strawman/collection/decorators/HasSeqOps.scala index dc6233b574..1c81e47347 100644 --- a/collections-contrib/src/main/scala/strawman/collection/decorators/HasSeqOps.scala +++ b/collections-contrib/src/main/scala/strawman/collection/decorators/HasSeqOps.scala @@ -6,8 +6,8 @@ import scala.Predef.String import strawman.collection.immutable.Range -/** Type class witnessing that a collection representation type `Repr` has - * elements of type `A` and has a conversion to `SeqOps[A, CC, C]`. +/** Type class witnessing that a collection type `C` has + * elements of type `A` and has a conversion to `SeqOps[A, _, _]`. * * This type enables simple enrichment of `Seq`s with extension methods which * can make full use of the mechanics of the Scala collections framework in @@ -15,69 +15,47 @@ import strawman.collection.immutable.Range * * @see [[scala.collection.decorators.HasIterableOps]] */ -trait HasSeqOps[Repr] extends HasIterableOps[Repr] { - /** A conversion from the representation type `Repr` to `SeqOps[A, CC, C]`. */ - val conversion: Repr => SeqOps[A, CC, C] +trait HasSeqOps[C] extends HasIterableOps[C] { + /** A conversion from the type `C` to `SeqOps[A, _, _]`. */ + val conversion: C => SeqOps[A, AnyConstr, _] } object HasSeqOps { - /** A type alias for `HasSeqOps` that moves type members to type parameters */ - type Aux[Repr, A0, CC0[_], C0] = HasSeqOps[Repr] { type A = A0; type CC[X] = CC0[X]; type C = C0 } - // we want to provide implicit instances that unify all possible types `X` with a `SeqOps[A, CC, C]` // 1. Seq collections - implicit def seqHasSeqOps[CC0[X] <: SeqOps[X, CC0, CC0[X]], A0]: HasSeqOps.Aux[CC0[A0], A0, CC0, CC0[A0]] = - new HasSeqOps[CC0[A0]] { - type A = A0 - type CC[X] = CC0[X] - type C = CC0[A] - val conversion: CC0[A0] => SeqOps[A0, CC0, CC0[A0]] = c => c - } - - // 2. Seq views - implicit def viewHasSeqOps[CC0[X] <: SeqView[X], A0]: HasSeqOps.Aux[CC0[A0], A0, View, View[A0]] = - new HasSeqOps[CC0[A0]] { + implicit def seqHasSeqOps[CC[X] <: SeqOps[X, AnyConstr, _], A0]: HasSeqOps[CC[A0]] {type A = A0 } = + new HasSeqOps[CC[A0]] { type A = A0 - type CC[X] = View[X] - type C = View[A0] - val conversion: CC0[A0] => SeqOps[A0, View, View[A0]] = c => c + val conversion: CC[A0] => SeqOps[A0, AnyConstr, _] = c => c } - // 3. String - implicit def stringHasSeqOps: HasSeqOps.Aux[String, Char, immutable.IndexedSeq, String] = + // 2. String + implicit def stringHasSeqOps: HasSeqOps[String] { type A = Char } = new HasSeqOps[String] { type A = Char - type CC[X] = immutable.IndexedSeq[X] - type C = String - val conversion: String => SeqOps[Char, immutable.IndexedSeq, String] = stringToStringOps + val conversion: String => SeqOps[Char, AnyConstr, _] = stringToStringOps } - // 4. StringView - implicit def stringViewHasSeqOps: HasSeqOps.Aux[StringView, Char, View, View[Char]] = + // 3. StringView + implicit def stringViewHasSeqOps: HasSeqOps[StringView] { type A = Char } = new HasSeqOps[StringView] { type A = Char - type CC[X] = View[X] - type C = View[Char] - val conversion: StringView => SeqOps[Char, View, View[Char]] = v => v + val conversion: StringView => SeqOps[Char, AnyConstr, _] = v => v } - // 5. Array - implicit def arrayHasSeqOps[A0]: HasSeqOps.Aux[Array[A0], A0, immutable.IndexedSeq, Array[A0]] = + // 4. Array + implicit def arrayHasSeqOps[A0]: HasSeqOps[Array[A0]] { type A = A0 } = new HasSeqOps[Array[A0]] { type A = A0 - type CC[X] = immutable.IndexedSeq[X] - type C = Array[A0] - val conversion: Array[A0] => SeqOps[A0, immutable.IndexedSeq, Array[A0]] = arrayToArrayOps + val conversion: Array[A0] => SeqOps[A0, AnyConstr, _] = arrayToArrayOps } - // 6. Range collections - implicit def rangeHasSeqOps[Repr <: Range]: HasSeqOps.Aux[Repr, Int, immutable.IndexedSeq, immutable.IndexedSeq[Int]] = - new HasSeqOps[Repr] { + // 5. Range collections + implicit def rangeHasSeqOps[C <: Range]: HasSeqOps[C] { type A = Int } = + new HasSeqOps[C] { type A = Int - type CC[X] = immutable.IndexedSeq[X] - type C = immutable.IndexedSeq[Int] - val conversion: Repr => SeqOps[Int, immutable.IndexedSeq, immutable.IndexedSeq[Int]] = r => r + val conversion: C => SeqOps[Int, AnyConstr, _] = r => r } } From 931935361c2e2a2719d4ef44bc9caeca220a5e4c Mon Sep 17 00:00:00 2001 From: Julien Richard-Foy Date: Wed, 28 Feb 2018 08:52:36 +0100 Subject: [PATCH 11/12] Turn `val conversion` into `def apply` --- .../collection/decorators/HasImmutableMapOps.scala | 6 +++--- .../collection/decorators/HasIterableOps.scala | 4 ++-- .../strawman/collection/decorators/HasMapOps.scala | 4 ++-- .../strawman/collection/decorators/HasSeqOps.scala | 12 ++++++------ .../decorators/ImmutableMapDecorator.scala | 2 +- .../collection/decorators/IterableDecorator.scala | 4 ++-- .../collection/decorators/MapDecorator.scala | 8 ++++---- .../collection/decorators/SeqDecorator.scala | 4 ++-- 8 files changed, 22 insertions(+), 22 deletions(-) diff --git a/collections-contrib/src/main/scala/strawman/collection/decorators/HasImmutableMapOps.scala b/collections-contrib/src/main/scala/strawman/collection/decorators/HasImmutableMapOps.scala index ee6e1cd9ea..d5fd0a4068 100644 --- a/collections-contrib/src/main/scala/strawman/collection/decorators/HasImmutableMapOps.scala +++ b/collections-contrib/src/main/scala/strawman/collection/decorators/HasImmutableMapOps.scala @@ -14,7 +14,7 @@ trait HasImmutableMapOps[C] extends HasMapOps[C] { protected type _C <: immutable.MapOps[K, V, _CC, _C] /** A conversion from the type `C` to `immutable.MapOps[K, V, _, _]` */ - val conversion: C => immutable.MapOps[K, V, _CC, _C] + def apply(c: C): immutable.MapOps[K, V, _CC, _C] } @@ -27,7 +27,7 @@ object HasImmutableMapOps { type V = V0 type _CC[X, +Y] = CC[X, Y] type _C = CC[K, V] - val conversion: CC[K0, V0] => immutable.MapOps[K0, V0, _CC, _C] = c => c + def apply(c: CC[K0, V0]): immutable.MapOps[K0, V0, _CC, _C] = c } // 2. Sorted Map collections @@ -37,7 +37,7 @@ object HasImmutableMapOps { type V = V0 type _CC[X, +Y] = immutable.Map[X, Y] type _C = _CC[K, V] - val conversion: CC[K0, V0] => immutable.MapOps[K0, V0, _CC, _C] = c => c + def apply(c: CC[K0, V0]): immutable.MapOps[K0, V0, _CC, _C] = c } } \ No newline at end of file diff --git a/collections-contrib/src/main/scala/strawman/collection/decorators/HasIterableOps.scala b/collections-contrib/src/main/scala/strawman/collection/decorators/HasIterableOps.scala index 8356b50683..f399640c01 100644 --- a/collections-contrib/src/main/scala/strawman/collection/decorators/HasIterableOps.scala +++ b/collections-contrib/src/main/scala/strawman/collection/decorators/HasIterableOps.scala @@ -15,7 +15,7 @@ trait HasIterableOps[C] { type A /** A conversion from the type `C` to `IterableOps[A, _, _]` */ - val conversion: C => IterableOps[A, AnyConstr, _] + def apply(c: C): IterableOps[A, AnyConstr, _] } @@ -24,7 +24,7 @@ object HasIterableOps extends LowPriorityHasIterableOps { implicit def iterableHasIterableOps[CC[X] <: IterableOps[X, AnyConstr, _], A0]: HasIterableOps[CC[A0]] { type A = A0 } = new HasIterableOps[CC[A0]] { type A = A0 - val conversion: CC[A0] => IterableOps[A0, AnyConstr, _] = c => c + def apply(c: CC[A0]): IterableOps[A0, AnyConstr, _] = c } } diff --git a/collections-contrib/src/main/scala/strawman/collection/decorators/HasMapOps.scala b/collections-contrib/src/main/scala/strawman/collection/decorators/HasMapOps.scala index 57c51b12bc..2985afa24a 100644 --- a/collections-contrib/src/main/scala/strawman/collection/decorators/HasMapOps.scala +++ b/collections-contrib/src/main/scala/strawman/collection/decorators/HasMapOps.scala @@ -20,7 +20,7 @@ trait HasMapOps[C] extends HasIterableOps[C] { type A = (K, V) /** A conversion from the type `C` to `MapOps[K, V, _, _]` */ - val conversion: C => MapOps[K, V, ({ type l[X, +Y] = IterableOps[_, AnyConstr, _] })#l, _] + def apply(c: C): MapOps[K, V, ({ type l[X, +Y] = IterableOps[_, AnyConstr, _] })#l, _] } @@ -31,7 +31,7 @@ object HasMapOps extends LowPriorityHasMapOps { new HasMapOps[CC[K0, V0]] { type K = K0 type V = V0 - val conversion: CC[K0, V0] => MapOps[K0, V0, ({ type l[X, +Y] = IterableOps[_, AnyConstr, _] })#l, _] = c => c + def apply(c: CC[K0, V0]): MapOps[K0, V0, ({ type l[X, +Y] = IterableOps[_, AnyConstr, _] })#l, _] = c } } diff --git a/collections-contrib/src/main/scala/strawman/collection/decorators/HasSeqOps.scala b/collections-contrib/src/main/scala/strawman/collection/decorators/HasSeqOps.scala index 1c81e47347..60871d0a73 100644 --- a/collections-contrib/src/main/scala/strawman/collection/decorators/HasSeqOps.scala +++ b/collections-contrib/src/main/scala/strawman/collection/decorators/HasSeqOps.scala @@ -17,7 +17,7 @@ import strawman.collection.immutable.Range */ trait HasSeqOps[C] extends HasIterableOps[C] { /** A conversion from the type `C` to `SeqOps[A, _, _]`. */ - val conversion: C => SeqOps[A, AnyConstr, _] + def apply(c: C): SeqOps[A, AnyConstr, _] } object HasSeqOps { @@ -27,35 +27,35 @@ object HasSeqOps { implicit def seqHasSeqOps[CC[X] <: SeqOps[X, AnyConstr, _], A0]: HasSeqOps[CC[A0]] {type A = A0 } = new HasSeqOps[CC[A0]] { type A = A0 - val conversion: CC[A0] => SeqOps[A0, AnyConstr, _] = c => c + def apply(c: CC[A0]): SeqOps[A0, AnyConstr, _] = c } // 2. String implicit def stringHasSeqOps: HasSeqOps[String] { type A = Char } = new HasSeqOps[String] { type A = Char - val conversion: String => SeqOps[Char, AnyConstr, _] = stringToStringOps + def apply(c: String): SeqOps[Char, AnyConstr, _] = stringToStringOps(c) } // 3. StringView implicit def stringViewHasSeqOps: HasSeqOps[StringView] { type A = Char } = new HasSeqOps[StringView] { type A = Char - val conversion: StringView => SeqOps[Char, AnyConstr, _] = v => v + def apply(c: StringView): SeqOps[Char, AnyConstr, _] = c } // 4. Array implicit def arrayHasSeqOps[A0]: HasSeqOps[Array[A0]] { type A = A0 } = new HasSeqOps[Array[A0]] { type A = A0 - val conversion: Array[A0] => SeqOps[A0, AnyConstr, _] = arrayToArrayOps + def apply(c: Array[A0]): SeqOps[A0, AnyConstr, _] = arrayToArrayOps(c) } // 5. Range collections implicit def rangeHasSeqOps[C <: Range]: HasSeqOps[C] { type A = Int } = new HasSeqOps[C] { type A = Int - val conversion: C => SeqOps[Int, AnyConstr, _] = r => r + def apply(c: C): SeqOps[Int, AnyConstr, _] = c } } diff --git a/collections-contrib/src/main/scala/strawman/collection/decorators/ImmutableMapDecorator.scala b/collections-contrib/src/main/scala/strawman/collection/decorators/ImmutableMapDecorator.scala index a5e13570cb..0f342b1f96 100644 --- a/collections-contrib/src/main/scala/strawman/collection/decorators/ImmutableMapDecorator.scala +++ b/collections-contrib/src/main/scala/strawman/collection/decorators/ImmutableMapDecorator.scala @@ -25,7 +25,7 @@ class ImmutableMapDecorator[C, M <: HasImmutableMapOps[C]](coll: C)(implicit val */ def updatedWith[That](key: map.K)(f: PartialFunction[Option[map.V], Option[map.V]])(implicit bf: BuildFrom[C, (map.K, map.V), That]): That = { val pf = f.lift - val `this` = map.conversion(coll) + val `this` = map(coll) val previousValue = `this`.get(key) pf(previousValue) match { case None => bf.fromSpecificIterable(coll)(`this`.toIterable) diff --git a/collections-contrib/src/main/scala/strawman/collection/decorators/IterableDecorator.scala b/collections-contrib/src/main/scala/strawman/collection/decorators/IterableDecorator.scala index bc46339582..b1b77c1d9a 100644 --- a/collections-contrib/src/main/scala/strawman/collection/decorators/IterableDecorator.scala +++ b/collections-contrib/src/main/scala/strawman/collection/decorators/IterableDecorator.scala @@ -15,7 +15,7 @@ class IterableDecorator[C, I <: HasIterableOps[C]](coll: C)(implicit val it: I) * all the elements have been traversed or earlier if the operator returns `None` */ def foldSomeLeft[B](z: B)(op: (B, it.A) => Option[B]): B = - it.conversion(coll).iterator().foldSomeLeft(z)(op) + it(coll).iterator().foldSomeLeft(z)(op) /** * Right to left fold that can be interrupted before traversing the whole collection. @@ -29,6 +29,6 @@ class IterableDecorator[C, I <: HasIterableOps[C]](coll: C)(implicit val it: I) * `f` is applied to the previous result to produce the new result and the fold continues. */ def lazyFoldRight[B](z: B)(op: it.A => Either[B, B => B]): B = - it.conversion(coll).iterator().lazyFoldRight(z)(op) + it(coll).iterator().lazyFoldRight(z)(op) } diff --git a/collections-contrib/src/main/scala/strawman/collection/decorators/MapDecorator.scala b/collections-contrib/src/main/scala/strawman/collection/decorators/MapDecorator.scala index 53007d46c3..a3a2dcb8b3 100644 --- a/collections-contrib/src/main/scala/strawman/collection/decorators/MapDecorator.scala +++ b/collections-contrib/src/main/scala/strawman/collection/decorators/MapDecorator.scala @@ -19,7 +19,7 @@ class MapDecorator[C, M <: HasMapOps[C]](coll: C)(implicit val map: M) { def zipByKeyWith[W, X, That](other: Map[map.K, W])(f: (map.V, W) => X)(implicit bf: BuildFrom[C, (map.K, X), That]): That = { val b = bf.newBuilder(coll) for { - (k, v) <- map.conversion(coll) + (k, v) <- map(coll) w <- other.get(k) } { b += k -> f(v, w) @@ -60,7 +60,7 @@ class MapDecorator[C, M <: HasMapOps[C]](coll: C)(implicit val map: M) { val traversed = mutable.Set.empty[W] val pf = f.lift for { - (k, v) <- map.conversion(coll) + (k, v) <- map(coll) x <- pf(other.get(k).fold[(Option[map.V], Option[W])]((Some(v), None)){ w => traversed += w; (Some(v), Some(w)) }) } { b += k -> x @@ -106,7 +106,7 @@ class MapDecorator[C, M <: HasMapOps[C]](coll: C)(implicit val map: M) { */ def leftOuterJoin[W, That](other: Map[map.K, W])(implicit bf: BuildFrom[C, (map.K, (map.V, Option[W])), That]): That = { val b = bf.newBuilder(coll) - for ((k, v) <- map.conversion(coll)) { + for ((k, v) <- map(coll)) { b += k -> (v, other.get(k)) } b.result() @@ -126,7 +126,7 @@ class MapDecorator[C, M <: HasMapOps[C]](coll: C)(implicit val map: M) { def rightOuterJoin[W, That](other: Map[map.K, W])(implicit bf: BuildFrom[C, (map.K, (Option[map.V], W)), That]): That = { val b = bf.newBuilder(coll) for ((k, w) <- other) { - b += k -> (map.conversion(coll).get(k), w) + b += k -> (map(coll).get(k), w) } b.result() } diff --git a/collections-contrib/src/main/scala/strawman/collection/decorators/SeqDecorator.scala b/collections-contrib/src/main/scala/strawman/collection/decorators/SeqDecorator.scala index 726b119069..c47425713b 100644 --- a/collections-contrib/src/main/scala/strawman/collection/decorators/SeqDecorator.scala +++ b/collections-contrib/src/main/scala/strawman/collection/decorators/SeqDecorator.scala @@ -21,7 +21,7 @@ class SeqDecorator[C, S <: HasSeqOps[C]](coll: C)(implicit val seq: S) { * }}} */ def intersperse[B >: seq.A, That](sep: B)(implicit bf: BuildFrom[C, B, That]): That = - bf.fromSpecificIterable(coll)(new View.Intersperse(seq.conversion(coll), sep)) + bf.fromSpecificIterable(coll)(new View.Intersperse(seq(coll), sep)) /** Adds the element `sep` between each element of the sequence, * prepending `start` and appending `end`. @@ -39,6 +39,6 @@ class SeqDecorator[C, S <: HasSeqOps[C]](coll: C)(implicit val seq: S) { * }}} */ def intersperse[B >: seq.A, That](start: B, sep: B, end: B)(implicit bf: BuildFrom[C, B, That]): That = - bf.fromSpecificIterable(coll)(new View.IntersperseSurround(seq.conversion(coll), start, sep, end)) + bf.fromSpecificIterable(coll)(new View.IntersperseSurround(seq(coll), start, sep, end)) } From f487dc2a29ccdfb77cde2b7223077d006bcaf3ac Mon Sep 17 00:00:00 2001 From: Julien Richard-Foy Date: Thu, 1 Mar 2018 14:14:18 +0100 Subject: [PATCH 12/12] Wrap arrays in ImmutableArray --- .../scala/strawman/collection/decorators/HasSeqOps.scala | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/collections-contrib/src/main/scala/strawman/collection/decorators/HasSeqOps.scala b/collections-contrib/src/main/scala/strawman/collection/decorators/HasSeqOps.scala index 60871d0a73..24085a8e7d 100644 --- a/collections-contrib/src/main/scala/strawman/collection/decorators/HasSeqOps.scala +++ b/collections-contrib/src/main/scala/strawman/collection/decorators/HasSeqOps.scala @@ -1,10 +1,9 @@ package strawman.collection package decorators -import scala.{ Array, Char, Int } +import scala.{Array, Char, Int} import scala.Predef.String - -import strawman.collection.immutable.Range +import strawman.collection.immutable.{ImmutableArray, Range} /** Type class witnessing that a collection type `C` has * elements of type `A` and has a conversion to `SeqOps[A, _, _]`. @@ -48,7 +47,7 @@ object HasSeqOps { implicit def arrayHasSeqOps[A0]: HasSeqOps[Array[A0]] { type A = A0 } = new HasSeqOps[Array[A0]] { type A = A0 - def apply(c: Array[A0]): SeqOps[A0, AnyConstr, _] = arrayToArrayOps(c) + def apply(c: Array[A0]): SeqOps[A0, AnyConstr, _] = ImmutableArray.unsafeWrapArray(c) } // 5. Range collections