(noob question) method parameters in co-and-contravariance issue

Is co- and contravariance a concept related to inheritance or can it apply to normal method parameters ?
See definition at c2.com.

e.g. In java

    class Food{}
    class Grass extends Food {}
    class Antelope extends Food {}
    public class Main2
    {
        public static void main(String[] args) {
            Main2.eat( new Food() );
            Main2.eat( new Grass() );
            Main2.swallow( new Food() ); // compile-time error
            Main2.swallow( new Grass() );
        }
        public static void eat( Food f ) {}
        public static void swallow( Grass f ) {}
    }


So I get a compile-time error because the method parameters to swallow is covariant i.e. it only accepts Grass and its subtypes.

Am I correct in my reasoning ? Thanks

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

Not really covariance

As I understand the terms, co- and contra-variance are not applicable to this situation. The compile time error you get here is a simple one. You have only a single implementation of swallow(), and its parameter type is Grass. You passed it something that's not Grass, so you get an error. (Also note that static methods in Java are never inherited.)

The "co" in "covariance" is the four-way relationship among ParentClass : ChildClass :: ParentMethodReturnType : ChildMethodReturnType. As ChildClass is lower on the type lattice than ParentClass, so may the return type of an inherited method be lower in the ChildClass's implementation. "Co" because they vary in the same direction.

Likewise the "contra" in contravariance applies to parameter types: in child classes, parameters of methods may be higher on the type lattice for classes that are lower. The higher/lower here is the "contra"--the direction of sub/super is opposite.

HTH

Not exactly

Is co- and contravariance a concept related to inheritance

Yes, but to see this kind of variance in action in an OO language you need to override a method. But you don't need inheritance, overriding or other OO concepts to deal with or understand co- and contravariance. All you need is subtyping and function types. The symbol <: means "is a subytpe of". Also for any A, A <: A, so it doesn't mean "strictly a subtype of".

If you have a function type F1
F1=A1 -> B1

Meaning functions of type F1 take an A1 as a parameter and return a B1

And another function type
F2=A2 -> B2

Then F2 <: F1 iff A1 <: A2 and B2 <: B1

That's because an F2 can only be used where an F1 is used if it accepts at least all the possible parameters that an F1 takes and it returns something within the range of what an F1 promises.

Functions types that have two parameters, one of type X, and one of type Z can be represented as
F=X -> Y -> Z

Which is the same as

F=X -> (Y -> Z)

Arbitrarily many parameters can be represented the same way by adding more arrows.

So, if you have
F1=A1 -> B1 -> C1

and

F2=A2 -> B2 -> C2

Then, by the previous definition

F2 <: F1 iff A1 <: A2 and (B2 -> C2) <: (B1 -> C1)
so F2 <: F1 iff A1 <: A2 and B1 <: B2 and C2 <: C1

The same pattern repeats as you add more parameters.

From this you conclude that parameters to functions are contravariant (their subtyping direction is oppositethe function type's subytping direction) and results from functions are covariant (their subtyping the same as the function's subtyping direction).

From a co- and contravariance perspective, OO overriding is a bit of a fancy way to create a function with a type that is a subtype of type of the function being overridden.

There are other aspects of co- and contravariance, eg. with effects systems. But this should get you started.

[Edit: It took me about 4 tries to get all my formatting and subtyping right. Apologies to anybody who saw this before I was done.]

Consider the case of a generic method

Yes, but to see this kind of variance in action in an OO language you need to override a method.

So this applies in java because we can think of methods as function types: something that takes one or more types and return a new type.


public static <T> void add( T t, Collection<T> c )
{
    c.add( t );
}

Now consider this generic method. The 2nd parameter is contravariant and I don't need overriding.

Thanks

Nope

So this applies in java because we can think of methods as function types: something that takes one or more types and return a new type.

No, methods are functions (with dispatch rules), not function types. A function type is the type of a function. Just like Int is the type of 42, a function called + that adds two Ints would have type Int -> Int -> Int. In Java terms, think of function type as being more or less the same thing as method signature but without the method and parameter names.

For more about variance in an OO context, see this paper. It will do a far better job of explaining the concept than I can. Try hard to forget Java when you read it. Java's rules for variance aren't as fully general as they could be. That's okay within the design goals of Java, it's just not as illuminating about your variance questions.