Covarianza e controvarianza (informatica)
# Covarianza e controvarianza (informatica)

In informatica, covarianza e controvarianza sono proprietà che caratterizzano alcuni operatori sui tipi. Un operatore è covariante se conserva la relazione di sottotipo, controvariante se la inverte. Prendono il nome dalle omonime proprietà dei funtori in teoria delle categorie.

## Definizione

Un operatore F è covariante se F<T'> ≤ F<T> quando T' ≤ T. Analogamente, F è controvariante se F<T> ≤ F<T'> quando T' ≤ T. Un operatore è detto invariante se non è né covariante né controvariante.

Varianza e controvarianza sono caratteristiche degli operatori sui tipi che hanno uno o più parametri di tipo e producono un nuovo tipo. Non devono essere confuse con l'ereditarietà dei tipi in sé (es. non ha senso dire che un tipo è covariante o controvariante) o con la compatibilità tra tipi nell'assegnamento (es. in molti linguaggi a oggetti, un oggetto di un sottotipo è sempre assegnabile ad una variabile di un supertipo).

## Esempi

### Covarianza

Tipicamente, operatori che possono solo leggere oggetti del parametro di tipo possono essere covarianti. Ad esempio in C#, IEnumerable è covariante e un contenitore enumerabile di un sottotipo (es. IEnumerable<string>) può essere assegnato ad una variabile il cui tipo è un contenitore enumerabile di un suo supertipo (es. IEnumerable<object>). Questo perché i metodi di IEnumerable possono leggere oggetti dal contenitore ma non scriverli, quindi non c'è rischio di compiere operazioni non typesafe.

### Controvarianza

Analogamente, operatori che possono solo scrivere oggetti del parametro possono essere controvarianti. Ad esempio in C#, template come Action<> sono controvarianti, in quanto un'azione definita ad esempio su un oggetto può essere eseguita su una stringa, ma non viceversa.

### Array e contenitori generici

Contenitori generici i cui elementi possono essere modificati sono intrinsecamente nonvarianti, e nei linguaggi in cui sono considerati covarianti questo può permettere operazioni non-typesafe che possono generare errori a runtime.

### Funzioni

L'operatore funzione è tipicamente controvariante negli argomenti e covariante nei valori di ritorno, ovvero è typesafe sostituire una funzione $S_1$ -> $T_1$ con un'altra $S_2$ -> $T_2$ (ovvero $S_2$ -> $T_2$ ≤ $S_1$ -> $T_1)$ se essa accetta argomenti di un supertipo $(S_1$ ≤ $S_2)$ e restituisce valori di un sottotipo $(T_2$ ≤ $T_1).$ Usando le regole di inferenza, abbiamo:

   $S_{2}\leq S_{1}\quad T_{1}\leq T_{2} \over S_{1}\rightarrow T_{1}\leq S_{2}\rightarrow T_{2}$

Tale regola è stata formalizzata da John C. Reynolds e resa popolare da un articolo di Luca Cardelli.

Alcuni linguaggi fanno eccezione, ad esempio in Eiffel la specializzazione dei metodi è covariante negli argomenti. Questo crea quello che nella comunità Eiffel è noto come "catcall problem", che viola il principio di sostituzione di Liskov e rompe la sicurezza rispetto ai tipi del linguaggio, ma viene considerata dagli autori una caratteristica utile per tradurre in codice i requisiti di un progetto, e sono state proposte e implementate varie tecniche di analisi statica per porre rimedio al problema.

