How can I alias a covariant generic type parameter
以下代码无法编译(在Scala 2.11中):
1 2 3 4 5 6 7 8 9 10
| 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)
}
} |
错误消息是:
1 2 3
| 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规范:
The right-hand side of a type alias is always in invariant position.
这意味着您无法创建别名T并在右侧指定变量类型R。 List[R]也是相同的,因为它也是协变的。
但是,您可以提供带有类型参数的类型别名:
1 2 3 4
| case class CovariantClass [+R ](value : R ) {
type T [+R ] = List [R ]
def get : R = value
} |
如果发现自己想为类型参数R加上别名,则应该首先将其命名为其他名称。
- 感谢您的回答和引用scala规范。我同意重命名R而不是对其进行别名,但是我想为Either[ReturnValueInCaseOfFailure,R]之类的东西取一个别名。不幸的是,您建议的变通方法将T参数化,但是它可能是我所需要的最接近的方法。
之所以禁止,是因为它会允许不正确的程序,这始终是规则。您可以这样重写它:
关于如何中断的示例,请考虑以下问题:
1 2 3 4 5 6
| 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我为可能出现的问题添加了一个示例。这只是一个示例的开始,以说明这与差异问题之间的关系。如果您需要更多信息,请查询有关方差的问题-应该有一些示例,说明为什么某些位置的变体类型是非法的(我知道我之前写过:)。