I have some scala 2.13 code that basically boils down to this
import scala.language.implicitConversions
trait Base {
type V
def v: V
}
case class Derived(v: Int) extends Base {
type V = Int
}
object TestImplicitConversion {
implicit def getV[T <: Base](a: T): T#V = a.v
val a: Int = Derived(5)
}
Here I would expect the compiler to use the implicit conversion getV
to convert Derived
to Int
, but the code does not compile. Manually adding the call to getV
, will make the code compile. Can someone help me understand why the conversion where in the standard it is explained.
The way I found of making such a conversion work is by adding a second generic parameter and a constraint
implicit def getV2[T <: Base, S](a: T)(implicit constraint: T#V =:= S): S = constraint(a.v)
with this version the compiler uses the conversion and the code does compile.
Edit:
The alternative solution provided by @user using refinement type does indeed seem like a better approach. But it does not really provide an answer to why it original implementation does not work. So I am still interested in understanding why the compiler does not use the implicit def when an explicit call will make the code compile.
As gianluca aguzzi mentioned in the comments, type projection is unsound and should be avoided. Moreover,
T
is not a concrete type, so you can't use projections on it anyway. You can accept onlyBase#V
as a type parameter instead, and use a refinement type for the type ofa
:Thus, you can avoid casting and type projections.
Scastie