13

最近我注意到可以在类型别名中使用方差注释。以下是来自的示例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)

那么,我错过了什么?类型别名中差异注释的目的是什么?您能否举一个带有差异注释的类型别名示例,它与原始类型不同。

4

2 回答 2

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中的这句话。

类型构造函数声明对 t 可能代表的具体类型施加了额外的限制。除了边界 L 和 U 之外,类型参数子句可能会施加更高阶的边界和方差,由类型构造函数的一致性(第 3.5.2 节)控制。

于 2012-05-10T06:41:04.407 回答
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
}
于 2012-05-10T05:52:00.117 回答