I have difficulties understanding Scalas type system. What's the correct way to write this code?
implicit class ExtSeq[A <: GenSeqLike[B, A], B](seq: A) {
def prePadTo(len: Int, elem: B) = seq.reverse.padTo(len, elem).reverse
}
Simplest way - just use Seq
:
implicit class ExtSeq[T](seq: Seq[T]) {
def prePadTo(len: Int, elem: T) = seq.reverse.padTo(len, elem).reverse
}
Generic way - use IsTraversableOnce
:
import scala.collection.GenTraversableOnce
import scala.collection.generic.{IsTraversableOnce, CanBuildFrom}
class PrePadTo[T, Repr](coll: GenTraversableOnce[T]) {
def prePadTo[That](len: Int, elem: T)
(implicit cbf: CanBuildFrom[Repr, T, That]): That = {
val b = cbf()
val tmp = coll.toSeq
b ++= Iterator.fill(len - tmp.size)(elem)
b ++= tmp.iterator
b.result
}
}
implicit def toPrePadTo[Repr](coll: Repr)
(implicit traversable: IsTraversableOnce[Repr]) =
new PrePadTo[traversable.A, Repr](traversable.conversion(coll))
Usage:
scala> "abc".prePadTo(5, '-')
res0: String = --abc
There are two issues with your implicit class:
B
isn't constrained, so it will end up as Nothing
. You need a way to infer it from the input collection.
You need an implicit CanBuildFrom
on your prePadTo
method so that the padTo
method knows how to build a new instance of the input collection.
Hence:
import scala.collection.{GenSeq, GenSeqLike}
import scala.collection.generic.CanBuildFrom
implicit class ExtSeq[A <: GenSeqLike[B, A], B](seq: A with GenSeq[B]) {
def prePadTo(len: Int, elem: B)(implicit cbf: CanBuildFrom[A, B, A]) =
seq.reverse.padTo(len, elem).reverse
}
And then:
scala> List(1,2,3).prePadTo(10, 4)
res1: List[Int] = List(4, 4, 4, 4, 4, 4, 4, 1, 2, 3)
scala> Vector('1','2','3').prePadTo(10, '4')
res2: Vector[Char] = Vector(4, 4, 4, 4, 4, 4, 4, 1, 2, 3)
Note that it will not work for classes that are only implicit convertible to a Seq
, like String
for example.