Потребность в чистом в аппликативах

19

Я изучаю Притчи Хаскеля. Мне кажется (я, вероятно, ошибаюсь), что pureфункция на самом деле не нужна, например:

pure (+) <*> [1,2,3] <*> [3,4,5]

можно записать как

(+) <$> [1,2,3] <*> [3,4,5]

Может кто-нибудь объяснить преимущество, которое pureдает функция по сравнению с явным отображением fmap?

Джил Шафрири
источник
1
Вы правы - pure f <*> xэто точно так же, как fmap f x. Я уверен, что есть некоторые причины, по которым он pureбыл включен Applicative, но я не совсем уверен, почему.
Bradrn
4
У меня нет времени на ответ, и я не уверен, что это все равно будет хорошим или полным, но одно наблюдение: pureпозволяет использовать, ну, «чистые» значения в вычислениях Applicative. Хотя, как вы правильно заметили, pure f <*> xэто то же самое, что f <$> x, например , не существует такого эквивалента f <*> x <*> pure y <*> z. (По крайней мере, я так не думаю.)
Робин Зигмонд
3
В качестве другого, более теоретического обоснования - существует альтернативная формулировка, которая тесно связана с важным Monoidклассом - в которой pureсоответствует Monoidэлемент идентичности России. (Это говорит о том, что Applicativeбез pureможет быть интересным, поскольку Semigroup- то есть Monoidбез необходимости иметь идентичность - все еще используется. На самом деле, теперь я думаю об этом, мне кажется, я вспоминаю, что PureScript имеет именно такой pureкласс «Applicative без », хотя я не не знаю, для чего он используется.)
Робин Зигмонд
2
@RobinZigmond fmap (\f' x' z' -> f' x' y z') f <*> x <*> z, я думаю. Идея есть в Applicativeдокументации как закон "обмена".
HTNW
3
@RobinZigmond Applicativeбез pureсуществует как Applyиз полугрупповых .
дупло

Ответы:

8

Я здесь на грани своей компетенции, так что не принимайте это больше, чем есть, но это было слишком долго для комментариев.

Могут быть практические причины для включения pureв класс типов, но многие абстракции Haskell основаны на теоретических основах, и я считаю, что это также относится и к Applicative. Как сказано в документации, это сильный слабый моноидальный функтор (подробности см. На https://cstheory.stackexchange.com/q/12412/56098 ). Я полагаю, что это pureслужит идентичностью , так же как returnи для Monad(который является моноидом в категории эндофункторов ).

Рассмотрим pureи liftA2:

pure :: a -> f a
liftA2 :: (a -> b -> c) -> f a -> f b -> f c

Если вы немного покоситесь, вы сможете представить, что liftA2это бинарная операция, о которой также говорится в документации:

Поднимите бинарную функцию к действиям.

pureто есть соответствующая идентичность.

Марк Симанн
источник
6
Точно. Applicativeбез pureбыл бы, хм, полугрупповой функтор вместо моноидального.
осталось около
20

fmapне всегда сокращает это. В частности, pureэто то , что позволяет вам представить f(где fесть Applicative), когда у вас его еще нет. Хороший пример

sequence :: Applicative f => [f a] -> f [a]

Он берет список «действий», производящих значения, и превращает его в действие, создающее список значений. Что происходит, когда в списке нет действий? Единственный вменяемый результат - это действие, которое не дает никаких значений:

sequence [] = pure [] -- no way to express this with an fmap
-- for completeness
sequence ((:) x xs) = (:) <$> x <*> sequence xs

Если бы у вас этого не было pure, вам пришлось бы требовать непустого списка действий. Вы могли бы определенно заставить это работать, но это все равно, что говорить о сложении без упоминания 0 или умножения без 1 (как говорили другие, потому что Applicatives моноидальны). Вы будете неоднократно сталкиваться с крайними случаями, которые легко разрешаются, pureно вместо этого должны решаться странными ограничениями на ваши входные данные и другие вспомогательные средства.

HTNW
источник