Invariance, Covariance and Contravariance – there a metaphor?

huangapple go评论54阅读模式
英文:

Invariance, Covariance and Contravariance - Is there a metaphor?

问题

我想首先说一下,我知道这个话题已经在Stack Overflow上深入讨论过。但我觉得提供的解释并没有提供一个有助于记住示例和知识的强大故事。

有人能否提供一个比喻或故事,帮助我理解与Kotlin中以下示例相关的协变性和逆变性的概念?

以下是构造:

open class Animal
class Dog : Animal()

fun doSomething(fn: (Dog) -> Number) {}

我试图理解为什么以下代码有效:

animalToIntMethod: (Animal) -> Int = {TODO()}
doSomething(animalToIntMethod)

在Kotlin中,(Dog) -> Number 的内部表示是 Function<in Dog, out Number>,涉及到逆变性和协变性的使用,具体来说是 Function<in T, out R>。然而,我很难理解这些概念在这个上下文中实际是如何工作的。有人能否请解释在这个情境中逆变性和协变性的原理?

英文:

I want to start by saying that I know this topic has been discussed in depth on Stack Overflow. But I don't feel like the explanations given provide a strong story that helps to retain the examples and knowledge.

Can someone provide a metaphor or story to help me understand the concept of covariance and contravariance in relation to the following example in Kotlin?

Here are the constructs:

open class Animal
class Dog : Animal()

fun doSomething(fn: (Dog) -&gt; Number) {}

I'm trying to understand why the following code works:

animalToIntMethod: (Animal) -&gt; Int = {TODO()}
doSomething(animalToIntMethod)

In Kotlin, the internal representation of (Dog) -&gt; Number is Function&lt;Dog, Number&gt;, which involves the use of contravariance and covariance, specifically Function&lt;in T, out R&gt;. However, I'm having trouble understanding how these concepts actually work. Could someone please explain the principles of contravariance and covariance in this context?

答案1

得分: 1

很难用几句话来解释整个概念,正如你所说,你已经阅读了一些解释,所以我认为我会在很大程度上重复其他地方所说的内容。相反,我将专注于你的示例。

你的函数doSomething()需要另一个函数,将狗“转化”为一个数字:(Dog) -> Number。假设doSomething()是一个文档编制员,创建了一种狗的目录。这个人必须知道每只狗的高度才能将其放入目录中,但他们不知道如何测量狗的高度,所以他们需要一个合格的兽医来帮助。兽医就是我们的(Dog) -> Number函数 - 他们知道如何为给定的狗提供高度。

我们找到了一位非常合格的兽医animalToIntMethod,他知道如何测量世界上的任何动物 - 因此他们的类型是(Animal) -> Int

这两个人的关注点不同。文档编制员需要一个可以专门测量狗的人。兽医不专注于狗,他们可以测量一般的动物,他们的专业领域更广泛。但当然,这并不意味着他们不能合作。兽医可以测量任何动物,包括狗。

这是反变性的一个示例,表示为Function<in T, out R>中的in。文档编制员不需要专门处理狗的人(那将是不变性)。只要提供的兽医可以处理哺乳动物或一般动物 - 只要他们可以处理狗,就可以。

英文:

It is hard to explain the whole concept in a few words and as you said, you already read some explanations, so I think I would pretty much repeat what was said in other places. Instead, I'll focus on your example.

Your function doSomething() requires another function which "converts" a dog into a number: (Dog) -&gt; Number. Let's say doSomething() is a documentalist who creates some kind of a catalogue of dogs. This person has to know the height of each dog to put it into the catalogue, but they don't know how to measure the height of a dog, so they need a qualified veterinarian for this. Veterinarian is our (Dog) -&gt; Number function - they know how to provide a height for a given dog.

We found a veterinarian animalToIntMethod who is super-qualified, they know how to measure any animal in the world - therefore their type is (Animal) -&gt; Int.

The area of focus of both persons is different. Documentalist needs someone, who can measure dogs specifically. Veterinarian doesn't focus on dogs, they can measure animals in general, their area of expertise is much wider. But of course, that doesn't mean they can't cooperate. Veterinarian can measure any animal, including dogs.

This is an example of contravariance, represented as in in the Function&lt;in T, out R&gt;. Documentalist doesn't need someone working with dogs exclusively (that would be invariance). It is fine if the provided veterinarian can work with mammals or animals in general - as long as they can work with dogs.

答案2

得分: 1

逆变性(Contravariance)

狗是doSomething()所请求的函数的输入,因此它是逆变的。随着输入(逆变的)类型变得更具体,函数类型变得不那么具体,反之亦然。

能够接受任何输入的函数也能够接受狗作为输入。因此,(Any)->____也符合(并且是)(Dog)->_____的子类型。

或者可以这样说...doSomething()期望能够通过传递狗来使用此函数,因此传递的函数必须能够接受狗作为输入。能够接受Any作为输入的函数可以接受任何输入,因此它绝对可以接受狗。

协变性(Covariance)

数字是doSomething()所请求的函数的输出,因此它是协变的。随着输出(协变的)类型变得更具体,函数类型也变得更具体,反之亦然。

能够生成整数的函数也是能够生成数字的函数。因此,(___)->Int也符合(并且是)(___)->Number的子类型。

另一种表述方式...doSomething()期望能够使用此函数来生成数字。如果它得到一个生成整数的函数,那么它实际上得到了数字,因此请求得到满足。

不变性(Invariance)

这不适用于函数类型参数。由于函数是一等公民类型,强制输入或输出的不变性将违反 Liskov 替换原则。

英文:

Contravariance

Dog is an input to doSomething()'s requested function, so it is contravariant. As the input (contravariant) type gets more specific, the function type gets less specific, and vice versa.

A function that can accept Any as an input is also a function that can accept Dogs as an input. Therefore (Any)-&gt;____ also qualifies as (and is a subtype of) a (Dog)-&gt;_____.

Or another way to put it...doSomething() is expecting to be able to use this function by passing Dogs to it, so the function passed in must be able to accept Dogs as input. A function that accepts Any as an input can accept any input, so it can definitely accept Dogs.

Covariance

Number is an output of doSomething()'s requested function, so it is covariant. As the output (covariant) type gets more specific, the function type gets more specific, and vice versa.

A function that can produce Ints is also a function that produces Numbers. Therefore (___)-&gt;Int also qualifies as (and is a subtype of) (___)-&gt;Number.

Another way to put it...doSomething() expects to be able to use this function to produce Numbers. If it gets a function that produces Ints, then it is getting Numbers so the request is satisfied.

Invariance

This isn't supported for function type parameters. Since functions are first class types, it would violate the Liskov substitution principle to force invariance for the inputs or outputs.

huangapple
  • 本文由 发表于 2023年6月1日 01:24:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/76375950.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定