Говоря современным языком, принцип LSP гласит: все параметры всех функций должны быть ковариантными,
Не понятно, к чему тут указана ковариантность. Вариантность относится к тому как комплексные типы выстраиваются в иерархию подтипов. То есть, если у нас есть комплексный тип I, то I[A] <: I[B] если A <: B (ковариантность) или если B <: A (контрвариантность).
то есть ограничивающими дерево типов сверху относительно задекларированного для данного параметра.
То есть, исходя из этого параметры функций должны принимать в качестве аргументов значения, типы которых являются подтипами типа параметра. Обычно этому условию соответствуют статические типизированные языки, в которых есть подтипирование.
Однако в отличие от примера с ООП, уже существующий код, использующий тип Logger и функцию log, не может работать с этим новым типом. Нужно выполнить рефакторинг, и способ рефакторинга пользовательского кода зависит от того, как мы хотим сделать доступным этот новый тип пользователям.
Можно сделать это без рефакторинга, используя композицию:
Зачем тут GADT расширение языка используется?
Очень странная статья.
Не понятно, к чему тут указана ковариантность. Вариантность относится к тому как комплексные типы выстраиваются в иерархию подтипов. То есть, если у нас есть комплексный тип I, то I[A] <: I[B] если A <: B (ковариантность) или если B <: A (контрвариантность).
То есть, исходя из этого параметры функций должны принимать в качестве аргументов значения, типы которых являются подтипами типа параметра. Обычно этому условию соответствуют статические типизированные языки, в которых есть подтипирование.
Можно сделать это без рефакторинга, используя композицию:
Советую посмотреть обсуждение этого здесь.