Ключ к пониманию аппликативных функторов - это выяснить, какую структуру они сохраняют.
Регулярные функторы сохраняют базовую категориальную структуру: они отображают объекты и морфизмы между категориями и сохраняют законы категории (ассоциативность и идентичность).
Но у категории может быть больше структуры. Например, это может позволить определение отображений, которые похожи на морфизмы, но принимают несколько аргументов. Такие сопоставления определяются каррированием: например, функция двух аргументов определяется как функция одного аргумента, возвращающая другую функцию. Это возможно, если вы можете определить объект, представляющий тип функции. В общем, этот объект называется экспонентой (в 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 каждый функтор имеет каноническую силу по отношению к продукту, поэтому это свойство ничего не добавляет к определению.
Теперь, если вы знакомы с определением монады как моноида в категории эндофункторов, вам может быть интересно узнать, что аппликативы аналогичным образом являются моноидами в категории эндофункторов, где тензорным продуктом является дневная свертка. Но это гораздо сложнее объяснить.
person
Bartosz Milewski
schedule
27.01.2016