Просмотрел три статьи про вариантность, во всех неточности или ошибки:
https://ru.wikipedia.org/wiki/Ковариантность_и_контравариантность_(программир...
Статья неверна в принципе, начиная с определений:
Ковариа?нтность и контравариа?нтность[1] в программировании — способы переноса наследования типов на производные[2] от них типы — контейнеры, обобщённые типы, делегаты и т. п.
Ковариантностью называется сохранение иерархии наследования исходных типов в производных типах в том же порядке.
Если мы просто передаём параметр, List<Cat> там, где от нас ждут List<T extends Animal>, то мы можем подставить List<Cat> «вместо» List<Animal>, но при этом List<Cat> не становится потомком List<Animal>. Т.е., иерархии никакой нет, а есть заменимость типов в конкретном контексте.
Далее, если мы просто пишем Animal *А = new Cat(), то я бы это тоже рассмотрел как ковариантность, хотя здесь нет никаких производных иерархий. Точно так же, здесь есть подстановка одного вместо другого, я бы назвал это «тривиальным» или «единичным» случаем вариантности. Хотя, конечно, о терминах не спорят - их просто принимают. Но если мы говорим о натуральных числах, то логичнее включить в определение единицу, а не начинать их с двойки.
http://habibutsu.github.io/variance.html - для С++ ошибка в том, что говорится «нужно было С», или «нужно было А», в то время как правильно будет «нужно было B или C» или «нужно было B или А»
http://boyarincev.net/2014/09/covariance-and-contrvariance-in-c-sharp/
В контравариантности путаница (неправильно объяснено).
И т.д.
Вопрос: где же найти правильную статью про ковариантность, но без теорката (из моих трёх статей одна - с теоркатом, но её это не спасло от фактических ошибок). И заодно, предлагаю своё определение:
**********************
Вариантность - это возможность использовать подтип Пт вместо супертипа Ст или наоборот, в ряде частных случаев:
- в место, типизированное как Ст, можно записать Пт
- в место, типизированное как функция(Пт), можно записать функцию типа функция(Ст). В место, типизированное как функция() — Ст, можно записать функцию типа функция() — Пт
- если типизированный контейнер Конт<T> только читается из места, типизированного как Конт<Ст>, то можно записать в него Конт<Пт> (в C# и в Java такая возможность замены требует особого указания в сигнатуре места). Если в него только пишется, то в место, типизированное как Конт<Пт>, можно записать Конт<Пт>
- если виртуальная функция принимает Пт, то её можно перекрыть в потомке так, что она будет принимать Ст. Если ф-я возвращает Ст, то её можно перекрыть, чтобы она возвращала Пт.
- если запись з1 отличается от записи з2 только тем, что поле з1.П1 - типа Пт, а з2.П1 - типа Ст, то з1 - подтип з2.
Т.е., получается, что отношение между типами зависит не только от самих типов или от их родственности (Конт<Т> могут быть друг другу не родственны или родственны странным образом через Object), но и от контекста применения
Инвариантность - это просто отсутствие ко- или контра-вариантности, т.е. отсутствие предмета обсуждения как такового.
**********************
Прошу проверить правильность моего определения и сказать, все ли случаи применения вариантности в реальных ЯП я перечислил. Про Хаскель пока не спрашиваю, т.к. до функторов ещё не дочитал :) Но можете и про него написать :)
А вот предыдущая тема про это: Ковариантность и динамическая типизация