Chain implicit conversion of collection

2020-03-29 14:06发布

问题:

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.