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 extendingIterable) as well as other (potentially user-defined) types that could be converted to a Scala collection type. This trait makes it possible to treat Scala collections and types that can be implicitly converted to a collection type uniformly. For example, one can provide extension methods that work both on collection types and onStrings (Strings do not extendIterable, but can be converted toIterable)
IsIterable provides three members:
1. type memberA, which represents the element type of the targetIterable[A] 1. type memberC, which represents the type returned by transformation operations that preserve the collection’s elements type 1. methodapply, which provides a way to convert between the type we wish to add extension methods to,Repr, andIterableOps[A, Iterable, C].
One must provideIsIterable as an implicit parameter type of an implicit conversion. Its usage is shown below. Our objective in the following example is to provide a generic extension methodmapReduce to any type that extends or can be converted toIterable. In our example, this includesString.
import scala.collection.{Iterable, IterableOps} import scala.collection.generic.IsIterable class ExtensionMethods[Repr, I <: IsIterable[Repr]](coll: Repr, 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 } } implicit def withExtensions[Repr](coll: Repr)(implicit it: IsIterable[Repr]): ExtensionMethods[Repr, it.type] = new ExtensionMethods(coll, it)// 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 = 59Here, we begin by creating a classExtensionMethods which contains ourmapReduce extension method.
Note thatExtensionMethods takes a constructor argumentcoll of typeRepr, whereRepr represents (typically) the collection type, and an argumentit of a subtype ofIsIterable[Repr]. The body of the method starts by converting thecoll argument to anIterableOps in order to call theiterator method on it. The remaining of the implementation is straightforward.
ThewithExtensions implicit conversion makes themapReduce operation available on any typeRepr for which it exists an implicitIsIterable[Repr] instance. Note how we keep track of the precise type of the implicitit argument by using theit.type singleton type, rather than the widerIsIterable[Repr] type. We do that so that the information carried by the type membersA andC of theit argument is not lost.
When themapReduce method is called on some type of which it is not a member, implicit search is triggered. Because implicit conversionwithExtensions is generic, it will be applied as long as an implicit value of typeIsIterable[Repr] can be found. Given that theIsIterable companion object contains implicit members that return values of typeIsIterable, this requirement is typically satisfied, and the chain of interactions described in the previous paragraph is set into action. (See theIsIterable companion object, which contains a precise specification of the available implicits.)
Note: Currently, it's not possible to combine the implicit conversion and the class with the extension methods into an implicit class due to limitations of type inference.
IsIterable for New TypesOne must simply provide an implicit value of typeIsIterable specific to the new type, or an implicit conversion which returns an instance ofIsIterable specific to the new type.
Below is an example of an implementation of theIsIterable trait where theRepr type isRange.
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 }(Note that in practice theIsIterable[Range] instance is already provided by the standard library, and it is defined as anIsSeq[Range] instance)
The type of elements we can traverse over (e.g.
The type returned by transformation operations that preserve the same elements type (e.g.
The type returned by transformation operations that preserve the same elements type (e.g.filter,take).
In practice, this type is oftenRepr itself, excepted in the case ofSeqView[A] (and otherView[A] subclasses), where it is “only”View[A].
The type of elements we can traverse over (e.g.
The type of elements we can traverse over (e.g.Int).
A conversion from the representation typeRepr to aIterableOnce[A].
A conversion from the representation typeRepr to aIterableOnce[A].
A conversion from the typeRepr toIterableOps[A, Iterable, C]
[Since version 2.13.0]\'conversion\' is now a method named \'apply\'[Since version 2.13.0]\'conversion\' is now a method named \'apply\'