Согласно принципу Лисков, экземпляр подтипа должен вести себя так же, как экземпляр супертипа. И вот у нас Keyboard - подтип Device, мы делаем какой-нибудь List<Device> (как бы супертип) и List<Keyboard> (как бы подтип).
Но на самом деле нет. Если мы рассмотрим полную сигнатуру функции «добавить» с учётом динамической типизации, то оказывается, что попытка вставить Mouse в List<Keyboard> имеет определённое поведение, отличающееся от такового для List<Device>. В первом случае будет ошибка времени выполнения (некорректный тип), а во втором мышь вставится в список.
Т.е. здесь нет ковариантности.
А теперь вопрос: что используется в ковариантности в языках, позволяющих скомпилировать код, вставляющий «нечто» (any,variant,t) в такой вот «псевдоковариантный» контейнер.
Мне кажется, должен быть какой-то костыль для этой ситуации, и я подозреваю, что он может присутствовать в Scala и/или в C#. Кто может что-нибудь сказать?