scala - Scala中的存在与协方差

标签 scala covariance existential-type

考虑下面的两个代码。他们实现了相同的目标:只有这样的 A[T] -s 可以存储在Container在哪里 T扩展 C
然而,他们使用两种不同的方法来实现这一目标:

1) 存在主义

2) 协方差

我更喜欢第一个解决方案,因为那样 A保持简单。我有什么理由想要使用第二种解决方案(协方差)?

我对第二种解决方案的问题是它是 不自然从某种意义上说,它不应该是 A -s 描述我可以在 Container 中存储什么以及不能存储什么的责任,这应该是 Container 的责任。第二种解决方案也是更复杂一旦我想开始对 A 进行操作然后我必须处理所有与协方差相关的东西。

使用第二种(更复杂、更不自然)解决方案我会得到什么好处?

object Existentials extends App {

  class A[T](var t:T)

  class C

  class C1 extends C

  class C2 extends C

  class Z

  class Container[T]{
    var t:T = _
  }

  val c=new Container[A[_<:C]]()
  c.t=new A(new C)
  //  c.t=new Z // not compile

  val r: A[_ <: C] = c.t

  println(r)
}

object Cov extends App{
  class A[+T](val t:T)

  class C

  class C1 extends C

  class C2 extends C

  class Z

  class Container[T]{
    var t:T = _
  }

  val c: Container[A[C]] =new Container[A[C]]()
  c.t=new A(new C)
  //c.t=new A(new Z) // not compile

  val r: A[C] = c.t

  println(r)
}

编辑(回应阿列克谢的回答):

评论:
“我对第二种解决方案的问题是,从某种意义上说,描述我可以在 Container 中存储什么而不是什么,这应该是 Container 的责任,这是不自然的。”

如果我有 class A[T](var t:T)这意味着我只能存储 A[T] -s 而不是( A[S] 其中 S<:T )在容器中,在任何容器中。

但是,如果我有 class A[+T](var t:T)然后我可以存储A[S]在哪里 S<:T以及在任何容器中。

所以当声明 A是不变的还是协变的,我决定什么类型的 A[S] 可以存储在容器中(如上所示),这个决定发生在 A 的声明中.

但是,我认为,这个决定应该发生在容器的声明中,因为它是特定于容器的,允许进入该容器的只有 A[T]。 -s 或 A[S]在哪里 S<:T -s。

换句话说,改变 A[T] 中的方差具有全局效果,同时从 A[T] 更改容器的类型参数至A[_<:S]对容器本身有明确的局部影响。所以这里的“变化应该有局部影响”的原则也有利于存在主义的解决方案。

最佳答案

在第一种情况下 A更简单,但在第二种情况下,它的客户是。由于您使用 A 的地方通常不止一个。 ,这通常是一个值得权衡的选择。你自己的代码演示一下:当你需要写A[_ <: C]在第一种情况下(在两个地方),您可以使用 A[C]在第二个。

此外,在第一种情况下,您可以只写 A[C]在哪里 A[_ <: C]真的很需要。假设你有一个方法

def foo(x: A[C]): C = x.t

现在你不能调用foo(y)y: A[C1]即使它是有道理的:y.t确实有类型 C .

当您的代码中发生这种情况时,可以修复它,但是第三方呢?

当然,这也适用于标准库类型: if 类型如 MaybeList不是协变的,所有获取/返回它们的方法的签名都必须更复杂,或者许多当前有效且完全有意义的程序会中断。

it should not be A-s responsibility to describe what I can store in a Container and what not, that should be the Container's responsibility.



差异与您可以在容器中存储什么无关。大约是什么时候A[B]A[C] 的子类型.这个论点有点像说你不应该有 extends根本:否则class Apple extends Fruit允许您存储 AppleContainer[Fruit] , 并决定是 Container的责任。

关于scala - Scala中的存在与协方差,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46021771/

相关文章:

scala - 如何避免使用 null?

scala - RDD 访问另一个 RDD 中的值

scala - java.lang.String 不能转换为 scala.Serializable

c++ - C++ 协方差/覆盖/循环问题

C# 转换继承的通用接口(interface)

scala - 泛型中的任何与下划线

haskell - GHC 是否对存在类型使用动态调度?

json - 为蛇形 ADT 配置了 JsonCodec

java - Java 中的逆变问题

scala - 存在类型如何与路径依赖类型重叠?