A trait which can be used to avoid code duplication when defining extension methods that should be applicable both to existing Scala collections (i.e., types extending Iterable) as well as other (potentially user-defined) types that could be converted to a Scala collection type. This trait makes it possible to uniformly treat Scala collections and types that can be implicitly converted to a collection type. For example, one can provide extension methods that work both on collection types and on Strings. (Strings do not extend Iterable, but can be converted to Iterable.)
IsIterable provides three members:
- type member
A, which represents the element type of the targetIterable[A] - type member
C, which represents the type returned by transformation operations that preserve the collection’s element type - method
apply, which provides a way to convert between the type we wish to add extension methods to,Repr, andIterableOps[A, Iterable, C].
Usage
One must provide IsIterable as an implicit parameter of the extension method. Its usage is shown below. Our objective in the following example is to provide a generic extension method mapReduce for any type that extends or can be converted to Iterable, such as String.
import scala.collection.generic.IsIterable
extension [Repr, I <: IsIterable[Repr]](coll: Repr)(using it: I)
def mapReduce[B](mapper: it.A => B)(reducer: (B, B) => B): B = {
val iter = it(coll).iterator
var res = mapper(iter.next())
while (iter.hasNext)
res = reducer(res, mapper(iter.next()))
res
}
// See it in action!
List(1, 2, 3).mapReduce(_ * 2)(_ + _) // res0: Int = 12
"Yeah, well, you know, that's just, like, your opinion, man.".mapReduce(x => 1)(_ + _) // res1: Int = 59
The extension method takes a receiver coll of type Repr, where Repr typically represents the collection type, and an argument it of a subtype of IsIterable[Repr].
The body of the method starts by converting the coll argument to an IterableOps in order to call the iterator method on it. The rest of the implementation is straightforward.
The mapReduce extension method is available on any type Repr for which there is an implicit IsIterable[Repr] instance. The companion object for IsIterable provides an instance for types that are already an IterableOps.
Implementing IsIterable for New Types
For a custom type, one need only provide an implicit value of type IsIterable that specifies the element type, the collection type, and an implementation of apply that converts the collection to an IterableOps.
Below is an example implementation of the IsIterable trait where the Repr type is Range. In practice, IsIterable[Range] is already provided by the implicit value for any IterableOps, as for List in the previous example. Similarly, the instance for String was available because the library provides an IsSeq[String].
implicit val rangeRepr: IsIterable[Range] { type A = Int; type C = IndexedSeq[Int] } =
new IsIterable[Range] {
type A = Int
type C = IndexedSeq[Int]
def apply(coll: Range): IterableOps[Int, IndexedSeq, IndexedSeq[Int]] = coll
}
Attributes
- Companion
- object
- Source
- IsIterable.scala
- Graph
-
- Supertypes
- Known subtypes