Recently I\'ve noticed that variance annotations can be used in type aliases. Here is example from Predef
:
type Function[-A, +B] = Function1[A,
If the abstract type is only declared (as in Domain
), the invariance will be enforced. If the abstract type is defined to have a more relaxed variance, this is respected once known (as in DomainImpl
):
trait Domain {
type InvList[T]
val xi: InvList[Int]
private val xa: InvList[Any] = xi // won't compile
}
class DomainImpl extends Domain {
type InvList[T] = List[T]
val xi: InvList[Int] = List(1)
private val xa: InvList[Any] = xi // will compile
}
So, I don't know for sure, but I'm going to offer a possible explanation.
Type aliases in Scala are fairly "weak"; they do not completely create new types, just new ways of writing the old types (and new path dependent types); this means that if you define
type InvList[T] = List[T]
and write InvList[T]
, it is exactly as if you wrote List[T]
; that's why InvList[Int] <: InvList[Any]
, because, rewritten, this is just List[Int] <: List[Any]
. I'm not actually sure exactly how "weak" Scala type aliases are... they are a little stronger than Haskell's because of path-dependent types, but are weaker than class declarations. Maybe someone else can explain further.
So, why does Scala allow you to put variance annotations in there, if it's just going to ignore them and rewrite the type anyway? It's for type members. It's so that you can say
trait A { type F[+T] }
and require that implementations conform to the +T
variance, so that you allow
trait B extends A { type F[+T] = List[T] }
but not
trait C extends A { type F[T] = Function[T,T] }
Or, as from this sentence from the Scala Language Spec S4.3.
A type constructor declaration imposes additional restrictions on the concrete types for which t may stand. Besides the bounds L and U , the type parameter clause may impose higher-order bounds and variances, as governed by the conformance of type constructors (§3.5.2).