Подтвердить что ты не робот

Что означает "аппликативный"?

Когда я читал материал в Haskell, я иногда сталкивался с прилагательным "аппликативный", но мне не удалось найти достаточно четкое определение этого прилагательного (в отличие от, скажем, класса Haskell Applicative). Я хотел бы научиться распознавать фрагмент кода/алгоритма/структуры данных и т.д., Который является "аппликативным", точно так же, как я могу распознать "рекурсивный". Некоторые сравнительные примеры "аппликативного" и того, что намеревается сделать для этого различия, (которое, я надеюсь, является чем-то более значимым по своему праву, чем "неприменительное" ), было бы очень оценено.

Изменить: например, почему было выбрано слово "аппликативный", чтобы назвать класс, а не какое-то другое имя? Что это за класс, который делает имя Applicative настолько хорошим для него (даже ценой его неясности)?

Спасибо!

4b9b3361

Ответ 1

Неясно, к чему "аппликативный" используется для обозначения, не зная контекста.

Если это действительно не относится к аппликативным функторам (т.е. Applicative), то это, вероятно, относится к самой форме приложения: f a b c является аппликативной формой, и именно здесь аппликативные функторы получают свое имя от: f <$> a <*> b <*> c аналогично. (Действительно, скобки идиомы принимают это соединение дальше, позволяя вам записать его как (| f a b c |).)

Аналогичным образом, "аппликативные языки" могут быть противопоставлены языкам, которые в основном не основаны на применении функции к аргументу (обычно в форме префикса); например, конкатенационные ( "основанные на стеке" ) языки не применяются.

Чтобы ответить на вопрос о том, почему аппликативные функторы называются то, что они в глубине, я рекомендую читать Аппликационное программирование с эффектами; основная идея состоит в том, что многие ситуации требуют чего-то вроде "расширенного приложения": применение чистых функций в каком-то эффектном контексте. Сравните эти определения map и mapM:

map :: (a -> b) -> [a] -> [b]
map _ [] = []
map f (x:xs) = f x : map f xs

mapM :: (Monad m) => (a -> m b) -> [a] -> m [b]
mapM _ [] = return []
mapM f (x:xs) = do
  x' <- f x
  xs' <- mapM f xs
  return (x' : xs')

с mapA (обычно называемый traverse):

mapA :: (Applicative f) => (a -> f b) -> [a] -> f [b]
mapA _ [] = pure []
mapA f (x:xs) = (:) <$> f x <*> mapA f xs

Как вы можете видеть, mapA гораздо более кратким и, более очевидно, связано с map (тем более, если вы используете префиксную форму (:) в map тоже). В самом деле, использование апликативного функторного обозначения, даже если у вас есть полный Monad, распространен в Haskell, так как он часто намного более ясен.

Также полезно посмотреть на определение:

class (Functor f) => Applicative f where
  pure :: a -> f a
  (<*>) :: f (a -> b) -> f a -> f b

Сравните тип (<*>) с типом приложения: ($) :: (a -> b) -> a -> b. Какие предложения Applicative представляют собой обобщенную "отмененную" форму приложения, а ее код написан в аппликативном стиле.

Более формально, как упоминалось в статье и обозначено ertes, Applicative является обобщением комбайнов ; pure является обобщением K :: a -> (r -> a) (aka const), а (<*>) является обобщением S :: (r -> a -> b) -> (r -> a) -> (r -> b). Часть r -> a просто обобщается на f a; исходные типы получаются с экземпляром Applicative для ((->) r).

Как практический вопрос, pure также позволяет вам писать аппликативные выражения более равномерным образом: pure f <*> effectful <*> pure x <*> effectful в отличие от (\a b -> f a x b) <$> effectful <*> effectful.

Ответ 2

На более фундаментальном уровне можно сказать, что "аппликативный" означает работу в какой-либо форме СК-исчисления. Это также относится к классу Applicative. Он дает вам комбинаторы pure (обобщение K) и <*> (обобщение S).

Ваш код применим, когда он выражается в таком стиле. Например, код

liftA2 (+) sin cos

является аппликативным выражением

\x -> sin x + cos x

Конечно, в Haskell класс Applicative является основной конструкцией для программирования в аппликативном стиле, но даже в монадическом или стрекозном контексте вы можете написать применительно:

return (+) `ap` sin `ap` cos

arr (uncurry (+)) . (sin &&& cos)

Является ли последний фрагмент кода применимым, однако, противоречиво, потому что можно утверждать, что аппликативный стиль требует каррирования, чтобы иметь смысл.