【问题标题】:Overriding variance in subtype子类型中的压倒性差异
【发布时间】:2014-03-20 12:33:56
【问题描述】:

有人可以解释一下为什么示例 1 可以编译,而示例 2 没有吗?

示例 1:

trait Foo[+A]
trait Bar[A] extends Foo[A]

示例 2:

trait Foo[A[+_]]
trait Bar[A[_]] extends Foo[A]

示例 2 无法编译并出现以下错误消息:“类型参数 (A) 的种类不符合 trait Foo 中类型参数的预期种类 (类型 A)。A 的类型参数与类型 A 的不匹配预期参数:类型_(在 trait Bar) 是不变的,但类型 _ (在 trait Foo 中) 被声明为协变"

【问题讨论】:

    标签: scala higher-kinded-types


    【解决方案1】:

    在示例 1 中,+A 不受 A 约束。任何类型都可以是 Foo 的参数。这是对Foo 的约束。意思是在Foo的接口中,A只能出现在协变位置(简而言之,可能是方法结果,而不是方法参数)。

    拥有Bar不协变意味着Bar的接口不满足相同的约束(或至少不宣传它),所以可能在Bar中,添加了一个带有A参数的方法。这很常见。例如有collection.Seq[+A],它由collection.mutable.Seq[A] 扩展。这不会造成不健全的问题。如果Y <: X,则Bar[Y] 不是Bar[X],但仍是Foo[X]

    另一方面,在示例 2 中,A[+_] 是对 A 的约束。只有具有协变类型参数的类型才可以是 Foo 的参数。 Foo 的代码可能会利用该约束,例如将 A[String] 分配给 A[Any]Foo 的代码中的某处。

    然后允许 Bar 用非协变类型实例化是不合理的。当A 不再协变时,仍然可以调用从Foo 继承的代码并将A[String] 分配给A[Any]

    只要您允许解除对通用参数的约束,就会发生非常相似的可靠性问题。假设您有Foo[X <: Ordered[X]](或Foo[X : ordering]),并且在Foo 中有一个方法sort。然后假设Bar[X] extends Foo 是允许的。也许Bar 的代码中没有任何内容需要订购X,但sort 仍然是可调用的,并且肯定会与无法订购的项目行为不端。


    关于您在 Traversable[+Ele​​m, +Col[+_]] 的评论中的问题,并将其扩展为一个可变类:

    从技术上讲,是的,你可以扩展它并在里面放一些var youCanMutateThat : Elem = _。我想这不是你要找的。但我想你的计划是允许使用带有可变 Col 的扩展,我认为你不能这样做,原因如上所述。但是,为什么你首先有 Col[+_] 约束?

    【讨论】:

    • 有道理,谢谢。简短的后续问题:目标是使特征 Traversable[+Ele​​m, +Col[+_]] { def map[B](f : Elem => B) : Col[B] } 有什么办法可以创建一个可变的子类(如 mutable.Seq 示例)
    【解决方案2】:

    在示例 2 中需要为 A[_] 添加协方差

    trait Bar[A[+_]] extends Foo[A]
    

    因为 Foo 期望协变类型作为参数(+_ 是替代类型约束的一部分)并且继承类型需要保证参数是协变的(替代类型的限制)。

    在示例 1 中,您定义 Foo(如容器)通过参数是协变的,并且继承的容器可以是不变的(对替换类型没有限制)

    Martin-Löf 类型理论的更多细节(谓词参数多态性

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-01-01
      • 2020-08-27
      • 1970-01-01
      相关资源
      最近更新 更多