问题
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.
来源:https://stackoverflow.com/questions/33458782/scala-type-members-variance