Laurent Laurent - 1 year ago 59
Scala Question

Scala cannot access getter methods when using implicit type-class (variance + polymorphism)

In the following example I don't understand why I cannot access the getter methods on the

M1
,
M2
,
M3
classes.
I need the trait
S
to be covariant with
T
in order to implement the
runSeq
method. Because of that the
toto
method needs to take
U
as superclass of
T
. When I do that, I can no longer access the constructor fields of the argument
t
.
If I remove the covariance requirement (
+T
) everything works but then I dont know how to implement the
runSeq
method (in particular it needs to receive an implicit type-class object).

abstract class M
case class M1(s: String) extends M
case class M2(i: Int) extends M
case class M3(b: Boolean) extends M
trait S[+T] {
def follow(s: String): String
def toto[U >: T](t: U): String
}
implicit object S1 extends S[M1] {
val m1 = M1("1") // this is for testing only
def follow(s: String): String = s + ".M1." + toto(m1)
def toto[M1](t: M1): String = "toto" + t.s // ERROR: cannot resolve "s"
}
implicit object S2 extends S[M2] {
val m2 = M2(2) // for testing purposes only
def follow(s: String): String = s + ".M2." + toto(m2)
def toto[M2](t: M2): String = "toto" + t.i.toString // ERROR: cannot resolve "i"
}
implicit object S3 extends S[M3] {
val m3 = M3(true) // for testing purposes
def follow(s: String): String = s + ".M3." + toto(m3)
def toto[M3](t: M3): String = "toto" + t.b.toString // ERROR: cannot resolve "b"
}
def run[T: S](s: String): String = implicitly[S[T]].follow(s)

run[M1]("run")

def runSeq(seq: S[M]*)(s: String) =
seq.foldLeft(s)((st, tg) => run(st)(tg))

runSeq(S3,S2,S1)("runSeq")

Answer Source

First, note that def toto[U >: T](t: U): String is basically the same as def toto(t: Any): String, since Any satisfies the bound for any possible T. This also explains why you can't access T's members in t. Note that variance is irrelevant for this.

T is only covariant because runSeq needs to take a sequence of S[T] with various types of T derived from M. Is there a better way of doing that?

def runSeq(seq: S[_ <: M]*) corresponds directly to this. However, you are likely to run into type erasure problems: given an S[_ <: M], you can't know its actual type parameter or check if an M is of a suitable type. Look up ClassTag and TypeTag for solutions. Again, this is a problem independent of variance.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download