Covariance and contravariance (computer science)
Encyclopedia
Within the type system
Type system
A type system associates a type with each computed value. By examining the flow of these values, a type system attempts to ensure or prove that no type errors can occur...

 of a programming language
Programming language
A programming language is an artificial language designed to communicate instructions to a machine, particularly a computer. Programming languages can be used to create programs that control the behavior of a machine and/or to express algorithms precisely....

, covariance and contravariance refers to the ordering of types from narrower to wider and their interchangeability or equivalence in certain situations (such as parameters, generics, and return types).
  • covariant: converting from wider (double) to narrower (float).

  • contravariant: converting from narrower (float) to wider (double).

  • invariant: Not able to convert.


For example, a type that may assume the values {a,b,c,d} is wider than one that may only assume the values {a,b}. Hence, a type conversion from {a,b,c,d}->{a,b}, such as in the case of passing a double to a function expecting a float, is a covariant conversion. Similarly, a type conversion from {a,b}->{a,b,c,d}, such as in the case of calling a function returning a float in the place of one returning a double, is a contravariant conversion of the function (the function type is its result type).

Note, types may share values yet not be equivalent. For example, types assuming values {a,b} and {b,c}, respectively, are not equivalent to each other...though they are equivalent to a type assuming the values {b} since {b}->{a,b} and {b}->{b,c}.

A class's type equivalence is implied by its inheritance hierarchy (and is the justification for inheritance). However, since all possible changes to a derived class can destroy this assertion, some languages restrict the presumption of this implicit equivalence in some situations. For example, in C# 3.0 generic parameters did not support covariance or contravariance; IEnumerable was not assignable to IEnumerable as one might intuit; however, this is now supported in C# 4.0. Standard arrays have had covariance & contravariance since .NET was introduced, although it doesn't statically guarantee that array assignments are valid and an exception may be thrown at runtime.

Formal definition

Within the
type system
Type system
A type system associates a type with each computed value. By examining the flow of these values, a type system attempts to ensure or prove that no type errors can occur...

 of a programming language
Programming language
A programming language is an artificial language designed to communicate instructions to a machine, particularly a computer. Programming languages can be used to create programs that control the behavior of a machine and/or to express algorithms precisely....

, a typing rule or a type conversion operator is:
  • covariant if it preserves the ordering, ≤, of types
    Subtype
    In programming language theory, subtyping or subtype polymorphism is a form of type polymorphism in which a subtype is a datatype that is related to another datatype by some notion of substitutability, meaning that program constructs, typically subroutines or functions, written to operate on...

    , which orders types from more specific to more generic;
  • contravariant if it reverses this ordering, which orders types from more generic to more specific;
  • invariant if neither of these applies.


These terms come from category theory
Category theory
Category theory is an area of study in mathematics that examines in an abstract way the properties of particular mathematical concepts, by formalising them as collections of objects and arrows , where these collections satisfy certain basic conditions...

, which has a general definition of covariance and contravariance that unifies the computer science definition of these terms with the definition used in vector spaces.

This distinction is important in considering argument and return types of methods in class hierarchies
Class hierarchy
As in taxonomy, the classifications of species, a class hierarchy in computer science is a classification of object types, denoting objects as the instantiations of classes inter-relating the various classes by relationships such as "inherits", "extends", "is an abstraction of", "an interface...

. In object-oriented languages such as C++, if class B is a subtype of class A, then all member functions of B must return the same or narrower set of types as A; the return type is said to be covariant. On the other hand, if the member functions of B take the same or broader set of arguments compared with the member functions of A, the argument type is said to be contravariant. The problem for instances of B is how to be perfectly substitutable
Liskov substitution principle
Substitutability is a principle in object-oriented programming. It states that, in a computer program, if S is a subtype of T, then objects of type T may be replaced with objects of type S without altering any of the desirable properties of that program...

 for instances of A. The only way to guarantee type safety and substitutability is to be equally or more liberal than A on inputs, and to be equally or more strict than A on outputs. Note that not all programming languages guarantee both properties in every context, and that some are unnecessarily strict; they are said not to support covariance or contravariance in a given context; the behavior of some programming languages is discussed below.

Typical examples:
  • The operator which constructs array types
    Array data type
    In computer science, an array type is a data type that is meant to describe a collection of elements , each selected by one or more indices that can be computed at run time by the program. Such a collection is usually called an array variable, array value, or simply array...

     from element types is usually covariant on the base type: since StringObject then ArrayOf(String)ArrayOf(Object). Note that this is only correct (i.e. type safe) if the array is immutable; if insert and remove operators are permitted, then the insert operator is covariant (e.g. one can insert a String into an ArrayOf(Object)) and the remove operator is contravariant (e.g. one can remove an Object from an ArrayOf(String)). Since the mutators have conflicting variance, mutable arrays should be invariant on the base type.
  • A call to a function with a parameter of type T (defined as fun f (x : T) : Integer) can be replaced by a call to a function g (defined as fun g (x : S) : Integer) if TS. In other words, if g cares less about the type of its parameter, then it can replace f anywhere, since both return an Integer. So, in a language accepting function arguments, gf and the type of the parameter to f is said to be contravariant.
  • In the general case, the type of the result is covariant.


In object-oriented programming
Object-oriented programming
Object-oriented programming is a programming paradigm using "objects" – data structures consisting of data fields and methods together with their interactions – to design applications and computer programs. Programming techniques may include features such as data abstraction,...

, substitution is also implicitly invoked by overriding
Method overriding (programming)
Method overriding, in object oriented programming, is a language feature that allows a subclass or child class to provide a specific implementation of a method that is already provided by one of its superclasses or parent classes...

methods
Method (computer science)
In object-oriented programming, a method is a subroutine associated with a class. Methods define the behavior to be exhibited by instances of the associated class at program run time...

 in subclasses: the new method can be used where the old method was invoked in the original code. Programming languages vary widely on their allowed forms of overriding, and on the variance of overridden methods' types.

Origin of the terms

The origin of these terms is in category theory
Category (mathematics)
In mathematics, a category is an algebraic structure that comprises "objects" that are linked by "arrows". A category has two basic properties: the ability to compose the arrows associatively and the existence of an identity arrow for each object. A simple example is the category of sets, whose...

, where the types in the type system
Type system
A type system associates a type with each computed value. By examining the flow of these values, a type system attempts to ensure or prove that no type errors can occur...

 form a category C, in which morphisms represent the subtype relationship. The subtype relationship therefore reflects the substitution principle
Liskov substitution principle
Substitutability is a principle in object-oriented programming. It states that, in a computer program, if S is a subtype of T, then objects of type T may be replaced with objects of type S without altering any of the desirable properties of that program...

: any expression of type t can be substituted by an expression of type s if st.

Defining a function that accepts type p and returns type r creates a new type pr in the type system which the new function name is associated with. This function definition operator is actually a functor
Functor
In category theory, a branch of mathematics, a functor is a special type of mapping between categories. Functors can be thought of as homomorphisms between categories, or morphisms when in the category of small categories....

 F : C × CC that creates the said type. From the substitution principle above, this functor must be contravariant in the first argument and covariant in the second.

Need for covariant argument types?

In many strictly-typed languages (with the notable exception of Eiffel
Eiffel (programming language)
Eiffel is an ISO-standardized, object-oriented programming language designed by Bertrand Meyer and Eiffel Software. The design of the language is closely connected with the Eiffel programming method...

, see below), subclassing must allow for substitution. That is, a child class can always stand in for a parent class. This places restrictions on the sorts of relationships that subclassing can represent. In particular, it means that arguments to member functions can only be contravariant and return types can only be covariant, as explained in previous section.

This creates problems in some situations, where argument types should be covariant to model real-life requirements. Suppose you have a class representing a person. A person can see the doctor, so this class might have a method virtual void Person::see(Doctor d). Now suppose you want to make a subclass of the Person class, Child. That is, a Child is a Person. One might then like to make a subclass of Doctor, Pediatrician. If children only visit pediatricians, we would like to enforce that in the type system. However, a naive implementation fails: because a Child is a Person, Child::see(d) must take any Doctor, not just a Pediatrician.

We could try moving the see method to the Doctor class hierarchy, but we would have the same problem: If a Doctor could see a Person and a Child is a Person, then there is still no way to enforce that a Child must see a Pediatrician and that a Person who is not a Child cannot see a Pediatrician and must see another Doctor.

In this case, the visitor pattern
Visitor pattern
In object-oriented programming and software engineering, the visitor design pattern is a way of separating an algorithm from an object structure on which it operates. A practical result of this separation is the ability to add new operations to existing object structures without modifying those...

 could be used to enforce this relationship. Another way to solve the problems, in C++
C++
C++ is a statically typed, free-form, multi-paradigm, compiled, general-purpose programming language. It is regarded as an intermediate-level language, as it comprises a combination of both high-level and low-level language features. It was developed by Bjarne Stroustrup starting in 1979 at Bell...

, is using generic programming (see below).

Avoiding the need for covariant argument types

The need for covariant argument types arises from strategies in object oriented languages for context-sensitive selection of the code used in method calls. This does not include the first parameter of a method call, which is the object itself, which is not contravariant.

Castagna showed that types used for runtime selection of the method are covariant, while types not used for runtime selection of the method are contravariant. In Castagna's work, examples which would suggest the usage of covariance for parameter types are treated with the usage of multiple dispatch
Multiple dispatch
Multiple dispatch or multimethods or function overloading is the feature of some object-oriented programming languages in which a function or method can be dynamically dispatched based on the run time type of more than one of its arguments...

, i.e. overriding where the right method is selected also based on the type of some arguments; applying the rule, covariance is allowed for those argument types. However, this solution cannot be applied to most programming languages, since they do not support multiple dispatch
Multiple dispatch
Multiple dispatch or multimethods or function overloading is the feature of some object-oriented programming languages in which a function or method can be dynamically dispatched based on the run time type of more than one of its arguments...

.

Note that for (static) overload resolution, the opposite rule applies: types used for compile-time method selection (i.e. parameter types) are contravariant; types not used to select the method are covariant.

These terms are also used in the context of modern programming languages that offer other functors to create new types with type variables, e.g., generic programming
Generic programming
In a broad definition, generic programming is a style of computer programming in which algorithms are written in terms of to-be-specified-later types that are then instantiated when needed for specific types provided as parameters...

 or parametric polymorphism
Parametric polymorphism
In programming languages and type theory, parametric polymorphism is a way to make a language more expressive, while still maintaining full static type-safety. Using parametric polymorphism, a function or a data type can be written generically so that it can handle values identically without...

, and exception handling
Exception handling
Exception handling is a programming language construct or computer hardware mechanism designed to handle the occurrence of exceptions, special conditions that change the normal flow of program execution....

 where method definitions are enriched with annotations that indicate possible failures.

Overview of covariance/contravariance in some programming languages

Both the subtype
Subtype
In programming language theory, subtyping or subtype polymorphism is a form of type polymorphism in which a subtype is a datatype that is related to another datatype by some notion of substitutability, meaning that program constructs, typically subroutines or functions, written to operate on...

 and method overriding concepts are defined differently between programming languages. They do not necessarily follow the substitution principle above, sometimes adding runtime checking instead. What follows is a simple comparison of how overriding methods behave in some common programming languages.

C++

C++
C++
C++ is a statically typed, free-form, multi-paradigm, compiled, general-purpose programming language. It is regarded as an intermediate-level language, as it comprises a combination of both high-level and low-level language features. It was developed by Bjarne Stroustrup starting in 1979 at Bell...

 supports covariant return type
Return type
In computer programming, the return type defines and constrains the data type of value returned from a method or function...

s in overridden virtual function
Virtual function
In object-oriented programming, a virtual function or virtual method is a function or method whose behaviour can be overridden within an inheriting class by a function with the same signature...

s. Adding the covariant return type
Covariant return type
In object-oriented programming, a covariant return type of a method is one that can be replaced by a "narrower" type when the method is overridden in a subclass....

 was the first modification of the C++ language approved by the standards committee in 1998. See

Arrays in C# and Java

In the above discussion we have shown that type safety requires invariance of array types. However, arrays of reference types are covariant in both languages, and this leads to lack of static type safety: for instance, in C# string[] is a subtype of object[], and in Java [] is a subtype of [], although with some caveats. For instance, in C#, we have:


// a is a single-element array of System.String
string[] a = new string[1];

// b is an array of System.Object
object[] b = a;

// Assign an integer to b. This would be possible if b really were
// an array of objects, but since it really is an array of strings,
// we will get an ArrayTypeMismatchException with the following message:
// "Attempted to store an element of the incorrect type into the array".
b[0] = 1;


The same problem exists in Java, too:

// a is a single-element array of String
String[] a = new String[1];

// b is an array of Object
Object[] b = a;

// Assign an Integer to b. This would be possible if b really were
// an array of Object, but since it really is an array of String,
// we will get a java.lang.ArrayStoreException.
b[0] = 1;


Note: In the above cases you can read from b without problem. It is only when trying to write to the array that you must know its real type.

C#

In C# it is possible to store an object which is an instance of an equal or smaller type in that storage location.


object str = "string";


Ever since C# 1.0, arrays where the element type is a reference type are covariant.


object[] strings = new string[1];


Method group to delegate conversions are covariant in return types, and contravariant in argument types.

Generic delegate types are always invariant in C# 3.0

A variant interface which inherits from another variant interface must do so in a manner which does not introduce problems in the type system

C# 4.0 allows declaration of covariance and contravariance on parameterized interface and delegate types

D

The D Programming Language
D (programming language)
The D programming language is an object-oriented, imperative, multi-paradigm, system programming language created by Walter Bright of Digital Mars. It originated as a re-engineering of C++, but even though it is mainly influenced by that language, it is not a variant of C++...

 supports covariance for method overriding:

interface IFactory {
Object Create;
}

class X { }

class XFactory : IFactory {
// This method implements IFactory.Create
X Create {
return new X;
}
}

Java

Return type covariance is implemented in the Java
Java (programming language)
Java is a programming language originally developed by James Gosling at Sun Microsystems and released in 1995 as a core component of Sun Microsystems' Java platform. The language derives much of its syntax from C and C++ but has a simpler object model and fewer low-level facilities...

 programming language version J2SE 5.0
Java Platform, Standard Edition
Java Platform, Standard Edition or Java SE is a widely used platform for programming in the Java language. It is the Java Platform used to deploy portable applications for general use...

. Parameter types have to be exactly the same (invariant) for method overriding
Method overriding (programming)
Method overriding, in object oriented programming, is a language feature that allows a subclass or child class to provide a specific implementation of a method that is already provided by one of its superclasses or parent classes...

, otherwise the method is overloaded
Type polymorphism
In computer science, polymorphism is a programming language feature that allows values of different data types to be handled using a uniform interface. The concept of parametric polymorphism applies to both data types and functions...

 with a parallel definition instead.

Generics
Generics in Java
Generics are a facility of generic programming that was added to the Java programming language in 2004 as part of J2SE 5.0. They allow "a type or method to operate on objects of various types while providing compile-time type safety." A common use of this feature is when using a Java Collection...

 were introduced in Java in Java 5.0 to allow type-safe generic programming. Unlike arrays, generic classes are neither covariant nor contravariant. For example, neither List nor List is a subtype of the other:


// a is a single-element List of String
List a = new ArrayList;
a.add("foo");

// b is a List of Object
List b = a; // This is a compile-time error


However, generic type parameters can contain wildcards (a shortcut for an extra type parameter that is only used once). Example: Given a requirement for a method which operates on Lists, of any object, then the only operations that can be performed on the object are those for which the type relationships can be guaranteed to be safe.

// a is a single-element List of String
List a = new ArrayList;
a.add("foo");

// b is a List of anything
List b = a;

// retrieve the first element
Object c = b.get(0);
// This is legal, because we can guarantee
// that the return type "?" is a subtype of Object

// Add an Integer to b.
b.add(new Integer (1));
// This is a compile-time error;
// we cannot guarantee that Integer is
// a subtype of the parameter type "?"


Wildcards can also be bound, e.g. "? extends Foo" or "? super Foo" for upper and lower bounds, respectively. This allows to refine permitted performance. Example: given a List, then an element can be retrieved and safely assigned to a Foo type (covariance). Given a List, then a Foo object can be safely added as an element (contravariance).

Eiffel

Eiffel
Eiffel (programming language)
Eiffel is an ISO-standardized, object-oriented programming language designed by Bertrand Meyer and Eiffel Software. The design of the language is closely connected with the Eiffel programming method...

 allows covariant return and parameter types in overriding methods. This is possible because Eiffel does not require subclasses to be substitutable for superclasses — that is, subclasses are not necessarily subtypes.

However, this can lead to surprises if subclasses with such covariant parameter types are operated upon presuming they were a more general class (polymorphism), leading to the possibility of compiler
Compiler
A compiler is a computer program that transforms source code written in a programming language into another computer language...

 errors.

Sather

Sather supports both covariance and contravariance. Calling convention for overridden methods are covariant with out arguments and return values, and contravariant with normal arguments (with the mode in).

Scala

The Scala programming language
Scala programming language
Scala is a multi-paradigm programming language designed to integrate features of object-oriented programming and functional programming. The name Scala is a portmanteau of "scalable" and "language", signifying that it is designed to grow with the demands of its users...

 supports declaration-site variance annotations.

Scala originally supported use-site declarations of covariance and contravariance, but it turned out to be difficult to achieve consistency of usage-site type annotations, so that type errors were not uncommon. Because declaration-site annotations provide excellent guidance on which methods should be generalized with lower bounds, Scala later adopted them. Though usage-site variance annotations allow a single class to have covariant as well as non-variant fragments,
Scala's mixin
Mixin
In object-oriented programming languages, a mixin is a class that provides a certain functionality to be inherited or just reused by a subclass, while not meant for instantiation , Mixins are synonymous functionally with abstract base classes...

composition makes it relatively easy to explicitly factor classes into covariant and non-variant fragments.

External links

The source of this article is wikipedia, the free encyclopedia.  The text of this article is licensed under the GFDL.
 
x
OK