Класс 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
, и, поскольку дистрибутивы / представительства имеют фиксированную форму, эта реализация является уникальной.Ответы:
Я не уверен насчет этой первой гипотезы, и, основываясь на обсуждениях с @AsadSaeeduddin, это, вероятно, будет трудно доказать, но вторая гипотеза верна. Чтобы понять почему, рассмотрим
StrongApplicative
законhusk . unhusk == id
; то есть для всехx :: f ()
,husk (unhusk x) == x
. Но в Haskell,unhusk == const ()
так что закон равносильно тому, для всехx :: f ()
,husk () == x
. Но это, в свою очередь, подразумевает, что может существовать только одно отдельное значение типаf ()
: если было два значенияx, y :: f ()
, тоx == husk ()
иhusk () == y
, такx == y
. Но если существует только одно возможноеf ()
значение, то оноf
должно иметь фиксированную форму. (Например, дляdata Pair a = Pair a a
, есть только одно значение типаPair ()
, это существоPair () ()
, но есть несколько значений типаMaybe ()
или[()]
.) Таким образомhusk . unhusk == id
подразумевает, что онf
должен быть фиксированной формы.источник
f ()
" подразумевается "число вхождений,a
которые не могут варьироваться" в присутствии причудливых GADT и прочего?a
не может варьироваться» не является достаточным условием дляStrongApplicative
экземпляра; например,data Writer w a = Writer (w,a)
имеет неизменную кратностьa
, но не являетсяStrongApplicative
. Вам на самом деле нужно, чтобы форма функтора была инвариантной, и я считаю, что это следствие того,f ()
что она является одиночной.f ()
», подразумевает «число вхожденийa
не может варьироваться». Я возражаю, что последний шаг этого аргумента не совсем верно; например рассмотретьdata Weird a where One :: a -> Weird a; None :: Weird Bool
. Существует отдельное значение типаWeird ()
, но разные конструкторы имеют различное числоa
s внутри. (Это не полный контрпример, потому чтоFunctor
это сложно, но как мы узнаем, что это не может быть исправлено?)Weird ()
это синглтон, но он не имеет фиксированной формы. НоWeird
это неFunctor
так, так что не может быть вStrongApplicative
любом случае. Я полагаю, что соответствующая гипотеза была бы такой: если «f
а»Functor
, означаетf ()
ли «синглтон»f
фиксированную форму ? Я сильно подозреваю, что это правда, но, как вы заметили, у меня пока нет никаких доказательств.Мы можем ответить хотя бы на один из этих вопросов отрицательно:
На самом деле один из примеров законности
StrongApplicative
в исходном вопросе неверен. Писатель аппликативенMonoid => (,) m
не такStrongApplicative
, потому что к примеруhusk $ unhusk $ ("foo", ()) == ("", ()) /= ("foo", ())
.Аналогично, пример контейнера фиксированного размера:
фиксированной кратности 1, не является сильным аппликативным, потому что, если мы определяем,
husk = Left
тоhusk $ unhusk $ Right () /= Right ()
, и наоборот. Эквивалентный способ увидеть это состоит в том, что это всего лишь писатель, применимый для выбора моноидаBool
.Таким образом, существуют аппликативы «фиксированного размера», которых нет
StrongApplicative
. Все лиStrongApplicative
s имеют фиксированный размер, еще неизвестно.источник
Давайте возьмем представимые функторы в качестве нашего определения «контейнера фиксированного размера»:
Реальное
Representable
имеет несколько законов и суперклассов, но для целей этого ответа нам действительно нужны только два свойства:(Хорошо, нам также нужен законопослушный
instance StrongApplicative ((->) r)
. Легко, ты уже согласен, что он существует.)Если мы примем это определение, то я могу подтвердить эту гипотезу 1:
правда. Вот как:
Есть много законов, которые нужно доказать, но я сосредоточусь только на Большой четверке, которая
StrongApplicative
добавляет - вы, вероятно, уже верите в вводныеApplicative
иOpApplicative
, но если нет, их доказательства выглядят так же, как приведенные ниже ( которые в свою очередь очень похожи друг на друга). Для ясности я буду использоватьzipf
,huskf
и т. Д. Для экземпляра функции, иzipr
,huskr
и т. Д. Для представимого экземпляра, чтобы вы могли отслеживать, какой есть какой. (И чтобы легко было убедиться, что мы не принимаем то, что пытаемся доказать, как допущение! Это нормально использоватьunhuskf . huskf = id
при доказательствеunhuskr . huskr = id
, но было бы неправильно предполагатьunhuskr . huskr = id
это же доказательство.)Доказательство каждого закона происходит в основном одинаково: разверните определения, отбросьте изоморфизм, который
Representable
вам дает, затем используйте аналогичный закон для функций.источник
instance StrongApplicative f => Representable f where type Rep f = forall x. f x -> x
.index
это просто. Я еще не разработал трюкtabulate
, но это кажется мучительно близко.StrongApplicative
экземпляр, но я не смог доказать законы. Поздравляю с этим! Я тоже пытался сделатьRepresentable
примерStrongApplicative
, но не мог придумать хорошийRep
тип - мне было бы интересно узнать, как выforall x. f x -> x
этого добиваетесь?forall x. f x -> x
- это именно те функции, которые выбирают отверстие и возвращают значение в этом отверстии. (И, думая о том, как это осуществитьtabulate
, я выдвинул возражение против типаunhusk
; подробности см. В комментариях к самому вопросу.)forall x. f x -> x
будет работать какRep
. Я считаю, что, используя этоRep
, вы можете писатьindex
для любого типа, а не только для одногоStrongApplicative
- поэтому я подозреваю, что этоforall x. f x -> x
может быть слишком общим.