最近我注意到方差注释可以用在类型别名中。以下是Predef
的示例:
type Function[-A, +B] = Function1[A, B]
我开始思考,可以在哪里使用它。显然,您不能将方差更改为相反,或使不变类型表现为共变或逆变。编译器会抛出一个错误,比如
scala> type BrokenFunc[+T, -R] = Function1[T, R]
<console>:7: error: covariant type T occurs in contravariant position in type
[+T, -R]T => R of type BrokenFunc
但是,你可以使一些变体类型的行为像不变量(至少,编译器不会与此争论)。所以,我尝试制作List
scala> type InvList[T] = List[T]
defined type alias InvList
但是这个新的不变量List
仍然表现得像它的原始协变版本:
scala> val l: InvList[String] = List("foo")
l: InvList[String] = List(foo)
scala> val anyList: InvList[Any] = l
anyList: InvList[Any] = List(foo)
那么,我错过了什么?类型别名中的方差注释的目的是什么?您能举例说明带有方差注释的类型别名,它与原始类型不同。
答案 0 :(得分:8)
所以,我不确定,但我会提供一个可能的解释。
Scala中的类型别名相当“弱”;他们没有完全创建新类型,只是编写旧类型的新方法(以及新的路径依赖类型);这意味着如果你定义
type InvList[T] = List[T]
并写InvList[T]
,就像你写List[T]
一样;这就是为什么InvList[Int] <: InvList[Any]
,因为,重写,这只是List[Int] <: List[Any]
。我实际上并不确定“弱”的Scala类型别名是什么......它们比Haskell强一点,因为路径依赖类型,但比类声明弱。也许其他人可以进一步解释。
那么,为什么Scala允许你在那里放置方差注释,如果它只是忽略它们并且仍然重写类型?它适用于类型成员。这是你可以说
trait A { type F[+T] }
并要求实现符合+T
方差,以便您允许
trait B extends A { type F[+T] = List[T] }
但不是
trait C extends A { type F[T] = Function[T,T] }
或者,从Scala Language Spec S4.3的这句话开始。
类型构造函数声明对具体类型施加了额外的限制 这可能是什么意思。除了边界L和U之外,还有类型参数子句 可以施加更高阶的界限和差异,由一致性决定 类型构造函数(§3.5.2)。
答案 1 :(得分:2)
如果仅声明抽象类型(如Domain
),则将强制执行不变性。如果抽象类型被定义为具有更宽松的方差,那么一旦知道(例如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
}