> A piece of code that accepts a reference to some type A and only ever reads from it can correctly accept a reference to a subtype of A.
The same is true of a piece of code that writes through the reference or returns it. That's how sub-typing works.
> A piece of code that accepts a reference to a type A and only ever writes to it can correctly accept a reference to a supertype of A.
Have you ever programmed in a language with subtyping? Let me show you an example from Java (a popular object oriented programming language).
class Parent {}
class Child {
int x;
}
class Example {
static void writeToChild(Child c) {
c.x = 20;
}
static void main() {
writeToChild(new Parent());
}
}
This code snippet doesn't compile, but suppose the compiler allowed us to do so, do you think it could work? No. The function writeToChild cannot accept a reference to the supertype even though it only writes through the reference.
I've seen a lot of people in this comment section talking about read and write which I find really odd. They have nothing to do with variance. The contravariant property is a property of function values and their parameters. It is entirely unrelated to the body of the function. A language without higher order functions will actually never have a contravariant type within it. This is why many popular OOP languages do not have them.