Creating extension methods for SeqLike

2019-09-02 10:27发布

问题:

Let's say I want to extend the functionality of SeqLike:

import collection.SeqLike

implicit class Test[A, Repr <: SeqLike[A, Repr]](val sq: Repr) extends AnyVal {
  def foo(): Repr = sq
}

Then this doesn't work:

Vector(1, 2, 3).foo()

Also not:

new Test(Vector(1, 2, 3)).foo()

<console>:41: error: inferred type arguments
                    [Nothing,scala.collection.immutable.Vector[Int]] 
                    do not conform to class Test's type parameter bounds 
                    [A,Repr <: scala.collection.SeqLike[A,Repr]]
              new Test(Vector(1, 2, 3)).foo()
              ^

Only this works:

new Test[Int, Vector[Int]](Vector(1, 2, 3)).foo()

How can I make the implicit class work?

回答1:

Just have sq be typed as a SeqLike instead of a type parameter. This will allow the compiler to have some firm ground on which to rely, from which Repr can then be inferred. The only apparent problem now would be that we're going to have a type mismatch in foo, as it is supposed to return a Repr but we return a SeqLike[A, Repr].

The solution is simple enough, use the repr method:

implicit class Test[A, Repr](val sq: SeqLike[A, Repr]) extends AnyVal {
  def foo(): Repr = sq.repr
}
Vector(1, 2, 3).foo()


回答2:

I'd usually solve this like this:

implicit class Test[A, Repr <: SeqLike[A, Repr]](val sq: Repr with SeqLike[A, Repr]) extends AnyVal {

However, this won't work properly with Array or String, so IsTraversableLike is a better solution. If you truly need SeqLike, it's very easy to duplicate IsTraversableLike.