
With covariant arrays this cannot be guaranteed to be safe, since the backing store might actually be an array of cats. It should always be possible to put a Dog into an Animal. So the contravariant rule is not safe.Ĭonversely, a Cat cannot be treated as an Animal. Clearly, not every Animal can be treated as if it were a Cat, since a client reading from the array will expect a Cat, but an Animal may contain e.g. If we wish to avoid type errors, then only the third choice is safe. invariant: an Animal is not a Cat and a Cat is not an Animal.We have the option to treat this as either: For the purposes of this example, this array supports both reading and writing elements. For the type Animal we can make the type Animal, which is an "array of animals". To illustrate this general phenomenon, consider the array type. Mutable data types which act as both sources and sinks should be invariant. Read-only data types (sources) can be covariant write-only data types (sinks) can be contravariant. For example, an Action represents a first-class function expecting an argument of type T, and a function that can handle any type of animal can always be used instead of one that can only handle cats. The typing rules for interface variance ensure type safety. For example, the delegate type Func represents a function with a contravariant input parameter of type T and a covariant return value of type TResult. Types with more than one type parameter may specify different variances on each type parameter. The above interfaces are declared as IEnumerable, Action, and IList. For each so-marked type parameter, the compiler conclusively verifies, with any violation being fatal, that such use is globally consistent. The variance of a C# generic interface is declared by placing the out (covariant) or in (contravariant) attribute on (zero or more of) its type parameters. Neither IList nor IList is a subtype of the other, because IList is invariant on T.The subtyping is reversed because Action is contravariant on T. The subtyping is preserved because IEnumerable is covariant on T. IEnumerable is a subtype of IEnumerable.The article considers how this applies to some common type constructors.įor example, in C#, if Cat is a subtype of Animal, then: invariant or nonvariant if not variant.variant if covariant, contravariant or bivariant.bivariant if both of these apply (i.e., if A ≤ B, then I ≡ I).contravariant if it reverses this ordering: If A ≤ B, then I ≤ I.covariant if it preserves the ordering of types (≤), which orders types from more specific to more generic: If A ≤ B, then I ≤ I.Within the type system of a programming language, a typing rule or a type constructor is: ( November 2021) ( Learn how and when to remove this template message) Unsourced material may be challenged and removed. Please help improve this article by adding citations to reliable sources. This section needs additional citations for verification. In order to keep the type system simple and allow useful programs, a language may treat a type constructor as invariant even if it would be safe to consider it variant, or treat it as covariant even though that could violate type safety. On the other hand, programmers often find contravariance unintuitive, and accurately tracking variance to avoid runtime type errors can lead to complex typing rules. By making type constructors covariant or contravariant instead of invariant, more programs will be accepted as well-typed. Here the subtyping relation of the simple types is reversed for the complex types.Ī programming language designer will consider variance when devising typing rules for language features such as arrays, inheritance, and generic datatypes. On the other hand, "function from Animal to String" is a subtype of "function from Cat to String" because the function type constructor is contravariant in the parameter type. This means that the subtyping relation of the simple types are preserved for the complex types. In the OCaml programming language, for example, "list of Cat" is a subtype of "list of Animal" because the list type constructor is covariant. For example, how should a list of Cats relate to a list of Animals? Or how should a function that returns Cat relate to a function that returns Animal?ĭepending on the variance of the type constructor, the subtyping relation of the simple types may be either preserved, reversed, or ignored for the respective complex types. Variance refers to how subtyping between more complex types relates to subtyping between their components. For instance, if the type Cat is a subtype of Animal, then an expression of type Cat should be substitutable wherever an expression of type Animal is used. Many programming language type systems support subtyping.
