Daniel Shin
Daniel Shin

Reputation: 5206

How to define implicit class for Traversable with CanBuildFrom?

I'm trying to add some methods to Traversable trait by pimping via implicit class.

But I'm kind of lost with CanBuildFrom trait. Consider the following:

implicit class TraversableExt[+A, +Repr <: Traversable[A]](traversable: Repr) {
  def debug[That](name: String)(implicit bf: CanBuildFrom[Repr, A, That]): That =
    traversable.map{ a => println(name + ": " + a); a }(bf)
}

This fails with error:

Error:(21, 59) type mismatch;
found : scala.collection.generic.CanBuildFrom[Repr,A,That]
required: scala.collection.generic.CanBuildFrom[Traversable[A],A,That]
traversable.map{ a => println(name + ": " + a); a }(bf) ^

I'm guessing that since Repr in CanBuildFrom[-Repr, -Elem, +To] is contravariant and thus my Repr which is upper-bound by Traversable[A] may not work.

But overall, I'm pretty lost. Can anyone help?

Upvotes: 4

Views: 195

Answers (1)

Giovanni Caporaletti
Giovanni Caporaletti

Reputation: 5556

You have to use TraversableLike and higher kinded types to make the type inferencer happy and make it work with CanBuildFrom:

implicit class TraversableExt[A, C[X] <: TraversableLike[X, C[X]]](traversable: C[A]) {
  def debug[That](name: String)(implicit bf: CanBuildFrom[C[A], A, That]): That =
    traversable.map{ a => println(name + ": " + a); a }(bf)
}

The compiler is now able to correctly infer the type of your collection C[A] instead of generically look for a CanBuildFrom[Traversable[A],...]

As a rule of thumb, when you want to return the same collection class as the one passed in, you have to use the *Like classes

Upvotes: 6

Related Questions