diff --git a/collections-contrib/src/main/scala/strawman/collection/decorators/HasImmutableMapOps.scala b/collections-contrib/src/main/scala/strawman/collection/decorators/HasImmutableMapOps.scala new file mode 100644 index 0000000000..d5fd0a4068 --- /dev/null +++ b/collections-contrib/src/main/scala/strawman/collection/decorators/HasImmutableMapOps.scala @@ -0,0 +1,43 @@ +package strawman.collection +package decorators + +/** + * Type class witnessing that a collection type `C` has + * a conversion to `immutable.MapOps[K, V, _, _]`. + * + * @see [[scala.collection.decorators.HasIterableOps]] + */ +trait HasImmutableMapOps[C] extends HasMapOps[C] { + + // 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] + + /** A conversion from the type `C` to `immutable.MapOps[K, V, _, _]` */ + def apply(c: C): immutable.MapOps[K, V, _CC, _C] + +} + +object HasImmutableMapOps { + + // 1. Map collections + 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 _CC[X, +Y] = CC[X, Y] + type _C = CC[K, V] + def apply(c: CC[K0, V0]): immutable.MapOps[K0, V0, _CC, _C] = c + } + + // 2. Sorted Map collections + 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 _CC[X, +Y] = immutable.Map[X, Y] + type _C = _CC[K, V] + 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 new file mode 100644 index 0000000000..f399640c01 --- /dev/null +++ b/collections-contrib/src/main/scala/strawman/collection/decorators/HasIterableOps.scala @@ -0,0 +1,44 @@ +package strawman.collection +package decorators + +/** + * 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 C Collection type (e.g. `List[Int]`) + */ +trait HasIterableOps[C] { + + /** The type of elements (e.g. `Int`) */ + type A + + /** A conversion from the type `C` to `IterableOps[A, _, _]` */ + def apply(c: C): IterableOps[A, AnyConstr, _] + +} + +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 + def apply(c: CC[A0]): IterableOps[A0, AnyConstr, _] = c + } + +} + +trait LowPriorityHasIterableOps { + + // Makes `HasSeqOps` instances visible in `HasIterableOps` companion + 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[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 new file mode 100644 index 0000000000..2985afa24a --- /dev/null +++ b/collections-contrib/src/main/scala/strawman/collection/decorators/HasMapOps.scala @@ -0,0 +1,46 @@ +package strawman.collection +package decorators + +/** + * 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 C Collection type (e.g. `Map[Int, String]`) + */ +trait HasMapOps[C] extends HasIterableOps[C] { + + /** The type of keys */ + type K + + /** The type of values */ + type V + + type A = (K, V) + + /** A conversion from the type `C` to `MapOps[K, V, _, _]` */ + def apply(c: C): MapOps[K, V, ({ type l[X, +Y] = IterableOps[_, AnyConstr, _] })#l, _] + +} + +object HasMapOps extends LowPriorityHasMapOps { + + // 1. Map collections + 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 + def apply(c: CC[K0, V0]): MapOps[K0, V0, ({ type l[X, +Y] = IterableOps[_, AnyConstr, _] })#l, _] = c + } + +} + +trait LowPriorityHasMapOps { + + // Makes `HasImmutableMapOps` instances visible in `HasMapOps` companion + 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 new file mode 100644 index 0000000000..24085a8e7d --- /dev/null +++ b/collections-contrib/src/main/scala/strawman/collection/decorators/HasSeqOps.scala @@ -0,0 +1,60 @@ +package strawman.collection +package decorators + +import scala.{Array, Char, Int} +import scala.Predef.String +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, _, _]`. + * + * 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.decorators.HasIterableOps]] + */ +trait HasSeqOps[C] extends HasIterableOps[C] { + /** A conversion from the type `C` to `SeqOps[A, _, _]`. */ + def apply(c: C): SeqOps[A, AnyConstr, _] +} + +object HasSeqOps { + + // we want to provide implicit instances that unify all possible types `X` with a `SeqOps[A, CC, C]` + // 1. Seq collections + implicit def seqHasSeqOps[CC[X] <: SeqOps[X, AnyConstr, _], A0]: HasSeqOps[CC[A0]] {type A = A0 } = + new HasSeqOps[CC[A0]] { + type A = A0 + 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 + 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 + 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 + def apply(c: Array[A0]): SeqOps[A0, AnyConstr, _] = ImmutableArray.unsafeWrapArray(c) + } + + // 5. Range collections + implicit def rangeHasSeqOps[C <: Range]: HasSeqOps[C] { type A = Int } = + new HasSeqOps[C] { + type A = Int + 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 d0f24b3089..0f342b1f96 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,7 @@ package strawman package collection package decorators -class ImmutableMapDecorator[K, V, CC[X, +Y] <: immutable.Map[X, Y]](`this`: CC[K, V]) { +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 +23,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(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/IterableDecorator.scala b/collections-contrib/src/main/scala/strawman/collection/decorators/IterableDecorator.scala index 5d9fc8942c..b1b77c1d9a 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,7 @@ package strawman package collection package decorators -class IterableDecorator[A](val `this`: Iterable[A]) extends AnyVal { +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 +14,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(coll).iterator().foldSomeLeft(z)(op) /** * Right to left fold that can be interrupted before traversing the whole collection. @@ -28,6 +28,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(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 f140642648..a3a2dcb8b3 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,26 @@ package strawman.collection package decorators -trait MapDecorator[K, V] { - - 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(coll) + w <- other.get(k) } { b += k -> f(v, w) } @@ -33,42 +31,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(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 +81,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 +100,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(coll)) { + b += k -> (v, other.get(k)) } b.result() } @@ -121,14 +119,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(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 87480e10d5..c47425713b 100644 --- a/collections-contrib/src/main/scala/strawman/collection/decorators/SeqDecorator.scala +++ b/collections-contrib/src/main/scala/strawman/collection/decorators/SeqDecorator.scala @@ -2,29 +2,26 @@ package strawman.collection package decorators /** - * @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 + * @tparam C type of the decorated collection (e.g. `List[Int]`, `String`, etc.) */ -class SeqDecorator[A, CC[X] <: SeqOps[X, CC, _]](`this`: CC[A]) { +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. * * @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, That](sep: B)(implicit bf: BuildFrom[C, B, That]): That = + bf.fromSpecificIterable(coll)(new View.Intersperse(seq(coll), sep)) /** Adds the element `sep` between each element of the sequence, * prepending `start` and appending `end`. @@ -33,15 +30,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, That](start: B, sep: B, end: B)(implicit bf: BuildFrom[C, B, That]): That = + bf.fromSpecificIterable(coll)(new View.IntersperseSurround(seq(coll), 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..6c56f27bef 100644 --- a/collections-contrib/src/main/scala/strawman/collection/decorators/package.scala +++ b/collections-contrib/src/main/scala/strawman/collection/decorators/package.scala @@ -7,17 +7,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[A, CC[X] <: SeqOps[X, CC, _]](seq: CC[A]): SeqDecorator[A, CC] = - new SeqDecorator[A, CC](seq) + 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[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(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/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/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-contrib/src/test/scala/strawman/collection/decorators/SeqDecoratorTest.scala b/collections-contrib/src/test/scala/strawman/collection/decorators/SeqDecoratorTest.scala index 7ed1896d94..87d7ac8b8e 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,27 @@ 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 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 result3 = array.intersperse(0) + typed[Array[Int]](result3) + array.view.intersperse(0) + string.intersperse(' ') + string.view.intersperse(' ') + } + + def typed[T](t: => T): Unit = () + } 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 { 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 } =