Почему Либо получает Шоу, а может и нет?
Документация обоих Either
а также Maybe
указывают, что у них есть случаи Show
,
Either
определяется как производный Show
просто:
data Either a b = Left a | Right b
deriving (Eq, Ord, Read, Show, Typeable)
Еще, Maybe
не:
data Maybe a = Nothing | Just a
deriving (Eq, Ord)
Так как они являются частью base
и так похожи, почему не Maybe
напрямую выводить Show
?
Другой вопрос также может быть, откуда он Show
пример?
2 ответа
Экземпляр для Maybe
определяется явно в GHC.Show
наряду с примерами для целого ряда других распространенных типов, таких как кортежи. Вы можете узнать, где был определен экземпляр, используя :i
команда в ghci
:
Prelude> :i Maybe
data Maybe a = Nothing | Just a -- Defined in ‘Data.Maybe’
instance Eq a => Eq (Maybe a) -- Defined in ‘Data.Maybe’
instance Monad Maybe -- Defined in ‘Data.Maybe’
instance Functor Maybe -- Defined in ‘Data.Maybe’
instance Ord a => Ord (Maybe a) -- Defined in ‘Data.Maybe’
instance Read a => Read (Maybe a) -- Defined in ‘GHC.Read’
instance Show a => Show (Maybe a) -- Defined in ‘GHC.Show’
Я не знаю, почему они определили экземпляр явно или поместили его в GHC.Show
вместо Data.Maybe
- насколько я могу судить, его можно перенести в Data.Maybe
и / или производные. Я думаю, что они не хотели Data.Maybe
зависеть ни от чего, кроме GHC.Base
(как это происходит сейчас), предположительно потому, что он используется в некоторых других основных модулях.
Кортежи AFAIK нигде не определены, поэтому во избежание появления осиротевших экземпляров [1] экземпляры Show для кортежей должны быть определены в GHC.Show[2]. Реализация этих случаев происходит с использованием foldr1
:
show_tuple :: [ShowS] -> ShowS
show_tuple ss = showChar '('
. foldr1 (\s r -> s . showChar ',' . r) ss
. showChar ')'
поэтому GHC.Show импортирует GHC.List, где определена эта функция. GHC.List, в свою очередь, определяет lookup
, который находится в Maybe
монада (уклон мономорфизма старого доброго старого Haskell 98, я думаю). Таким образом, GHC.List импортирует Data.Maybe. Для того, чтобы определить Show
Например, Data.Maybe необходимо импортировать GHC.Show (прямо или косвенно), что сделает всю последовательность GHC.Show -> GHC.List -> Data.Maybe -> GHC.Show циклической зависимостью. GHC не очень хорошо поддерживает циклические зависимости (не потому, что их легко поддерживать!), Поэтому base работает очень усердно, чтобы их избежать.
[1] Экземпляр-сирота - это экземпляр, определенный в другом модуле, нежели класс и тип, участвующий в экземпляре. Формально, Haskell требует, чтобы поиск экземпляров выполнялся в любом модуле, прямо или косвенно импортированном компилируемым модулем; но для не сиротских случаев GHC может замкнуть это и просто посмотреть в двух местах. Для бесхозных экземпляров он должен отслеживать каждый потерянный экземпляр в модуле, а затем отслеживать, что эти экземпляры повторно открываются каждым модулем, который их импортирует, что является более дорогостоящим (и означает, что он должен поддерживать контекстную среду с потенциально большим количеством экземпляров, которые даже не относятся к текущему модулю, потому что он фактически не импортирует эти классы или типы). Поэтому хорошей практикой является избегать случаев осиротения по этой причине.
Несколько более философски, сиротские экземпляры - действительно хороший способ получить два конфликтующих экземпляра одного и того же класса / типа в вашей программе, так как они оба "видимы" в вашей программе. Main
Модуль означает, что они будут конфликтовать. Так что языковая особенность сама по себе довольно хитрая.
[2] IIRC GHC предоставляет только Show
экземпляры вплоть до (относительно небольшого) фиксированного числа компонентов кортежей, которые не совсем соответствуют Haskell 98, но достаточно хороши для любых практических задач программирования. (Серьезно, в любом случае, не используйте кортежи с более чем 3 элементами, вы забудете, что означают конкретные компоненты). Я не знаю, был ли обновлен стандарт, чтобы привести GHC в соответствие за последние несколько лет или нет.