Я не могу понять определение Википедии "Аппликативный функтор"
Изучая функторы, аппликативные функторы и монады в Haskell, я нашел это определение на Wikipedia:
В функциональном программировании, в частности, Haskell, functor - это структура, которая похожа на монаду (return
, fmap
, join
) без join
или как функтор с return
.
Я не могу понять: мне кажется, что предоставление return
(т.е. pure
) функтору недостаточно для получения аппликативного функтора, потому что вам нужно предоставить ap
(т.е. <*>
) также, которые не могут быть определены только в терминах fmap
и return
. Я пропустил что-то или определение Википедии не совсем правильно?
EDIT 2017-02-08: Я нашел другую полезную информацию по этому вопросу в ответе this.
Ответы
Ответ 1
Неверная статья. Предположим, что мы имеем монаду m
без join
или функтор с return
. Мы можем сразу определить pure
:
pure :: Monad m => a -> m a
pure = return
Мы не можем, однако, определить (<*>)
только с fmap
и return
. Все, что у нас есть, это fmap
, поэтому мы получим m (m a)
, если попытаемся использовать m (a -> b)
. В этой точке нам нужно join
или его эквивалент (>>=)
:
(<*>) :: Monad m => m (a -> b) -> m a -> m b
f <*> x = join (fmap (flip fmap x) f)
-- or, easier to read:
-- f <*> x = do
-- f' <- f
-- x' <- x
-- return f' x'
Аппликативный функтор подобен функтору с return
и ap
, но no join
. Так что да, вы были совершенно правы, Википедия пропустила операцию аппликативного (см. оригинальную бумагу).
Кстати, если вы добавите только pure
, вы получите указанный функтор. typeclassopedia дает лучший обзор Applicative
, чем статья в Википедии.
Ответ 2
Вы правы, аппликативные функции требуют <*>
, а также pure
для минимального определения. Стоит отметить, что мы можем получить fmap
от этих:
fmap f a = pure f <*> a
Аналогично мы можем получить аппликативное определение из монад:
pure = return
f' <*> a' = do
f <- f'
a <- a'
return $ f a
Вы можете посмотреть, как аппликативные функции представляют собой обобщение функторов на многопараметрические функции или как сочетание значений с контекстом по горизонтали:
liftA2 f a b = f <$> a <*> b
fmap :: (a -> b) -> (f a -> f b)
liftA2 :: (a -> b -> c) -> (f a -> f b -> f c)