Reputation: 4640
I am trying to create an implicit converter that would would use a implicit converter that's currently in scope (eg. A => B)
and would be able to convert any type of Traversable[A]
to Traversable[B]
.
So far I got:
implicit def convertLists[A, B](from: Traversable[A])(implicit conv: A => B): Traversable[B] = from.map(conv)
This however does not work with:
val listOfB: List[B] = convertLists(List[A]())
If I change Traversable
to List
, then it works fine, eg:
implicit def convertLists[A, B](from: List[A])(implicit conv: A => B): List[B] = from.map(conv)
Do I need to add anything else to allow the converter to accept any subclass of Traversable
?
Upvotes: 5
Views: 973
Reputation: 8673
Using the approach in scala collections library you could come out with such code:
import scala.collection.generic.CanBuildFrom
import scala.collection.TraversableLike
implicit val boolToInt = (b: Boolean) => if (b) 1 else 0
implicit def convertLists[A, B, Repr, That](from: TraversableLike[A, Repr])(implicit conv: A => B, bf: CanBuildFrom[Repr, B, That]): That = from map conv
val listOfB: List[Int] = List(true, false)
which gives
listOfB: List[Int] = List(1, 0)
Upvotes: 2
Reputation: 14224
You have explicitly defined convertLists
to return Traversable[B]
. Traversable
is not a subtype of List
(it's its supertype), so the result of convertLists
(Traversable
) can't be a return type of listOfB
(List
).
You can define convertLists
to infer the result type based on the type of its argument, if you use CanBuildFrom
:
import scala.collection.TraversableLike
import scala.collection.generic.CanBuildFrom
import scala.language.higherKinds
// `CC` is some concrete subtype of `Traversable`
// `That` is an automatically inferred result collection type
implicit def convertLists[A, B, CC[T] <: TraversableLike[T, CC[T]], That](
from: CC[A]
)(
implicit
conv: A => B,
// witness that it's possible to build
// a collection with elements `B` from a collection `CC[A]`,
// and compute the resulting collection type `That`
bf: CanBuildFrom[CC[A], B, That]
): That = from.map(conv)
Now assuming the following simple definition of A
and B
case class A(i: Int)
case class B(i: Int)
implicit def aisb(a: A): B = B(a.i)
The following works:
val listOfB: List[B] = convertLists(List[A](A(1)))
And you don't have to call convertLists
explicitly:
val listOfB2: List[B] = List[A](A(1))
Upvotes: 2