If you take a look at the flow docs on covariant/contravariant fields in interfaces, covariant implies read-only and contravariance implies write-only. However, I don't really understand why. In their docs on variance, they're defined as
Covariance
- Covariance does not accept supertypes.
- Covariance does accept subtypes.
Contravariance
- Contravariance does accept supertypes.
- Contravariance does not accept subtypes.
But that doesn't really map to read-only/write-only in my mind. Could anyone explain more in depth why it's the case?
Covariance and contravariance are terms that refer to the ability to use a more derived type (more specific) or a less derived type (less specific) than originally specified. Generic type parameters support covariance and contravariance to provide greater flexibility in assigning and using generic types.
This enables you to assign to delegates not only methods that have matching signatures, but also methods that return more derived types (covariance) or that accept parameters that have less derived types (contravariance) than that specified by the delegate type.
Covariance permits a method to have return type that is more derived than that defined in the delegate. Contravariance permits a method that has parameter types that are less derived than those in the delegate type.
Covariance can be translated as "different in the same direction," or with-different, whereas contravariance means "different in the opposite direction," or against-different. Covariant and contravariant types are not the same, but there is a correlation between them. The names imply the direction of the correlation.
I'm not familiar with the syntax of the language, so this answer is in pseudo-code.
Imagine we have three types, Siamese < Cat < Animal
, and define an interface
interface CatCage {
cat: Cat
}
and write some methods
get_cat_in_cage (CatCage c) -> Cat {
c.cat
}
put_cat_in_cage (Cat c, CatCage cage) {
cage.cat = c
}
If we make the field covariant, we can define an instance like
SiameseCage < CatCage {
cat : Siamese
}
But if we do
put_cat_in_cage (aCat, aSiameseCage)
What is the value of aSiameseCage.cat
in this instance? SiameseCage
thinks it ought to be a Siamese
, but we've just been able to make it a Cat
- clearly, the field can't be writeable on the interface and be covariant at the same time.
If we make the field contravariant, we can define an instance like
AnimalCage < CatCage {
cat : Animal
}
But now we can't do
get_cat_in_cage (anAnimalCage)
As the value of anAnimalCage.cat
isn't guaranteed to be a Cat
. So the field can't be readable on the interface if it's contravariant.
You could potentially make it readable on the interface by returning an Object
, or whatever the base type is, but that probably wouldn't have any real use case, so the language is sensible in deciding against it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With