简体   繁体   中英

Scala: what's the reason of 'covariant type R occurs in invariant position'?

Given the following example code:

trait Gen[T, +R] {

  def follow[K](gen: Gen[R, K]): Gen[T, K]

  def i: T
  def j: R
}
case class GenImpl[T, R](i: T, j: R) extends Gen[T, R] {

  override def follow[K](gen: Gen[R, K]): Gen[T, K] = {
    GenImpl[T, K](this.i, gen.j)
  }
}

The compiler will give the following error:

Error:(9, 17) covariant type R occurs in invariant position in type Gen[R,K] of value gen
  def follow[K](gen: Gen[R, K]): Gen[T, K]
                ^

however, this code cannot possibly fail a type validation in runtime. The covariant of R simply means if:

R1 <: R, R2 <: R

Then:

Gen[T, R1] <:< Gen[T, R]

So a Gen[R, K] can be a parameter of .follow() of both Gen[T, R] and Gen[T, R1]. But Gen[R1, K] can only be a parameter of .follow() of Gen[T, R1], if applied on Gen[T, R2] or Gen[T, R] it will trigger a compilation error. There is no need to set R or R1 in Gen[R/R1, K] to be contravariant to do its job.

I can't see a case that can pass compilation and fail in runtime. What do you think? Is the compiler throwing a false alarm?

Suppose we have

class R0
class R1 extends R0
class T0

Then any instance of Gen[T0, R0] must offer the following service:

def follow[K](gen : Gen[R0, K]) : Gen[T0,K]

The variance annotation claims that Gen[T0, R1] <:< Gen[T0, R0] . So it must be substitutable and offer the same service. But Gen[T0, R1] actually offers the following service:

def follow[K](gen: Gen[R1, K]) : Gen[T0,K]

Suppose I had code like this:

def doFollow[K]( g : Gen[T0, R0], h : Gen[R0, K] ) : Gen[T0,K] = {
  g.follow( h )
}

Cool. Let's suppose I have some instances, constructed however:

val g0 : Gen[T0, R0]     = ???
val h0 : Gen[R0, String] = ???
val g1 : Gen[T0, R1]     = ???

I call doFollow[String]( g0 , h0 ) and everything works great, I get a result.

g1 , I have claimed in my variance annotation, is substitutable for g0 . So now I try doFollow[String]( g1 , h0 ) . Oops. Then I must execute, in the body of the function,

g1.follow[String]( h0 )

but g1 only knows how to follow a Gen[R1, String] . h0 is a Gen[R0, String] . It is not true that Gen[R0, String] <:< Gen[R1, String] , since the first parameter is declared invariant (it would have to be contravariant). So h0 is not an acceptable argument to g1 's follow method, while it is an acceptable argument to g0 's follow method. Things of g1 's type, Gen[T0, R1] , are not in fact substitutable for things of g0 's type Gen[T0, R0] .

But your variance annotation requires that Gen[T0, R1] objects can be substitutable for Gen[T0, R0] s. They cannot, and the compiler correctly called you on it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM