If you are like me, you occasionally want to write enhanced methods for Scala collections or sequences, but you'd like to bind the collection type as well as the element type, not just upcast to Seq[T].
There is a way to do it, and it works like this:
object enhance {
import scala.language.higherKinds
import scala.language.implicitConversions
import scala.collection.SeqLike
import scala.collection.generic.CanBuildFrom
implicit class Enhance[T, S[E] <: SeqLike[E, S[E]]](seq: S[T]) {
def first3(implicit cbf: CanBuildFrom[S[T], T, S[T]]) = seq.take(3)
def foo = seq.iterator
def goo(implicit cbf: CanBuildFrom[Nothing, T, S[T]]) = foo.take(3).to[S]
def moo[U](f: T => U)(implicit cbf: CanBuildFrom[S[T], U, S[U]]) = seq.map(f)
}
}
Using the type signature pattern above, the enhanced methods are aware of both the element type T
(e.g. Int
or String
) and the higher-kinded sequence type S
(e.g. List
or Vector
) and so it can return exactly the sequence type that it was called on.
Many sequence methods may require CanBuildFrom
implicits, which are added to the Enhance
methods as implicit parameters, where they are needed in the examples above.
Following is a sample run, showing the desired higher-kinded collection return types:
scala> import enhance._
import enhance._
scala> (1 to 10).toList.first3
res0: List[Int] = List(1, 2, 3)
scala> (1 to 10).toVector.first3
res1: Vector[Int] = Vector(1, 2, 3)
scala> (1 to 10).toList.goo
res2: List[Int] = List(1, 2, 3)
scala> (1 to 10).toVector.goo
res3: Vector[Int] = Vector(1, 2, 3)
scala> (1 to 10).toList.moo(_.toDouble)
res4: List[Double] = List(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0)
scala> (1 to 10).toVector.moo(_.toDouble)
res5: Vector[Double] = Vector(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With