The compiler is failing to choose the correct implicit conversion method when that conversion occurs within an implicit class declaration. In the example below, I have a Foo[T]
class and an implicit Helper
class that takes a Foo
and provides a print
method. That print method calls show
, which is itself a method provided by an implicit conversion on Foo
.
The catch is that there are two possible conversions that provide show
: one converts Foo[T]
to a Bar[T]
and the other converts Foo[Array[T]]
to a BarArray[T]
. The idea is that when we have a Foo
that contains an array, we want to apply the more specific BarArray
conversion. As far as I understand, the compiler chooses the conversion with the most specific type first.
This works in normal contexts as shown in the example below, but breaks within the context of the print
method in the implicit Helper
class. There, the same show
method is called and therefore I would expect the same conversions should be applied. However, in this context the compiler always chooses the Bar
conversion, even when it has a Foo[Array[T]]
and should choose the BarArray
conversion.
What is going wrong?
Minimal failing code example:
package scratch
import scala.language.implicitConversions
class Foo[T](val value: T) {}
object Foo {
implicit def fooToBar[T](foo: Foo[T]): Bar[T] = {
new Bar(foo.value)
}
implicit def fooArrayToBarArray[T](foo: Foo[Array[T]]): BarArray[T] = {
new BarArray(foo.value)
}
}
class Bar[T](val value: T) {
def show(): String = {
s"Bar($value)"
}
}
class BarArray[T](val value: Array[T]) {
def show(): String = {
value.map(v => s"Bar($v)").mkString(", ")
}
}
object Scratch extends App {
implicit class Helper[T](foo: Foo[T]) {
def print(): Unit = {
println(foo.show())
}
}
val foo0 = new Foo(123)
val foo1 = new Foo(Array(123, 456))
// conversions to Bar and BarArray work correctly here
println(foo0.show()) // Bar(123)
println(foo1.show()) // Bar(123), Bar(456)
// conversions called from within the implicit Helper class
// always choose the Bar conversion
foo0.print // Bar(123)
foo1.print // Bar([I@xxxxxxxx) <- should be Bar(123), Bar(456)
}
Versions:
- Scala 2.12.10
- SBT 1.4.3
- JDK 1.8.0_241