
Covariance and Contravariance - marvel_boy
https://mikeash.com/pyblog/friday-qa-2015-11-20-covariance-and-contravariance.html
======
rectang
A fascinating limitation of Go is its lack of support for functions with
covariant return types.

Say that you want a function which returns a Reader:

    
    
        type ReaderMaker func() io.Reader
    

A function which returns a concrete type which implements Reader cannot be
substituted. This makes sense because interface types are always a two-word
tuple, and a concrete type could be a one-word pointer, a big struct, etc.

    
    
        var invalid ReaderMaker = func() *io.PipeReader { return &io.PipeReader{} }
    

More subtly, a function which returns a different but compatible _interface_
type also won't work.

    
    
        // This assignment is invalid because although anything that implements
        // ReadCloser also implements Reader, a ReadCloser itable won't necessarily
        // have Read() at the same offset as a Reader itable.
        var invalid ReaderMaker = func() io.ReadCloser { return &io.PipeReader{} }
    

[http://play.golang.org/p/5PPpVZhap_](http://play.golang.org/p/5PPpVZhap_)

* edit: fixed swapped types in comment

~~~
DonaldFisk
I haven't learned GoLang, but what I want to say here is quite general.

I regard that (class names ending in "er") as an antipattern, and have seen
them again and again in Java programs. I call them "thingers". Except in
simulation programs, objects should just sit there passively and have things
done to them by methods. For example, rather than a parser class you should
have a parse method, which belongs to a grammar class, and takes a list of
tokens as its argument, and returns a parse tree.

Here, it's simpler just to have a method/function/procedure called read()
which can be an method belonging to whatever classes of object you want to
read from. And if you want to make something, just call new rather than have a
factory class.

I'm not the only one who doesn't like thingers: see [http://steve-
yegge.blogspot.co.uk/2006/03/execution-in-kingd...](http://steve-
yegge.blogspot.co.uk/2006/03/execution-in-kingdom-of-nouns.html)

Now, before you argue that I don't grok object orientation, I'll tell you that
I've added object orientation to Prolog, and to a Lisp dialect (twice). (I've
also got several years of OO application programming, in Java and Lisp, behind
me.) Perhaps you could call me an OO skeptic though - I can accept that.

~~~
wlievens
Readers in Java are character-based streams. How would you implement a
composite i/o stream model then, if Readers are bad-smell thingies? In Java.
Adding buffering or counting or throttling to a stream is rather easy using
this pattern; is there some other fundamentally sound way of doing that?

~~~
DonaldFisk
You've answered your own question. In Java, what's called a reader is nothing
of the sort. It's really a stream (i.e. a thing, rather than a thinger), and I
would have called it that, but programmers are now lumbered with it being
called a Reader.

~~~
wlievens
So your answer is to search your vocabulary for a proper term that isn't a
"nounified-verb"?

I guess I do that anyway, since it feels cleaner, though just as often there
isn't one, or it's already taken (like [Input|Output]Stream in my example),
and I can't say I really feel that bad about nounifying a verb.

In fact, when you nounify a verb (in other words: turn a function into an
object) you can add so much to it. For instance, you could track how long it
takes to execute, you can replace some of its constituent components (e.g. GOF
Strategy Pattern), you could tune parameters that aren't part of its
computational input (something like buffer size, or enabling caching, etc)...
these are all things that would otherwise have to be communicated through a
lengthy list of parameters, and turning them into object state frees up your
actual method signature for the parameters that really matter.

------
vilhelm_s
My favorite article on this topic is Edward Z. Yang's "Tomatoes are a subtype
of vegetables", which illustrates function contravariance with very charming
drawings.

[http://blog.ezyang.com/2014/11/tomatoes-are-a-subtype-of-
veg...](http://blog.ezyang.com/2014/11/tomatoes-are-a-subtype-of-vegetables/)

~~~
jacquesm
Technically (botanically) Tomatoes are actually a subtype of fruits. (they
contain seeds)

~~~
wavefunction
In every sense tomatoes are fruit, not just technically.

~~~
DonaldFisk
No, just botanically.

You can't make jam from tomatoes. Or at least it wouldn't be called jam. You
_can_ make jam from rhubarb, so in some sense that is a fruit.

Similarly, you can make soup out of tomatoes, but not out of rhubarb.

So, from the point of view of a cook, tomatoes can be considered vegetables
and rhubarb fruit.

~~~
maxerickson
There are recipes out there for rhubarb soup and tomato jam (nothing fancy,
just search on either term).

~~~
DonaldFisk
There's also strawberry soup ([http://www.food.com/recipe/strawberry-
soup-122037](http://www.food.com/recipe/strawberry-soup-122037)) and carrot
jam ([http://www.foodandwine.com/recipes/carrot-
jam](http://www.foodandwine.com/recipes/carrot-jam)).

I think we need fuzzy classes. :-)

------
bhrgunatha
Dan Grossman of the University of Washington delivered a Programming Languages
course on Coursera.

Thanks to his lecture [1], I'll never forget "Function subtyping is
contravariant in its argument types and covariant in its return type" \- even
though I have to think to remember whether the subtype is the contravariant or
covariant, I remember the principle very very clearly due to his antics.

[1]
[https://www.youtube.com/watch?v=pb_k8h6RuAY](https://www.youtube.com/watch?v=pb_k8h6RuAY)

~~~
john_butts
That's a great course, and actually a perfect conceptual base for Swift in a
lot of ways.

------
j2kun
Covariance and contravariance also have meanings in mathematics, for which
this seems like a specialization though I don't have a concrete connection.

In math you often have a class of objects and functions on those objects (say,
groups with group homomorphisms, or simpler, types in a programming language
with functions that take one type to another). Then in math one often learns
about an object by associating another object with it. For example, a
topological space can be associated with a specific kind of group that tells
you about the space's structure.

Then you can ask how this "association" behaves with the functions on the
object. I'll use notation now, and say that for an object X the "associated"
object is G(X). Often G carries over to functions, so that if f is a function
X -> Y then G(f) is a function G(X) to G(Y). Such an "association" is called
covariant because it preserves the ordering of domain -> codomain. The
"association" is instead called contravariant if it reverses the order. E.g.,
f: X -> Y becomes G(f): G(Y) -> G(X). Of course, I'm using "association" but
the real word for G is functor.

~~~
jessriedel
Interestingly, in physics covariance and contravariance of vectors are
basically on equal footing. This is pretty different the OP usage for which
covariance and contravariance denote either toward or away from the root of a
tree (the object hierarchy).

Like you, I'd be interested to know whether there's a deeper connection in the
terminology.

~~~
tel
As others have said, you can recover the notation exactly through category
theory

~~~
jessriedel
Could you link me? Ctrl-F-ing for 'vector' didn't help.

~~~
tel
My understanding, though I've never personally made it explicit, is that you
view "contra/covariance" as arising not as a property of vector spaces exactly
(concretely) but instead from tensors-as-multilinear-maps and you talk about
them as functors between transformation groups on vector spaces which either
preserve (co-) or reverse (contra-) transformations.

The inner product lets you view vector spaces as spaces of transformations
already so the same sort of idea comes out now viewing vector spaces as maps
in their own right.

------
solutionyogi
Eric Lippert did a great series on covariance and contravariance and how they
are implemented in C# and various design decisions which went in to it. I
would highly recommend it if you use C#.

[http://blogs.msdn.com/b/ericlippert/archive/tags/covariance+...](http://blogs.msdn.com/b/ericlippert/archive/tags/covariance+and+contravariance/)

------
cefstat
There is something in the article that looks wrong. It's suggesting that `let
returnsCat: () -> Cat = animalF` should work, where `animalF` returns
`Animal`. The Swift playground agrees with my understanding that this is not
correct: "Cannot convert value of type '() -> Animal' to specified type '() ->
Cat'".

~~~
gclaramunt
I think is just a matter of editing, the line "It even works with functions:"
should probably say something like "It's even the same with functions" as
before clearly stated that "let cat: Cat = Animal()" doens't work

~~~
mikeash
"Editing"? You overestimate me. Anyway, thank you both for pointing this out,
I've improved (I hope) the wording now.

------
draw_down
I remember reading about this issue from my C# days. But, does all this stuff
actually help write better programs?

I now work in a language that doesn't really support building "types" in this
way and I can't say I miss thinking about this sort of thing nor consider its
lack to be a problem. Types in this sense strike me as more of a boondoggle of
overhead that gets in the way more than helps achieve the goal of whatever I'm
writing.

~~~
mikeash
That's a good question. I don't know the answer.

What language are you working in now? Some really don't support variance (Go,
apparently, and C would be an obvious example) but many others support it but
just don't enforce it (Python and all the other dynamically-typed duck-typed
languages).

------
JackFr
No shout out to Scala, which handles this all quite nicely? _> sad face<_

~~~
mikeash
I don't know much Scala, and my blog is mostly focused on the Apple world.

That said, I always love seeing other perspectives, so a comment on the
article describing how Scala gets it right would be welcome.

~~~
chengl
here you go. Variance explained by Martin Odersky in his coursera course
[https://class.coursera.org/progfun-005/lecture/83](https://class.coursera.org/progfun-005/lecture/83)
(you need to login to watch it). Or the Scala doc [http://docs.scala-
lang.org/tutorials/tour/variances.html](http://docs.scala-
lang.org/tutorials/tour/variances.html)

------
Paradigma11
Relevant lecture videos:
[https://channel9.msdn.com/Shows/Going+Deep/C9-Lectures-
Brian...](https://channel9.msdn.com/Shows/Going+Deep/C9-Lectures-Brian-
Beckman-Covariance-and-Contravariance-in-Physics-1-of-1)
[https://channel9.msdn.com/shows/Going+Deep/E2E-Brian-
Beckman...](https://channel9.msdn.com/shows/Going+Deep/E2E-Brian-Beckman-and-
Erik-Meijer-CoContravariance-in-Physics-and-Programming-1-of-2/)

------
TeMPOraL
Nice article, though I _think_ you're mixing up subtypes and supertypes. Or I
am. After reading that I no longer know if "Cat extends Animal" means that Cat
is a subtype of Animal, or that it's a supertype of Animal.

E.g.

    
    
      let f1: A -> B = ...
      let f2: C -> D = f1
    
      (...)
      Applied here, the above code works if A is a subtype of C, and if B is a supertype of D.
      Put concretely:
    
      let f1: Animal -> Animal = ...
      let f2: Cat -> Thing = f1
    

So now Animal is a subtype of a Cat, and a supertype of Thing?

But I finally understand what covariance and contravariance means, so thanks!

\--

Also, related, could somebody more proficient in English than I am help me
understand which direction does the substitution go here?

"It says, in short, that an instance of a subclass can always be substituted
for an instance of its superclass."

Is it "an instance of subclass can replace an instance of superclass", or "it
can be replaced by"? Or is it context-dependent?

I ask because I'm not sure about the grammar aspect, and in my native language
(Polish) we have a few ambiguities like that, which I sometimes jokingly write
down with an arrow, e.g. "A can be ---substitutes for--> B" or "A can be
<\--substituted for-- B", to make it damn explicit which direction the word
works.

~~~
bonobo3000
I think the example is right. Cat is a subtype of Animal, and Animal is a
subtype of Thing

Cat < Animal < Thing

So if the function takes any animal, of course it can take a cat (so its
covariant in argument). And because all animals are things, we can say the
output type of f is Thing.. its just less accurate than Animal but still
correct - that is the contravariance in the result.

Cat extends Animal means "Cat can do everything Animal can, plus maybe more
stuff". So all cats are animals, but not all animals are cats. So Cat is a
subtype of Animal.

For the second question,

"an instance of subclass can replace an instance of superclass" is the right
one.

Really it makes a lot of sense intuitively. e.g say Int is a subclass of
Number (a simpler way to read is just "Int is a Number") . then anything you
can do with numbers, you can do with ints too so we can put an int in place of
a number.

~~~
TeMPOraL
My first question is about terminology used. I get why the code example works,
but the description I quoted says the reverse (i.e. "Animal is a subtype of
Cat", and "Animal is a supertype of Thing"). I assume that it's an error then.

Thx for the answer to the second question :). I _think_ my mind put an
additional "by" in that sentence and got confused.

~~~
jeremyjh
Yes I think he has made a mistake there. He did get it right earlier in the
article he says "This makes Cat a subtype of Animal. That means that all Cats
are Animals, but not all Animals are Cats."

~~~
maxerickson
The text describing the relationships between A,B,C,D makes a lot more sense
to me if the example is switched to

    
    
        Thing -> Cat

------
foolfoolz
I didn't expect this:

"This is a key rule: function return values can changed to subtypes, moving
down the hierarchy, whereas function parameters can be changed to supertypes,
moving up the hierarchy."

The part allowing a super type in an overridden methods parameters stood out.
I tried this out in scala and it did not compile. Intuitively it doesn't make
sense to me to allow wider types.

~~~
Moto7451
If you think about it it's intuitive. Take these type chains:

Thing -> Animal -> Dog Thing -> Animal -> Cat Thing -> Instrument -> Tuba

Lets say Thing has the following properties: \- IsVisible \- Mass \- CanMove

Lets say Tube has the following properties: \- PlayNote

And lets say that Cat has: \- Meow

Lets take the following contrived example for overriding:

    
    
      Class Debugger {
       void debug(Cat cat); 
      }
    
      Class ThingDebugger : Debugger {
       void debug(Thing thing);
      }
    

Going "wider" to Thing works because Cats, Dogs, and Tuba all have the
properties that a Thing has, because they _are_ instances of Thing. Going
"narrower" works for the same reason:

    
    
      Class Wrangler {
       Animal wrangle(); 
      }
    
      Class DogWrangler : Wrangler {
       Dog wrangle();
      }
    

A Dog can do anything an Animal can do because it is an Animal.

The type system is doing its best to save you from the following:

    
    
      Class Janky {
        void ThisIsOK();
        Animal PrepareForCrash();
      }
    
      Class AlsoJanky : Janky {
        void ThisIsOK();
        Instrument PrepareForCrash();
      }
    
      Class StillJanky : Janky {
        void ThisIsOK();
        Thing PrepareForCrash();
      }
    

In this case any mixing of PrepareForCrash will leave you with returned types
that don't completely share an interface. If the compiler let you do that,
you'd probably do this at some point start mixing the subtypes of Janky and
calls to PrepareForCrash would return Tubas when you expect Cats and then all
hell would break loose. Anyone who's used scripting languages should be
familiar with something like this happening at one point or another.

~~~
foolfoolz
I still don't get ThingDebugger.debug()

first you can't call super() in that method without a downcast. how does that
count as overriding the method?

it's a big problem if you have another subclass of Debugger that does not
change the parameter type. you now have 2 subclassed of Debugger, one has a
method debug(cat: Cat). the other has a method debug(cat: Thing). that accepts
cats, dogs, and tubas. these subtypes of Debugger are not safely
interchangeable.

this does not compile in scala, I'm surprised swift would allow it.

------
darthdeus
And here I was, thinking this would be about statistics.

~~~
amelius
And I thought this was about tensor analysis :)

[https://en.wikipedia.org/wiki/Covariance_and_contravariance_...](https://en.wikipedia.org/wiki/Covariance_and_contravariance_of_vectors)

~~~
ttflee
And it was generalised in category theory:

> The use of both terms in the modern context of multilinear algebra is a
> specific example of corresponding notions in category theory.

------
anoopelias
Another link on the topic. This picture[1] I lifted from scala course in
coursera.org

[1][http://stackoverflow.com/questions/2723397/java-generics-
wha...](http://stackoverflow.com/questions/2723397/java-generics-what-is-
pecs/19739576#19739576)

------
genug
A contravariant object transforms like a vector.

A covariant object transforms like the gradient of a scalar.

~~~
Macha
I understand these words, but not in the combination used.

I guess this is really simple to people with the nessecary maths background to
understand this, but for me I just don't see the connection between
vectors/gradients and type systems.

~~~
genug
I don't think there is any connection. But since the submission was titled
"covariant and contravariant" without any kind of context, it seemed
appropriate that we could all share our own personal ideas about what these
words were supposed to mean.

~~~
Macha
Ok. the source of the confusion for me then was that I was not previously
aware that covariant and contravariant were terms that could be applied to
vectors.

------
marvel_boy
It's amazing how well Mike Ash explains complex concepts !

