Класс типов Applicative
представляет слабые моноидальные функторы, которые сохраняют декартову моноидальную структуру в категории типизированных функций.
Другими словами, учитывая канонические изоморфизмы, свидетельствующие о том, что (,)
образует моноидальную структуру:
-- Implementations left to the motivated reader
assoc_fwd :: ((a, b), c) -> (a, (b, c))
assoc_bwd :: (a, (b, c)) -> ((a, b), c)
lunit_fwd :: ((), a) -> a
lunit_bwd :: a -> ((), a)
runit_fwd :: (a, ()) -> a
runit_bwd :: a -> (a, ())
Класс типов и его l aws можно эквивалентно записать так:
class Functor f => Applicative f
where
zip :: (f a, f b) -> f (a, b)
husk :: () -> f ()
-- Laws:
-- assoc_fwd >>> bimap id zip >>> zip
-- =
-- bimap zip id >>> zip >>> fmap assoc_fwd
-- lunit_fwd
-- =
-- bimap husk id >>> zip >>> fmap lunit_fwd
-- runit_fwd
-- =
-- bimap id husk >>> zip >>> fmap runit_fwd
Можно задаться вопросом, что такое функтор, который является oplax моноидальным по отношению та же структура может выглядеть так:
class Functor f => OpApplicative f
where
unzip :: f (a, b) -> (f a, f b)
unhusk :: f () -> ()
-- Laws:
-- assoc_bwd <<< bimap id unzip <<< unzip
-- =
-- bimap unzip id <<< unzip <<< fmap assoc_bwd
-- lunit_bwd
-- =
-- bimap unhusk id <<< unzip <<< fmap lunit_bwd
-- runit_bwd
-- =
-- bimap id unhusk <<< unzip <<< fmap runit_bwd
Если мы подумаем о типах, включенных в определения и l aws, откроется неутешительная истина; OpApplicative
не более конкретно определяет c ограничение, чем Functor
:
instance Functor f => OpApplicative f
where
unzip fab = (fst <$> fab, snd <$> fab)
unhusk = const ()
Однако, хотя каждый функтор Applicative
(на самом деле, любой Functor
) тривиально OpApplicative
, существует не обязательно хорошие отношения между Applicative
слабостями и OpApplicative
прозрачностями. Таким образом, мы можем искать сильные моноидальные функторы с декартовой моноидальной структурой:
class (Applicative f, OpApplicative f) => StrongApplicative f
-- Laws:
-- unhusk . husk = id
-- husk . unhusk = id
-- zip . unzip = id
-- unzip . zip = id
Первый закон выше тривиален, поскольку единственный житель типа () -> ()
является тождественной функцией на ()
.
Однако остальные три l aws и, следовательно, сам подкласс, не тривиальны. В частности, не каждый Applicative
является законным экземпляром этого класса.
Вот некоторые Applicative
функторы, для которых мы можем объявить законные экземпляры StrongApplicative
:
Identity
VoidF
(->) r
Monoid m => (,) m
(см. Ответы) Vec (n :: Nat)
Stream
(бесконечно)
А вот несколько Applicative
с, для которых мы не можем:
[]
Either e
Maybe
NonEmptyList
Схема здесь предполагает, что класс StrongApplicative
в некотором смысле является классом FixedSize
, где «фиксированный размер» * означает, что кратность ** жителей a
у жителей f a
является фиксированной.
Это можно указать как две гипотезы:
- Каждый
Applicative
, представляющий контейнер "фиксированного размера" элементов своего аргумента типа, является экземпляром StrongApplicative
- Экземпляр
StrongApplicative
не существует в котором число вхождений a
может варьироваться
C кто-нибудь думает о контрпримерах, которые опровергают эти предположения, или о некоторых убедительных рассуждениях, которые демонстрируют, почему они истинны или ложны? К сожалению, задача немного круглая. Я не знаю ни одного формального описания контейнера «фиксированного размера», и пытаюсь придумать его. StrongApplicative
- моя лучшая попытка.
Чтобы оценить, является ли это хорошим определением, мне нужно кое-что сравнить с ним. Учитывая некоторое формальное / неформальное определение того, что означает, что функтор имеет заданный размер или кратность по отношению к обитателям аргумента его типа, вопрос заключается в том, точно ли существование экземпляра StrongApplicative
различает функторы фиксированного и переменного размера.
Не зная о существующем формальном определении, я обращаюсь к интуиции при использовании термина «фиксированный размер». Однако, если кто-то уже знает о существующем формализме для размера функтора и может сравнить StrongApplicative
с ним, тем лучше.
** Под "множественностью" я имею в виду в широком смысле «сколько» произвольных элементов типа параметра функтора встречается у обитателя типа фоменора кодомена. Это без относительно специфицированного типа c, к которому применяется функтор, и, следовательно, без учета каких-либо специфических c обитателей типа параметра.
Не совсем точно вызвал некоторую путаницу в комментариях, поэтому вот несколько примеров того, что я бы посчитал размером / множественностью различных функторов:
VoidF
: фиксированный, 0 Identity
: фиксированный, 1 Maybe
: переменный, минимум 0, максимум 1 []
: переменная, минимальная 0, максимальная бесконечная NonEmptyList
: переменная, минимальная 1, максимальная бесконечная Stream
: фиксированная, бесконечная Monoid m => (,) m
: фиксированный, 1 data Pair a = Pair a a
: фиксированный, 2 Either x
: переменный, минимум 0, максимум 1 data Strange a = L a | R a
: фиксированный, 1