Класс 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, ())
Класс типов и его законы можно эквивалентно записать так:
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
Если мы думаем о типах, используемых в определениях и законах, неутешительная истина раскрывается; OpApplicativeне более конкретное ограничение, чем 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
Первый закон, приведенный выше, тривиален, поскольку единственным обитателем этого типа () -> ()является функция тождественности ().
Тем не менее, оставшиеся три закона, и , следовательно, сам подкласс, является не тривиальной. В частности, не каждый Applicativeявляется законным примером этого класса.
Вот некоторые Applicativeфункторы, для которых мы можем объявить законные случаи StrongApplicative:
IdentityVoidF(->) r(Посмотри ответы)Monoid m => (,) mVec (n :: Nat)Stream(Бесконечный)
И вот некоторые Applicativeиз которых мы не можем:
[]Either eMaybeNonEmptyList
Приведенная здесь схема предполагает, что StrongApplicativeкласс в некотором смысле является FixedSizeклассом, где «фиксированный размер» * означает, что множественность ** жителей одного aиз жителей f aявляется фиксированной.
Это можно сформулировать как две гипотезы:
- Каждый
Applicativeпредставляющий «фиксированный размер» контейнер элементов своего аргумента типа является экземпляромStrongApplicative - Не
StrongApplicativeсуществует экземпляра, в котором число вхожденийaможет варьироваться
Может ли кто-нибудь подумать о контрпримерах, опровергающих эти предположения, или о некоторых убедительных рассуждениях, демонстрирующих, почему они истинны или ложны?
* Я понимаю, что не правильно определил прилагательное «фиксированный размер». К сожалению, задача немного круглая. Я не знаю ни одного формального описания контейнера «фиксированного размера», и пытаюсь придумать его. StrongApplicativeмоя лучшая попытка до сих пор.
Однако, чтобы оценить, является ли это хорошим определением, мне нужно кое-что сравнить с этим. Учитывая некоторое формальное / неформальное определение того, что означает, что функтор имеет заданный размер или кратность по отношению к обитателям аргумента его типа, вопрос заключается в том, StrongApplicativeточно ли существование экземпляра различает функторы фиксированного и переменного размера.
Не зная о существующем формальном определении, я обращаюсь к интуиции при использовании термина «фиксированный размер». Однако, если кто-то уже знает о существующем формализме для размера функтора и может сравниться StrongApplicativeс ним, тем лучше.
** Под «множественностью» я имею в виду в произвольном смысле «сколько» произвольных элементов типа параметра функтора встречается у обитателя типа фомена функтора. Это не имеет отношения к конкретному типу, к которому применяется функтор, и, следовательно, не относится к каким-либо конкретным обитателям типа параметра.
Не точная информация об этом привела к некоторой путанице в комментариях, поэтому вот несколько примеров того, что я бы посчитал размером / множественностью различных функторов:
VoidF: фиксированный, 0Identity: фиксированный, 1Maybe: переменная, минимум 0, максимум 1[]: переменная, минимум 0, максимум бесконечноNonEmptyList: переменная, минимум 1, максимум бесконечноStream: фиксированный, бесконечныйMonoid m => (,) m: фиксированный, 1data Pair a = Pair a a: фиксированный, 2Either x: переменная, минимум 0, максимум 1data Strange a = L a | R a: фиксированный, 1
(->) rесть и они изоморфны в правильном направлении к этому.
(->) r; вам нужны компоненты изоморфизма, чтобы сохранить сильную аппликативную структуру. По какой-то причине у Representableкласса типов в Haskell есть загадочный tabulate . return = returnзакон (который на самом деле даже не имеет смысла для немонадных функторов), но он дает нам 1/4 от условий, которые мы должны сказать, tabulateи zipявляются морфизмами подходящей категории моноидов. , Остальные 3 - это дополнительные законы, которые вы должны требовать.
tabulateи indexявляются морфизмами подходящей категории ..."
returnне является серьезной проблемой. cotraverse getConst . Constявляется реализацией по умолчанию для return/ pureв терминах Distributive, и, поскольку дистрибутивы / представительства имеют фиксированную форму, эта реализация является уникальной.