Почему я должен использовать аппликативные функторы в функциональном программировании? - PullRequest
71 голосов
/ 04 июля 2011

Я новичок в Haskell и читаю о функторах и аппликативных функторах.Хорошо, я понимаю функторы и то, как я могу их использовать, но я не понимаю, почему аппликативные функторы полезны и как я могу использовать их в Haskell.Можете ли вы объяснить мне на простом примере, почему мне нужны аппликативные функторы?

Ответы [ 7 ]

51 голосов
/ 04 июля 2011

Аппликативные функторы - это конструкция, которая обеспечивает среднюю точку между функторами и монадами и поэтому более распространена, чем монады, и при этом более полезна, чем функторы.Обычно вы можете просто сопоставить функцию с функтором.Аппликативные функторы позволяют вам брать «нормальную» функцию (принимая нефункциональные аргументы) и использовать ее для работы с несколькими значениями в контексте функтора.Как следствие, это дает вам эффективное программирование без монад.

Хорошее, самодостаточное объяснение, содержащее примеры, можно найти здесь .Вы также можете прочитать практический пример синтаксического анализа , разработанный Брайаном О'Салливаном, который не требует предварительных знаний.

34 голосов
/ 07 июля 2011

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

Когда они полезны? Типичным примером является синтаксический анализ, когда вам нужно выполнить ряд действий, которые читают части структуры данных по порядку, а затем склеивают все результаты вместе. Это похоже на общую форму композиции функций:

f a b c d

, где вы можете думать о a, b и т. Д. Как о произвольных действиях, которые нужно выполнить, и f как о функторе, применяемом к результату.

f <$> a <*> b <*> c <*> d

Мне нравится думать о них как о перегруженном «пробеле». Или, что обычные функции Хаскеля находятся в аппликативном функторе тождества.

См. « Применимое программирование с эффектами »

12 голосов
/ 04 июля 2011

У Конора МакБрайда и Росса Патерсона Функциональная Жемчужина в стиле есть несколько хороших примеров.Это также ответственно за популяризацию стиля в первую очередь.Они используют термин «идиома» для «аппликативного функтора», но в остальном это довольно понятно.

8 голосов
/ 17 октября 2011

Трудно придумать примеры, когда вам нужны аппликативные функторы.Я могу понять, почему программист на промежуточном языке Haskell сам задает им этот вопрос, поскольку большинство вводных текстов представляют экземпляры, полученные из монад, использующих Applicative Functors, только в качестве удобного интерфейса.

Ключевое понимание, как упомянуто здесь и в большинстве введенийк предмету, является то, что Аппликативные Функторы находятся между Функторами и Монадами (даже между Функторами и Стрелками).Все монады являются аппликативными функторами, но не все функторы аппликативны.

Поэтому обязательно, иногда мы можем использовать аппликативные комбинаторы для чего-то, для чего мы не можем использовать монадические комбинаторы.Одной из таких вещей является ZipList (см. Также этот вопрос SO для некоторых деталей ), который является просто оболочкой для списков, чтобы иметь другой экземпляр Applicative чем тот, который получен из экземпляра списка Monad.В соответствующей документации используется следующая строка, чтобы дать интуитивное представление о том, для чего ZipList:

f <$> ZipList xs1 <*> ... <*> ZipList xsn = ZipList (zipWithn f xs1 ... xsn)

Как указано здесь , можно сделать изворотливую монадуэкземпляры, которые почти работают для ZipList.

Существуют и другие Аппликативные Функторы, которые не являются Монадами (см. этот ТАК вопрос), и их легко найти.Иметь альтернативный интерфейс для монад хорошо и все, но иногда создание монады неэффективно, сложно или даже невозможно, и тогда вам нужно * * * * * * * * * * * * * * * * * * 10 * .Отказ от ответственности 1031 *: Создание Аппликативных Функторов также может быть неэффективным, сложным и невозможным, если вы сомневаетесь, обратитесь к местному теоретику категории для правильного использования Аппликативных Функторов.

7 голосов
/ 27 декабря 2016

По моему опыту, аппликативные функторы хороши по следующим причинам:

Определенные виды структур данных допускают мощные типы композиций, но в действительности не могут быть сделаны монадами.Фактически, большинство абстракций в функционально-реактивном программировании попадают в эту категорию.Хотя технически мы можем сделать, например, Behavior (он же Signal) монадой, обычно это невозможно сделать эффективно.Аппликативные функторы позволяют нам создавать мощные композиции, не жертвуя эффективностью (правда, иногда сложнее использовать аппликатив, чем монаду, просто потому, что у вас не так много структуры для работы).

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

userData = User <$> field "Name" <*> field "Address"

, и вы можете написать движок, который будет проходить, чтобы найти все используемые поля и отобразить их в форме, а затем, когда вы получитеданные возвращаются снова, чтобы получить построенный User.Это нельзя сделать ни с помощью простого функтора (потому что он объединяет две формы в одну), ни с монадой, потому что с помощью монады вы можете выразить:

userData = do
    name <- field "Name"
    address <- field $ name ++ "'s address"
    return (User name address)

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

Еще одна приятная вещь в аппликативных функторах - это то, что они составляют.Точнее говоря, функтор композиции:

newtype Compose f g x = Compose (f (g x))

является аппликативным, когда f и g.То же самое нельзя сказать о монадах, которые создают целую историю трансформации монад, которая усложняется некоторыми неприятными способами.Аппликаты очень чистые, и это означает, что вы можете создать структуру нужного вам типа, сосредоточившись на небольших компонуемых компонентах.

Недавно в GHC появилось расширение ApplicativeDo, которое позволяет вам использоватьdo нотация с аппликативами, облегчающая некоторые нотационные сложности, если вы не делаете никаких монадических вещей.

6 голосов
/ 04 июля 2011

Один хороший пример: аппликативный разбор.

См. [Реальный мир haskell] ch16 http://book.realworldhaskell.org/read/using-parsec.html#id652517

Это код синтаксического анализатора с нотацией do:

-- file: ch16/FormApp.hs
p_hex :: CharParser () Char
p_hex = do
  char '%'
  a <- hexDigit
  b <- hexDigit
  let ((d, _):_) = readHex [a,b]
  return . toEnum $ d

Использование функтора делает его намного короче :

-- file: ch16/FormApp.hs
a_hex = hexify <$> (char '%' *> hexDigit) <*> hexDigit
    where hexify a b = toEnum . fst . head . readHex $ [a,b]

«Подъем» может скрыть основные детали некоторого повторяющегося кода. тогда вы можете просто использовать меньше слов, чтобы рассказать точную и точную историю.

3 голосов
/ 29 февраля 2012

Я бы также посоветовал взглянуть на это

В конце статьи приведен пример

import Control.Applicative
hasCommentA blogComments =
BlogComment <$> lookup "title" blogComments
            <*> lookup "user" blogComments
            <*> lookup "comment" blogComments

Что иллюстрирует некоторые особенности стиля прикладного программирования.

...