I think the an easy way to think about it is by thinking about function types, instead of collection types.
Func Arg Val represents a function type from Args to Vals
consider foo(func : Func a b)
The argument to the function foo must be assignable to (a subtype of) Func a b
What are those subtypes? We can find out by considering expressions involving the argument
bidentifer = func(avalue)
so func must be an object whose type is compatible with assignments from the avalue, so its argument type must be a supertype of a
while the assignment to bidentifier implies the return value must be assignable to the return type so it must be a subtype
therefore the subtypes of Func a b are the set of types Func (super a) (sub b)
this is the origin of the phrase be generous in what you accept and specific about what you return
thinking about whether bikes are vehicles and such doesn't really clarify anything. you have to actually think about the expressions you are trying to construct
Moving down the Func type hierarchy moves you up the Argument type hierarchy (contra) but moves you down the Return type hierarchy (co)
Does anyone else dislike this terminology? I'm curious if there is any link at all to tensor analysis and co-variant and contra-variant vectors. (I found the terminology confusing there too)
https://en.wikipedia.org/wiki/Covariance_and_contravariance_...
The terms are mathematical terms and are applicable to both sciences. Covarient and Contravarient describe the relationship between entities in both cases here.
To speak generally, if two objects have a co-variant relationship, then when one goes up the other goes up as well.
If two objects have a contra-varient relationship, when one goes up, the other goes down.
I'm being a bit reductive, but that's how I understand it.
I may be biased since I come from a math background; but what is a clearer termonology?
Covariance is when things vary in the same way; and contravariance is when they vary in opposite ways. This isn't even random math jargon; co/contra, vary, and ance are all standard english words/affixes being used for their normal meaning.
In my experience, what is difficult is understanding contravariant relationships; but that is not a problem of terminology.
Yes, you might be right, this might be completely clear, and I just find it confusing for some reason but from the example in the OP I have no idea the point it is trying to get across with these terms, e.g:
"""
This is covariance in action:
Every bike is a vehicle. (But not every vehicle is a bike.)
Every bike factory is a vehicle factory. (But not every vehicle factory is a bike factory.)
"""
As far as for tensor stuff, I think what is confusing to me at least, is that the components of standard vectors are contravariant, while their dual covectors are covariant. contravariant tensors raise indices and covariants lower them.
Anyway I'm glad this is clear to you but I find it confusing.
Although I wonder now if there is a mapping from the OO diagrams in the OP with Penrose Tensor notations where everything will just make sense....
The terminology is borrowed from category theory. The partially ordered set of types can be seen as a category where the points are the types and the arrows are the is-a relation.
In this setting, a covariant/contravariant type constructor is a covariant/contravariant endofunctor.
Ok thank you for this comment. I think maybe the clearer thing about categories (if I understand this right) is that the focus for covariance/contravariance is simply on the direction of the arrow. And yeah looking at the OP if you just look at the ascii diagram and the arrows it does actually seem clearer to me than the written description which seems completely confusing.
but this terminology is still confusing (to me at least) and something i mentioned on another comment regarding covectors: "Contravariant functors are also occasionally called cofunctors" (https://en.wikipedia.org/wiki/Functor#Covariance_and_contrav...)
I guess maybe the what's confusing is that yes, it's clear these are opposites, but it's not always clear which is the original and which is dual.
Yeah, it's definitely about the "direction". I also agree with you that the OP is yet another "monads are burritos" kind of tutorial. Unfortunately it's a difficult concept to grasp and everyone has to think through it by themself in order to "get" it.
Func Arg Val represents a function type from Args to Vals
consider foo(func : Func a b)
The argument to the function foo must be assignable to (a subtype of) Func a b
What are those subtypes? We can find out by considering expressions involving the argument
bidentifer = func(avalue)
so func must be an object whose type is compatible with assignments from the avalue, so its argument type must be a supertype of a
while the assignment to bidentifier implies the return value must be assignable to the return type so it must be a subtype
therefore the subtypes of Func a b are the set of types Func (super a) (sub b)
this is the origin of the phrase be generous in what you accept and specific about what you return
thinking about whether bikes are vehicles and such doesn't really clarify anything. you have to actually think about the expressions you are trying to construct
Moving down the Func type hierarchy moves you up the Argument type hierarchy (contra) but moves you down the Return type hierarchy (co)