scala - Scala 中的逆变和协方差

标签 scala covariance contravariance

我刚学了Scala。现在我对逆变和协方差感到困惑。

从这里 page ,我在下面学到了一些东西:

协方差

也许子类型最明显的特征是能够在表达式中用较窄类型的值替换较宽类型的值。例如,假设我有一些类型 Real , Integer <: Real ,以及一些不相关的类型 Boolean .我可以定义一个函数 is_positive :: Real -> BooleanReal 上运行值,但我也可以将此函数应用于 Integer 类型的值(或 Real 的任何其他子类型)。这种用较窄(后代)类型替换较宽(祖先)类型的方法称为 covariance . covariance的概念允许我们编写通用代码,并且在推理面向对象编程语言中的继承和函数式语言中的多态时非常有用。

但是,我也从其他地方看到了一些东西:

scala> class Animal
    defined class Animal

scala> class Dog extends Animal
    defined class Dog

scala> class Beagle extends Dog
    defined class Beagle

scala> def foo(x: List[Dog]) = x
    foo: (x: List[Dog])List[Dog] // Given a List[Dog], just returns it
     

scala> val an: List[Animal] = foo(List(new Beagle))
    an: List[Animal] = List(Beagle@284a6c0)

参数 xfoocontravariant ;它需要一个 List[Dog] 类型的参数,但我们给它一个 List[Beagle] ,没关系

[我认为第二个例子也应该证明Covariance .因为从第一个示例中,我了解到“将此函数应用于类型 Integer(或 Real 的任何其他子类型)的值”。因此,相应地,这里我们将此函数应用于 List[Beagle] 类型的值。 (或 List[Dog] 的任何其他子类型)。但令我惊讶的是,第二个例子证明了 Cotravariance ]

我认为两个说的是同一件事,但一个证明Covariance和另一个 Contravariance .我也看到了this question from SO .然而我仍然很困惑。我是否遗漏了什么或其中一个例子是错误的?

最佳答案

最近一篇关于该主题的好文章(2016 年 8 月)是“ Cheat Codes for Contravariance and Covariance ” by Matt Handler .

它从“ Covariance and Contravariance of Hosts and Visitors ”中提出的一般概念和来自Andre Tyukin的图表开始和 anoopeliasanswer .

http://blog.originate.com/images/variance.png

它的结论是:

Here is how to determine if your type ParametricType[T] can/cannot be covariant/contravariant:

  • A type can be covariant when it does not call methods on the type that it is generic over.
    If the type needs to call methods on generic objects that are passed into it, it cannot be covariant.

Archetypal examples:


Seq[+A], Option[+A], Future[+T]

  • A type can be contravariant when it does call methods on the type that it is generic over.
    If the type needs to return values of the type it is generic over, it cannot be contravariant.

Archetypal examples:


`Function1[-T1, +R]`, `CanBuildFrom[-From, -Elem, +To]`, `OutputChannel[-Msg]`

关于逆变,

Functions are the best example of contravariance
(note that they’re only contravariant on their arguments, and they’re actually covariant on their result).
For example:


class Dachshund(
  name: String,
  likesFrisbees: Boolean,
  val weinerness: Double
) extends Dog(name, likesFrisbees)

def soundCuteness(animal: Animal): Double =
  -4.0/animal.sound.length

def weinerosity(dachshund: Dachshund): Double =
  dachshund.weinerness * 100.0

def isDogCuteEnough(dog: Dog, f: Dog => Double): Boolean =
  f(dog) >= 0.5

Should we be able to pass weinerosity as an argument to isDogCuteEnough? The answer is no, because the function isDogCuteEnough only guarantees that it can pass, at most specific, a Dog to the function f.
When the function f expects something more specific than what isDogCuteEnough can provide, it could attempt to call a method that some Dogs don’t have (like .weinerness on a Greyhound, which is insane).

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

相关文章:

java - 集群中 Akka 成员(actor)查找

c# - 为什么 ReadOnlyCollection 不允许协方差?

scala - 在解释器中蒸发 Predef.any2stringadd

c#枚举协方差不起作用

c# - 无法将任务<List<TEntity>> 转换为任务<IList<TEntity>>

scala - 如果 `K >: T` 是协变或逆变,那么 `K <: T` 和 `T` 的方差是多少?

scala - Scala元组的常规 'map'函数?

c# - Casting List<T> - 协方差/逆变问题

scala - 我应该在多行 Scala 方法中使用返回吗?

c++ - Scala 可变数量的参数是 F-Bounded 类型的子类