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