I fail make an implicit conversion List[A] => List[B], given implicit conversion A => B.
There is a very related question that has a solution but does not work for me. Also, here is a nice documentation for chaining implicits, which I used as the base of my code below.
The point I try to make in the code below is that chaining implicits works fine in all expected cases, even for collection-like objects like the Container
below, but fails for collections:
object ChainImplicits extends App{
case class A(n: Int)
implicit def toA(n: Int): A = A(n)
case class B(m: Int, n: Int)
implicit def aToB[T](a: T)(implicit f: T => A): B = B(a.n, a.n)
case class C(m: Int, n: Int, o: Int) {
def total = m + n + o
}
implicit def bToC[T](b: T)(implicit f: T => B): C = C(b.m, b.n, b.m + b.n)
// works
println(5.total)
println(new A(5).total)
println(new B(5, 5).total)
println(new C(5, 5, 10).total)
case class Container[T](value:T) {
def map[B](f: T => B) = Container(f(value))
}
implicit def ContainerConv[A,B](container:Container[A])
(implicit f: A => B): Container[B] = container.map(f)
val container = Container(1)
container.value.total //Works, as expected
def containerCTotal(containerC: Container[C]) = containerC.value.total
containerCTotal(container) //Works too!
implicit def listConv[A,B](collection: List[A])
(implicit f: A => B): List[B] = collection.map(f)
val list = List(1)
def CTotals(list: List[C]) = list.map(_.total)
CTotals(listConv(list)) //Explicit conversion works, finds the chain implicit conversions Int => C :)
CTotals(list) //... but implicit does not :(
def ATotals(list: List[A]) = list.map(_.total)
ATotals(list) // Simple (non-chained) conversion of contained values does not work either :(
}
How can I make these last conversions work?
I also tried the (deprecated) view bounds, getting the same results:
implicit def convert[B, A <% B](l: List[A]): List[B] = l map { a => a: B }
Just in case, the compilation error is the expected:
type mismatch;
found : List[Int]
required: List[ChainImplicits.C]
CTotals(list) //... but implicit does not :(
And similarly for ATotals(list)
I tried it on scala versions 2.11.8 and 2.11.4.
UPDATE:
I confirmed after Andreas' comment that the underlying issue has something to do with the List being covariant. Giving covariance to the Container
class above (i.e. Container[+A]
) makes the previously working implicit conversion at containerCTotal(container)
to fail.
List[+A]
, which might prevent the compiler from resolving the implicit conversion chain. I'll do a quick search to see if I can find a confirmation and/or workaround. – Offishval x: List[A] = List(1)
. This works.val list = List(1); val x: List[A] = list
. This does not. – ExtortionateA
, butA
it appears in a contravariant position (as a parameter) here:implicit f: A => B
. I guess, that's why the implicit conversion can't be applied. – Oubliette