Я не могу понять определение Википедии "Аппликативный функтор"

Изучая функторы, аппликативные функторы и монады в 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)