Covariance/contravariance 以及与 consumers/producers 的关系
Covariance/contravariance and the relationship with consumers/producers
我在 covariance/contravariance 上阅读了这篇文章:http://julien.richard-foy.fr/blog/2013/02/21/be-friend-with-covariance-and-contravariance/
例子很清楚。然而,我很难理解最后得出的结论:
If you look at the definitions of Run[+A] and Vet[-A] you may notice
that the type Aappears only in the return type of methods of Run[+A]
and only in the parameters of methods of Vet[-A]. More generally a
type that produces values of type A can be made covariant on A (as you
did with Run[+A]), and a type that consumes values of type A can be
made contravariant on A (as you did with Vet[-A]).
From the above paragraph you can deduce that types that only have
getters can be covariant (in other words, immutable data types can be
covariant, and that’s the case for most of the data types of Scala’s
standard library), but mutable data types are necessarily invariant
(they have getters and setters, so they both produce and consume
values).
生产者:如果某物产生类型 A,我可以想象一些类型 A 的引用变量被设置为类型 A 的对象或 A 的任何子类型,但不是超类型,因此它可以是协变的是合适的。
Consumers:如果某物消费类型A,我想这意味着类型A可以用作方法中的参数。我不清楚这与协变或逆变有什么关系。
从示例中可以看出,将类型指定为 covariant/contravariant 会影响它如何被其他函数使用,但不确定它如何影响 类 他们自己。
It seems from the examples that specifying a type as covariant/contravariant affects how it can be consumed by other functions but not sure how it affects the classes themselves.
这篇文章着重于 class 用户的方差后果是正确的,而不是 class.
的实施者
文章表明,协变和逆变类型为用户提供更多自由(因为接受 Run[Mammal]
的函数有效地接受 Run[Giraffe]
或Run[Zebra]
)。对于实现者来说,视角是双重的:协变和逆变类型给了他们更多约束.
这些约束是协变类型不能出现在逆变位置,反之亦然。
例如考虑这个 Producer
类型定义:
trait Producer[+A] {
def produce(): A
}
类型参数 A
是协变的。因此我们只能用在协变的位置(比如方法return类型),不能用在逆变的位置(比如方法参数):
trait Producer[+A] {
def produce(): A
def consume(a: A): Unit // (does not compile because A is in contravariant position)
}
为什么这样做是违法的?如果编译这段代码会出什么问题?好吧,请考虑以下情况。首先,得到一些 Producer[Zebra]
:
val zebraProducer: Producer[Zebra] = …
然后将其向上转换为 Producer[Mammal]
(这是合法的,因为我们声明类型参数是协变的):
val mammalProducer: Producer[Mammal] = zebraProducer
最后,用 Giraffe
喂它(这也是合法的,因为 consume
方法 Producer[Mammal]
接受 Mammal
,而 Giraffe
是一个 Mammal
):
mammalProducer.consume(new Giraffe)
但是,如果您没记错的话,mammalProducer
实际上是一个 zebraProducer
,所以它的 consume
实现实际上只接受 Zebra
,而不是 [=26] =]!因此,在实践中,如果允许在逆变位置使用协变类型(就像我对 consume
方法所做的那样),类型系统将是不健全的。如果我们假装具有逆变类型参数的 class 也可以有一个处于协变位置的方法(代码见末尾),我们可以构造一个类似的场景(导致荒谬)。
(请注意,一些编程语言,例如 Java 或 TypeScript,具有这种不健全的类型系统。)
实际上,在 Scala 中,如果我们想在逆变位置使用协变类型参数,我们必须使用以下技巧:
trait Producer[+A] {
def produce(): A
def consume[B >: A](b: B): Unit
}
在那种情况下,Producer[Zebra]
不会期望在 consume
方法中传递实际的 Zebra
(但 B
类型的任何值,较低-bounded by Zebra
), 所以传递 Giraffe
是合法的,它是 Mammal
,它是 Zebra
.[=64= 的超类型]
附录:逆变的类似场景
考虑以下 class Consumer[-A]
,它有一个逆变类型参数 A
:
trait Consumer[-A] {
def consume(a: A): Unit
}
假设类型系统允许我们定义一个方法,其中 A
处于协变位置:
trait Consumer[-A] {
def consume(a: A): Unit
def produce(): A // (does not actually compile because A is in covariant position)
}
现在我们可以获取 Consumer[Mammal]
的实例,将其向上转换为 Consumer[Zebra]
(因为逆变)并调用 produce
方法来获取 Zebra
:
val mammalConsumer: Consumer[Mammal] = …
val zebraConsumer: Consumer[Zebra] = mammalConsumer // legal, because we claimed `A` to be contravariant
val zebra: Zebra = zebraConsumer.produce()
然而,我们的zebraConsumer
实际上是mammalConsumer
,其方法produce
可以return任何Mammal
,而不仅仅是Zebra
。所以,最后,zebra
可能会被初始化为 Mammal
而不是 Zebra
!为了避免这种荒谬,类型系统禁止我们在Consumer
class.
中定义produce
方法
我在 covariance/contravariance 上阅读了这篇文章:http://julien.richard-foy.fr/blog/2013/02/21/be-friend-with-covariance-and-contravariance/
例子很清楚。然而,我很难理解最后得出的结论:
If you look at the definitions of Run[+A] and Vet[-A] you may notice that the type Aappears only in the return type of methods of Run[+A] and only in the parameters of methods of Vet[-A]. More generally a type that produces values of type A can be made covariant on A (as you did with Run[+A]), and a type that consumes values of type A can be made contravariant on A (as you did with Vet[-A]).
From the above paragraph you can deduce that types that only have getters can be covariant (in other words, immutable data types can be covariant, and that’s the case for most of the data types of Scala’s standard library), but mutable data types are necessarily invariant (they have getters and setters, so they both produce and consume values).
生产者:如果某物产生类型 A,我可以想象一些类型 A 的引用变量被设置为类型 A 的对象或 A 的任何子类型,但不是超类型,因此它可以是协变的是合适的。
Consumers:如果某物消费类型A,我想这意味着类型A可以用作方法中的参数。我不清楚这与协变或逆变有什么关系。
从示例中可以看出,将类型指定为 covariant/contravariant 会影响它如何被其他函数使用,但不确定它如何影响 类 他们自己。
It seems from the examples that specifying a type as covariant/contravariant affects how it can be consumed by other functions but not sure how it affects the classes themselves.
这篇文章着重于 class 用户的方差后果是正确的,而不是 class.
的实施者文章表明,协变和逆变类型为用户提供更多自由(因为接受 Run[Mammal]
的函数有效地接受 Run[Giraffe]
或Run[Zebra]
)。对于实现者来说,视角是双重的:协变和逆变类型给了他们更多约束.
这些约束是协变类型不能出现在逆变位置,反之亦然。
例如考虑这个 Producer
类型定义:
trait Producer[+A] {
def produce(): A
}
类型参数 A
是协变的。因此我们只能用在协变的位置(比如方法return类型),不能用在逆变的位置(比如方法参数):
trait Producer[+A] {
def produce(): A
def consume(a: A): Unit // (does not compile because A is in contravariant position)
}
为什么这样做是违法的?如果编译这段代码会出什么问题?好吧,请考虑以下情况。首先,得到一些 Producer[Zebra]
:
val zebraProducer: Producer[Zebra] = …
然后将其向上转换为 Producer[Mammal]
(这是合法的,因为我们声明类型参数是协变的):
val mammalProducer: Producer[Mammal] = zebraProducer
最后,用 Giraffe
喂它(这也是合法的,因为 consume
方法 Producer[Mammal]
接受 Mammal
,而 Giraffe
是一个 Mammal
):
mammalProducer.consume(new Giraffe)
但是,如果您没记错的话,mammalProducer
实际上是一个 zebraProducer
,所以它的 consume
实现实际上只接受 Zebra
,而不是 [=26] =]!因此,在实践中,如果允许在逆变位置使用协变类型(就像我对 consume
方法所做的那样),类型系统将是不健全的。如果我们假装具有逆变类型参数的 class 也可以有一个处于协变位置的方法(代码见末尾),我们可以构造一个类似的场景(导致荒谬)。
(请注意,一些编程语言,例如 Java 或 TypeScript,具有这种不健全的类型系统。)
实际上,在 Scala 中,如果我们想在逆变位置使用协变类型参数,我们必须使用以下技巧:
trait Producer[+A] {
def produce(): A
def consume[B >: A](b: B): Unit
}
在那种情况下,Producer[Zebra]
不会期望在 consume
方法中传递实际的 Zebra
(但 B
类型的任何值,较低-bounded by Zebra
), 所以传递 Giraffe
是合法的,它是 Mammal
,它是 Zebra
.[=64= 的超类型]
附录:逆变的类似场景
考虑以下 class Consumer[-A]
,它有一个逆变类型参数 A
:
trait Consumer[-A] {
def consume(a: A): Unit
}
假设类型系统允许我们定义一个方法,其中 A
处于协变位置:
trait Consumer[-A] {
def consume(a: A): Unit
def produce(): A // (does not actually compile because A is in covariant position)
}
现在我们可以获取 Consumer[Mammal]
的实例,将其向上转换为 Consumer[Zebra]
(因为逆变)并调用 produce
方法来获取 Zebra
:
val mammalConsumer: Consumer[Mammal] = …
val zebraConsumer: Consumer[Zebra] = mammalConsumer // legal, because we claimed `A` to be contravariant
val zebra: Zebra = zebraConsumer.produce()
然而,我们的zebraConsumer
实际上是mammalConsumer
,其方法produce
可以return任何Mammal
,而不仅仅是Zebra
。所以,最后,zebra
可能会被初始化为 Mammal
而不是 Zebra
!为了避免这种荒谬,类型系统禁止我们在Consumer
class.
produce
方法