Ответ 1
Ключом к пониманию аппликативных функторов является определение структуры, которую они сохраняют.
Регулярные функторы сохраняют основную категориальную структуру: они сопоставляют объекты и морфизмы между категориями и сохраняют законы категории (ассоциативность и идентичность).
Но категория может иметь больше структуры. Например, это может позволить определение сопоставлений, которые похожи на морфизмы, но принимают несколько аргументов. Такие отображения определяются каррированием: например, функция двух аргументов определяется как функция одного аргумента, возвращающего другую функцию. Это возможно, если вы можете определить объект, представляющий тип функции. В общем, этот объект называется экспоненциальным (в Haskell это просто тип b->c
). Тогда мы можем иметь морфизмы от одного объекта до экспоненты и называть его двупараметрическим морфизмом.
Традиционное определение аппликативного функтора в Haskell основано на идее отображения функций множества аргументов. Но есть эквивалентное определение, которое разбивает функцию с несколькими аргументами вдоль другой границы. Вы можете посмотреть на такую функцию, как отображение продукта (пары в Haskell) на другой тип (здесь c
).
a -> (b -> c) ~ (a, b) -> c
Это позволяет нам рассматривать аппликативные функторы как функторы, сохраняющие произведение. Но продукт - всего лишь один пример того, что называется моноидальной структурой.
В общем случае моноидальная категория - это категория, снабженная тензорным произведением и единичным объектом. В Haskell это может быть, например, декартово произведение (пара) и тип единицы ()
. Обратите внимание, однако, что моноидальные законы (ассоциативность и единичные законы) действительны только до изоморфизма. Например:
(a, ()) ~ a
Тогда аппликативный функтор можно определить как функтор, сохраняющий моноидальную структуру. В частности, он должен сохранять устройство и продукт. Не имеет значения, выполняем ли мы "умножение" до или после применения функтора. Результаты должны быть изоморфны.
Однако нам действительно не нужен полномасштабный моноидальный функтор. Все, что нам нужно, это два морфизма (в отличие от изоморфизмов) - один для умножения и один для единицы. Такой функтор, наполовину сохраняющий моноидальную структуру, называется слабым моноидальным функтором. Следовательно, альтернативное определение:
class Functor f => Monoidal f where
unit :: f ()
(**) :: f a -> f b -> f (a, b)
Легко показать, что Monoidal
эквивалентно Applicative
. Например, мы можем получить pure
из unit
и наоборот:
pure x = fmap (const x) unit
unit = pure ()
Применительные законы следуют просто из сохранения моноидных законов (ассоциативность и единичные законы).
В теории категорий сохранение моноидальной структуры связано с тензорной прочностью, поэтому аппликативный функтор также известен как сильный слабый моноидальный функтор. Однако в Hask каждый функтор имеет каноническую силу по отношению к произведению, поэтому это свойство ничего не добавляет к определению.
Теперь, если вы знакомы с определением монады как моноида в категории эндофенторов, вам может быть интересно узнать, что аппликации аналогичны моноидам в категории эндофунторов, где тензорным продуктом является День свертка. Но это гораздо труднее объяснить.