Что такое определение Applicative Functor из теории категорий POV?

Мне удалось отобразить определение Функтора из теории категорий на определение Хаскелла следующим образом: поскольку объекты из Hask являются типами, функтор F

  • сопоставляет каждый тип a Hask с новым типом F a, грубо говоря, добавив к нему "F".
  • отображает каждый морфизм a -> b of Hask на новый морфизм F a -> F b с помощью fmap :: (a -> b) -> (f a -> f b).

До сих пор так хорошо. Теперь я добираюсь до Applicative и не могу найти упоминания о таком понятии в учебниках. Посмотрев, что он добавляет к Functor, ap :: f (a -> b) -> f a -> f b, я попытался найти свое собственное определение.

Во-первых, я заметил, что поскольку (->) также является типом, его морфизмы Hask тоже являются его объектами. В свете этого я сделал предложение, что аппликативный функтор является функтором, который также может отображать "стрелки" -объекты категории источников в морфизмы адресата.

Это правильная интуиция? Можете ли вы дать более формальное и строгое определение?

Ответ 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 каждый функтор имеет каноническую силу по отношению к произведению, поэтому это свойство ничего не добавляет к определению.

Теперь, если вы знакомы с определением монады как моноида в категории эндофенторов, вам может быть интересно узнать, что аппликации аналогичны моноидам в категории эндофунторов, где тензорным продуктом является День свертка. Но это гораздо труднее объяснить.

Ответ 2

Вы правы, Applicative переводит менее прямо, чем Functor или Monad. Но по существу это класс моноидальных функторов:

class Functor f => Monoidal f where
  pureUnit :: f ()
  fzip :: f a -> f b -> f (a,b)

Из этого вы можете определить – в Hask

pure x = fmap (const x) pureUnit

и

fs <*> xs = fmap (uncurry ($)) $ fzip fs xs