【问题标题】:How can I alias a covariant generic type parameter如何给协变泛型类型参数起别名
【发布时间】:2014-10-18 00:18:12
【问题描述】:

以下代码无法编译(在 Scala 2.11 中):

case class CovariantClass[+R](value: R) {
  type T = R
  def get: R = value
}

object Main {
  def main(args: Array[String]): Unit ={
    println(CovariantClass[String]("hello").get)
  }
}

错误信息是:

Error:(4, 8) covariant type R occurs in invariant position in type R of type T
  type T = R
       ^

为什么我不能为协变类型参数设置别名?如果我删除行type T = R,代码编译并打印hello,所以别名似乎是问题所在。不幸的是,这意味着我无法为更复杂的类型创建别名,例如,type T = List[R] 也无法编译,尽管List 是协变的。

【问题讨论】:

    标签: scala covariance type-alias


    【解决方案1】:

    来自scala spec

    类型别名的右侧始终处于不变的位置。

    这意味着您不能创建别名T 并在右侧指定变体类型R。这同样适用于List[R],因为它也是协变的。

    可以,但是提供带有类型参数的类型别名:

    case class CovariantClass[+R](value: R) {
      type T[+R] = List[R]
      def get: R = value
    }
    

    如果您发现自己想为类型参数 R 起别名,您可能应该首先将其命名为其他名称。

    【讨论】:

    • 感谢您的回答和引用 scala 规范。我同意重命名R 而不是给它起别名,但我想为Either[ReturnValueInCaseOfFailure,R] 之类的东西取一个别名。不幸的是,您提出的解决方法使 T 参数化,但这可能是我能得到的最接近我想要的。
    【解决方案2】:

    这是被禁止的,因为它会允许不正确的程序,这始终是规则。你可以这样重写:

    case class CovariantClass[+R](value: R) {
      type T <: R
      def get: R = value
    }
    

    至于它如何破坏的示例,请考虑以下内容:

    case class CovariantClass[+R](value: R) {
      type T = Int
      def get: R = value
      def put(x: T) {}
      def put2(x: R) {}
    }
    

    因为T 是如何定义的,所以它是不变的。这意味着它可以用于协变类型不能使用的地方,例如上面所见。请注意,put 可以编译,但 put2 没有。

    【讨论】:

    • 有趣。虽然在这个例子中,R 应该是T 的下限,但代码会中断,对吧?另外,我看不出如何在不引入类型绑定的情况下破坏它。它只是一种保守的设计选择吗?无论如何:谢谢你的例子。
    • @KuluLimpa 我为可能出现的问题添加了一个示例。这只是一个示例的开始,以说明这与方差问题之间的关系。如果您需要更多信息,请查找有关方差的问题——应该有一些示例说明为什么某些位置的变体类型是非法的(我知道我之前写过一些:))。
    猜你喜欢
    • 1970-01-01
    • 2017-07-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多