Подпись последовательности
sequence :: Monad m => t (m a) -> m (t a)
Но мы можем реализовать это как
sequence = traverse id
требуя, чтобы m
было просто Applicative
. Если монады являются аппликативными, то зачем заморачиваться этим ограничением на уровне типа?
Более конкретно: он был определен до того, как Applicative
стало известно, а позже был сохранен, потому что, когда Applicative
был добавлен к base
, он еще не был надклассом Monad
.
По какой-то причине некоторые функции в прошлом были обобщены до foldables/traversables, когда они были введены, но предыдущие функции, связанные с монадами (ap
, sequence
, ...), сохраняли свой тип даже при введении аппликативов. Такие изменения не являются обратно совместимыми, это правда, но другие обобщения также не являются обратно совместимыми, поэтому я не уверен в исторической причине. (Haskell сильно изменился: например, в прошлом Monad
даже не был подклассом Functor
)
@chi Если []
был дан экземпляр Foldable
, когда foldr
был обобщен до (a -> b -> b) -> b -> t a -> b
, это не совсем несовместимое изменение, не так ли? Когда был введен Applicative
, монады не сразу стали аппликативами. Теперь, когда Applicative
является является надклассом Monad
, sequence
, вероятно, можно было бы перепечатать. (Тем не менее, какой угловой случай мне не хватает?)
@chepner Это все равно несовместимо, в общем случае, к сожалению. Например, если class C a where foo :: a
с instance C [a] where foo = []
, то length foo
компилируется, если length
заставляет [a]
, но если length
требует только складной код, код неоднозначен. (В GHCi это работает только из-за ExtendedDefaultRules
) Это надуманный пример, но подобные неясности могут возникать в реальном коде.
Я считаю, но не могу найти ссылку прямо сейчас, что есть определенные (квази-надуманные? полностью надуманные?) примеры, где sequence
может быть быстрее, чем sequenceA
. Если кто-нибудь сможет найти это объяснение (может быть, благодаря Эдварду Кметту?), Я думаю, это будет отличный ответ.
В Haskell есть много функций, которые эквивалентны, но различны, потому что Applicative
(соответственно Functor
) не был суперклассом Monad
. Например:
return
против pure
ap
против <*>
liftM
против liftA
против fmap
liftM2
, liftM3
и т. д. по сравнению с liftA2
, liftA3
и т. д.
mapM
/forM
против traverse
/for
mapM_
/forM_
против traverse_
/for_
sequence
против sequenceA
mzero
и mplus
(из MonadPlus
) против empty
и <|>
(из Alternative
)
Старые функции с их оригинальными Monad
сигнатурами все еще присутствуют, но в новом коде, поскольку был реализован Аппликативно-монадное предложение (AMP), вы всегда можете использовать версии Applicative
, потому что они немного более общие, то есть вы всегда можете заменить return
на pure
, но не наоборот.
Не могли бы вы уточнить «причины совместимости»? В частности, что сломается, если сегодня sequence
изменить, например, на подпись типа sequenceA
?
@JosephSible: Вы знаете, я могу ошибаться — ничто должен не сломается только из-за изменения подписи, и на самом деле может быть план сделать это в будущем. Это были другие изменения, которые не вошли в AMP, такие как перемещение join
внутрь Monad
и удаление return
, iirc из-за проблем с ролевой системой.
Исторические причины. Для современного хаскеллиста есть
sequenceA
.