tl; dr : «Учитывает ли система типов Haskell принцип замещения Лискова?» —— Он не только соблюдает LSP, он применяет it!
Теперь, Haskell s Eq
типовой класс это совершенно другое животное
Да, и вообще типовые классы - это совершенно разные животные (или мета-животные?) от ОО-классов. Принцип замещения Лискова - это подклассы как подтипы . Итак, прежде всего класс должен определить тип, который делают классы OO (даже абстрактные / интерфейсы, только для них значения должны быть в подклассе). Но классы Haskell ничего подобного не делают! Вы не можете иметь «значение класса Eq
». На самом деле у вас есть значение некоторого типа, и этот тип может быть экземпляром класса Eq
. Таким образом, семантика классов полностью отделена от семантики значений.
Давайте сформулируем этот абзац также как параллельное сравнение:
- OO: классы содержат
- значения (более известные как объекты ★ )
- подклассы (значения которых также являются значениями родительского класса)
- Haskell: классы содержат
- типов (известные как экземпляры класса)
- подклассы (чьи экземпляры также являются экземплярами родительского класса)
Обратите внимание, что в описании класса Haskell значения даже не упоминаются. (Фактически, у вас могут быть классы, у которых даже нет методов, связанных с какими-либо значениями времени выполнения!)
Итак, теперь мы установили подклассы в Haskell, не имеющие ничего общего с значений подкласса , ясно, что принцип Лискова даже не может быть сформулирован на этом уровне. Вы могли бы сформулировать что-то подобное для типов:
- Если
D
является подклассом C
, тогда и введите этот экземпляр D
также можно использовать как экземпляр C
- что абсолютно верно, хотя и не обсуждается; действительно, компилятор обеспечивает это. Это влечет за собой
- Для того, чтобы написать
instance Ord T
для вашего типа T
, вы должны сначала также написать instance Eq T
(что, конечно же, было бы так же действительным само по себе, независимо от того, определен ли также экземпляр Ord
) - Если ограничение
Ord a
появляется в сигнатуре функции, функция также может автоматически предполагать, что тип a
имеет действительный экземпляр Eq
тоже.
Это может быть не очень интересный ответ на вопрос Лискова в Haskell.
Вот кое-что, что делает его немного более интересным хотя. Я сказал, что Haskell не имеет подтипов? Что ж, это действительно так! Не обычные старые типы Haskell98, а универсально определенные типы .
НЕ ПАНИ C Я попытаюсь объяснить, что это такое, на примере:
{-# LANGUAGE RankNTypes, UnicodeSyntax #-}
type T = ∀ a . Ord a => a -> a -> Bool
type S = ∀ a . Eq a => a -> a -> Bool
Заявление: S
является подтипом T
.
–Если вы обратили внимание, то вы, вероятно, думаете в этот момент подождите, подождите, подождите, это неправильный путь . В конце концов, Eq
- это супер класс Ord
, а не подкласс.
Но нет, S
- это подтип!
Демонстрация:
x :: S
x a b = a==b
y :: T
y = x
Другой путь невозможен:
y' :: T
y' a b = a>b
x' :: S
x' = y'
error:
• Could not deduce (Ord a) arising from a use of ‘y'’
from the context: Eq a
bound by the type signature for:
x' :: S
Possible fix:
add (Ord a) to the context of
the type signature for:
x' :: S
• In the expression: y'
In an equation for ‘x'’: x' = y'
Правильное объяснение типов Rank-2 / универсальной количественной оценки привело бы здесь слишком далеко, но моя точка зрения: Haskell действительно допускает своего рода подтипирование, и для него принцип подстановки Лискова является простым следствием принудительного компилятора «LSP» для типов в классах типов.
И это довольно мило, если вы спросите меня.
★ Мы не называем значения «объектами» в Haskell; объекты - это что-то другое для нас, поэтому я избегаю термина «объект» в этом посте.