-->

Scala type members variance

2019-04-29 10:38发布

问题:

Consider this short snippet:

trait Table[+A] {
  type RowType = Seq[A]
}

Scala 2.11.7 compiler gives the following error:

covariant type A occurs in invariant position in type Seq[A] of type RowType

Why is A considered to be in invariant position in Seq[A] while Seq itself is defined as trait Seq[+A]?

Also, could you please provide a use case demonstrating possible issues with this type definition if we ignore the error?

回答1:

For any B <: A your Table[B]#RowType will be more concrete than Table[A]#RowType. More concrete does not mean the same, so compiler is considering parameters for type aliases as invariant positions.

How you could fix this.

Abstract member

You can define your type as abstract, that mean you should define it later and probably stuck with same problem, but on trait Table level such definition will be correct

trait Table[+A] {
  type RowType <: Seq[A]
}

Concrete higher-kinded type

You can define parameterized type member, which could lead to changing how you can use this type, but in most cases should do the job.

trait Table[+A] {
  type RowType[+X] = Seq[X]
}

On type member variance

Not the strongest my field but i try to describe my thoughts.

Suppose you have

trait Table[+A] {
  type RowType = Seq[A]
}

def mkTable[A]: Table[A]  = new Table[A] {}

then you do following

val tupleTable = mkTable[(String, String)]
val prodTable: Table[Product] = tupleTable 

So what would be prodTable.RowType?

In case of your definition it should be Seq[Product]. But wait, prodTable and tupleTable are the same object, so their members should be the same, so prodTable.RowType should be Seq[(String, String)]

But if you change to the first approach like

trait Table[+A] {
  type RowType <: Seq[A]
}

def mkTable[A]: Table[A]  = new Table[A] {
  type RowType = Seq[A]
}

Compiler would know that RowType for Table[Product] is some type <: Seq[Product] which is true for Seq[(String, String)] and all ambiguilties are gone.