Класс 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
:
Identity
VoidF
(->) r
(Посмотри ответы)Monoid m => (,) m
Vec (n :: Nat)
Stream
(Бесконечный)
И вот некоторые Applicative
из которых мы не можем:
[]
Either e
Maybe
NonEmptyList
Приведенная здесь схема предполагает, что 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
, и, поскольку дистрибутивы / представительства имеют фиксированную форму, эта реализация является уникальной.