Что именно представляют собой категории, которые сопоставляются аппликативными функторами?
Я читал Аппликативные функторы, и у меня возникли трудности с согласованием несоответствия в соответствующих терминологиях теория категорий и функциональное программирование.
Хотя я просматривал различные блоги, наиболее полные ресурсы, которые я использовал для этого исследования, следующие:
В теории категорий functor является морфизмом из source в категорию target (в категории категорий). "Категория категорий" содержит набор объектов, который содержит как исходную, так и целевую категории и набор функторов, который содержит: функтор идентичности источника; функтор идентичности целевой категории; и функтор, соединяющий источник с мишенью (если категория источника совпадает с целевой категорией, а рассматриваемый функтор является тождеством, то нужен только один функтор).
В функциональном программировании аппликативные функторы описываются как пара операций:
-
pure : a -> f a
-
<*> : f ( a -> b) -> f a -> f b
.
Здесь мой вопрос
В какой интерпретации разъясняется соответствие между определением функционального программирования аппликативного функтора и категориальным определением функтора?
Более конкретно, какие части кортежа (pure,<*>)
соответствуют:
- категория источников
- целевая категория
- категория, в которой живет функтор.
- функтор эффект сохранения структуры на объектах категории источников
- эффект сохранения структуры функтора на морфизмах категории источников
Примечания: Я признаю, что это может быть неполная метафора, и не может быть однозначного соответствия для каждого из понятий, о которых я говорил. Я намеренно воздержался от того, чтобы поделиться своими предположениями о кажущихся соответствиях здесь, чтобы сохранить мой вопрос простым и избежать дальнейшего смешения вопросов.
Ответы
Ответ 1
Перефразируя этот ответ: Аппликативные функторы являются функторами, для которых также существует естественное преобразование, которое сохраняет моноидальную структуру их исходных/целевых категорий. В случае Haskell Applicative
endofunctors (поскольку их исходная и целевая категории Hask), моноидальная структура является декартовым произведением. Таким образом, для функтора Applicative
существуют естественные преобразования φ: (f a, f b) -> f (a, b)
и ι: () -> f ()
. Таким образом, мы могли бы определить Applicative
как
class Functor f => Applicative' f where
φ :: (f a, f b) -> f (a, b)
ι :: f () -- it could be \() -> f (),
-- but \() -> ... is redundant
Это определение эквивалентно стандартным. Мы можем выразить
φ = uncurry (liftA2 (,))
= \(x, y) -> (,) <$> x <*> y
ι = pure ()
и наоборот
pure x = fmap (\() -> x) ι
f <*> x = fmap (uncurry ($)) (φ (f, x))
So pure
и <*>
- альтернативный способ определения этого естественного преобразования.
Ответ 2
Вероятно, проще сначала рассмотреть класс Functor
(который является суперклассом Applicative
). Applicative
соответствует "слабый моноидальный функтор", как первая работа, на которую вы ссылались. Определение Functor
:
class Functor f where
fmap :: (a -> b) -> f a -> f b
Экземпляр Functor
является конструктором типа (вида * -> *
). Пример: Maybe
.
В категории, о которой мы говорим, относится категория Hask, которая имеет типы Haskell как объекты и (мономорфные) функции Haskell как морфизмы. Каждый экземпляр Functor
(и Applicative
, Monad
и т.д.) Является эндофункцией в этой категории, т.е. Функтором из категории в себя.
Две карты функтора - типы Haskell-типа-Haskell и функции Haskell-Haskell-функции - это конструктор типов f
и функция fmap
.
Например, Int
и Maybe Int
- оба объекта в Hask; Maybe
отображает от первого к последнему. Если chr :: Int -> Char
, то fmap
отображает его на fmap chr :: Maybe Int -> Maybe Char
. Законы Functor
соответствуют категориальным функторным законам.
В случае Applicative
, Functor
является суперклассом, поэтому все, что я только что сказал, применимо. В этом конкретном случае вы можете реализовать fmap
с помощью pure
и <*>
- liftA f x = pure f <*> x
- так что две части функтора, которые вы искали, это f
и liftA
. (Но учтите, что другие формулировки Applicative
не позволяют вам этого делать - в общем, вы полагаетесь на суперкласс класса Functor
.)
Я не совсем понимаю, что вы здесь подразумеваете под "категорией, в которой живет функтор".
Ответ 3
Как вы задали свой вопрос, предположите, что вы ищете категорию категорий, морфизмы которых можно понимать как функторы Haskell. Тогда это были бы виды. В категории есть один объект, который *, представляющий категорию типов хэскелей. Поскольку существует только один объект, должен быть только один набор морфизмов, который является конструктором типа (вида * → *). Не каждый конструктор типов является функтором, но каждый функтор является конструктором типа. Поэтому в этом смысле его можно понимать как морфизм от * до *.
Точкой вида является, конечно, отслеживание количества аргументов в конструкторе типа. Понять это как категорию как-то искусственно, так как у него есть только один объект.
Кроме того, для объекта *
нет никакого точного морфизма. Вы могли бы думать о Identity :: * -> *
, но это не тождество в строгом смысле (это зависит от естественного изоморфизма, хотя у вас есть Identity :: forall a . a -> Identity a
и runIdentity :: forall a . Identity a -> a
). То же самое касается композиции: вам всегда нужно использовать явный изоморфизм для работы с составленными функторами (Compose
/getCompose
).