Чи є усі контейнери фіксованого розміру сильними моноїдними функторами та / або навпаки?


9

Клас 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

Можна поцікавитись, як може виглядати фуніктор, який є моноідальним оксалом щодо тієї самої структури:

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 (нескінченно)

Ось декілька Applicatives, для яких ми не можемо:

  • []
  • Either e
  • Maybe
  • NonEmptyList

Зображення тут говорить про те, що StrongApplicativeклас є в певному сенсі FixedSizeкласом, де "фіксований розмір" * означає, що множина ** жителів aу мешканця f aє фіксованою.

Це можна сказати як дві здогадки:

  • Кожен, що Applicativeпредставляє контейнер «фіксованого розміру» елементів його аргументу типу, є примірникомStrongApplicative
  • Не StrongApplicativeіснує жодного примірника , в якому кількість подій aможе змінюватися

Чи може хтось подумати про контрприклади, які спростовують ці здогадки, чи якісь переконливі міркування, які демонструють, чому вони правдиві чи неправдиві?


* Я розумію, що я неправильно визначив прикметник "фіксованого розміру". На жаль, завдання трохи кругле. Я не знаю жодного формального опису контейнера "фіксованого розміру", і я намагаюся придумати його. StrongApplicative- моя найкраща спроба поки що.

Щоб оцінити, чи це добре визначення, мені потрібно щось порівняти. З огляду на деяке формальне / неофіційне визначення того, що означає функтору мати заданий розмір або кратність щодо жителів аргументу його типу, питання полягає в тому, чи існування StrongApplicativeекземпляра точно відрізняє функціонери фіксованого та різного розміру.

Не усвідомлюючи існуючого формального визначення, я звертаюся до інтуїції, використовуючи термін "фіксований розмір". Однак якщо хтось уже знає про існуючий формалізм за розміром функтора і може порівняти StrongApplicativeйого, тим краще.

** Під "кратністю" я маю на увазі у розрізненому розумінні "скільки" довільних елементів типу параметра функтора, що зустрічаються у мешканця кодомінного типу функтора. Це не стосується конкретного типу, до якого застосовується функтор, а отже, не враховуючи конкретних мешканців типу параметра.

Недокладність цього викликала деяку плутанину в коментарях, тож ось декілька прикладів того, що я вважаю б розміром / кратністю різних функторів:

  • 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

Коментарі не для розширеного обговорення; ця розмова була переміщена до чату .
Самуель Liew

Одне можливе визначення "фіксованого розміру" було б "представницьким". Усі представницькі функтори є сильними додатками в тому сенсі, який описаний тут, тому що (->) rвони є ізоморфними в правильному напрямку до цього.
Даніель Вагнер

@DanielWagner Я думаю, що вам потрібно більше, ніж просто ізоморфізм, щоб успадкувати сильну заявку від (->) r; вам потрібні компоненти ізоморфізму для збереження міцної прикладної структури. Чомусь Representableклас Хаскелла має таємничий tabulate . return = returnзакон (який насправді навіть не має сенсу для немонадних функціонерів), але він дає нам 1/4 умов, про які нам потрібно сказати, tabulateі zipце морфізми відповідної категорії моноїдів . Інші 3 - це додаткові закони, які потрібно вимагати.
Асад Саєдюддін

Вибачте, це повинно бути " tabulateі indexце морфізми відповідної категорії ..."
Асад Саєдюддін

@AsadSaeeduddin Те, що закон зазначено в документах, дивним чином, можливо, але, виявляється, вимагати returnне є серйозною проблемою. cotraverse getConst . Constє реалізацією за замовчуванням для return/ pureз точки зору Distributiveі, оскільки дистрибутори / представники мають фіксовану форму, ця реалізація є унікальною.
дуплод

Відповіді:


4
  • Кожен, що Applicativeпредставляє контейнер «фіксованого розміру» елементів його аргументу типу, є примірникомStrongApplicative
  • Не StrongApplicativeіснує жодного примірника , в якому кількість подій aможе змінюватися

Чи може хтось подумати про контрприклади, які спростовують ці здогадки, або якісь переконливі міркування, які демонструють, чому вони правдиві чи неправдиві?

Я не впевнений у цій першій думці, і виходячи з дискусій з @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 та інших речей?
Даніель Вагнер

@DanielWagner Виявилося, що "кількість випадків aне може змінюватися" не є достатньою умовою для StrongApplicativeекземпляра; наприклад, data Writer w a = Writer (w,a)має неоднакову кратність a, але не є a StrongApplicative. Насправді вам потрібна форма функтора, щоб бути інваріантною, що, на мою думку, є наслідком того, f ()щоб бути одинаком.
bradrn

Я не впевнений, що бачу, наскільки це актуально. У відповіді, підтверджуючи другу гіпотезу, ви стверджуєте, що "сильний додаток" передбачає "одне відмінне f ()" означає, що "кількість подій aне може змінюватися". Я заперечую, що останній крок цього аргументу явно не відповідає дійсності; наприклад розглянути data Weird a where One :: a -> Weird a; None :: Weird Bool. Існує чітке значення типу Weird (), але різні конструктори мають різну кількість as всередині. (Тут не повний контрприклад, тому що Functorважко, але як нам знати, що це неможливо виправити?)
Даніель Вагнер

@DanielWagner Добре, що Weird ()це сингл, але він не має виправленої форми. Але Weirdце не Functorтак, так що StrongApplicativeвсе одно не може бути . Я припускаю, що відповідна гіпотеза буде такою: якщо fце а Functor, то, f ()маючи на увазі, fє однотонний, який має фіксовану форму ? Я сильно підозрюю, що це правда, але, як ви зазначили, я ще не маю доказів.
bradrn

5

Ми можемо відповісти хоча б на одне з цих питань негативно:

Кожен додаток, що представляє контейнер «фіксованого розміру» елементів аргументу його типу, є примірником StrongApplicative

Насправді один із прикладів правомірного StrongApplicativeв первісному питанні є неправильним. Письменницький додаток Monoid => (,) mне є StrongApplicative, тому що, наприклад husk $ unhusk $ ("foo", ()) == ("", ()) /= ("foo", ()).

Аналогічно, приклад контейнера фіксованого розміру:

data Strange a = L a | R a

фіксованої кратності 1, не є сильним додатком, тому що якщо ми визначимо husk = Leftтоді husk $ unhusk $ Right () /= Right (), і навпаки. Еквівалентний спосіб переконатися в тому, що це лише сценарій програми для вашого вибору моноїда Bool.

Тож існують додатки "фіксованого розміру", яких немає StrongApplicative. Чи всі StrongApplicativeмають фіксований розмір, залишається з'ясувати.


5

Візьмемо представницькі функтори як наше визначення "контейнер фіксованого розміру":

class Representable f where
    type Rep f
    tabulate :: (Rep f -> a) -> f a
    index :: f a -> Rep f -> a

У реальної Representableє кілька законів та суперкласів, але для цілей цієї відповіді нам потрібні лише дві властивості:

tabulate . index = id
index . tabulate = id

(Гаразд, нам також потрібен закон, який дотримується законів instance StrongApplicative ((->) r). Легкий горошок, ви вже згодні, що він існує.)

Якщо ми візьмемо це визначення, то я можу підтвердити цю гіпотезу 1:

Кожен, що Applicativeпредставляє контейнер «фіксованого розміру» елементів аргументу його типу, є [законослухняним] екземпляромStrongApplicative

правда. Ось як:

instance Representable f => Applicative f where
    zip (fa, fb) = tabulate (zip (index fa, index fb))
    husk = tabulate . husk

instance Representable f => OpApplicative f where
    unzip fab = let (fa, fb) = unzip (index fab) in (tabulate fa, tabulate fb)
    unhusk = unhusk . index

instance Representable f => StrongApplicative f

Доказано багато законів, але я зосередитимусь саме на Великій четвірці, яка StrongApplicativeдодає - ви, напевно, вже вважаєте, що їх вводять, Applicativeі OpApplicativeякщо ви цього не зробите, їхні докази виглядають так само, як наведені нижче ( які, у свою чергу, дуже схожі між собою). Для наочності я буду використовувати zipf, huskfі т. Д. Для екземпляра функції, і zipr, huskrі т. Д. Для представницького екземпляра, тож ви можете відслідковувати, що це таке. (І щоб було легко перевірити, що ми не сприймаємо те, що ми намагаємось довести, як припущення! Це добре використовувати unhuskf . huskf = idпри доведенні unhuskr . huskr = id, але було б неправильно вважати unhuskr . huskr = idв тому самому доказі.)

Доведення кожного закону протікає в основному однаково: розгортайте визначення, відміняйте ізоморфізм, який Representableдає вам, а потім використовуйте аналогічний закон для функцій.

unhuskr . huskr
= { def. of unhuskr and huskr }
(unhuskf . index) . (tabulate . huskf)
= { index . tabulate = id }
unhuskf . huskf
= { unhuskf . huskf = id }
id

huskr . unhuskr
= { def. of huskr and unhuskr }
(tabulate . huskf) . (unhuskf . index)
= { huskf . unhuskf = id }
tabulate . index
= { tabulate . index = id }
id

zipr (unzipr fab)
= { def. of unzipr }
zipr (let (fa, fb) = unzipf (index fab) in (tabulate fa, tabulate fb))
= { def. of zipr }
let (fa, fb) = unzipf (index fab) in tabulate (zipf (index (tabulate fa), index (tabulate fb)))
= { index . tabulate = id }
let (fa, fb) = unzipf (index fab) in tabulate (zipf (fa, fb))
= { def. of (fa, fb) }
tabulate (zipf (unzipf (index fab)))
= { zipf . unzipf = id }
tabulate (index fab)
= { tabulate . index = id }
fab

unzipr (zipr (fa, fb))
= { def. of zipr }
unzipr (tabulate (zipf (index fa, index fb)))
= { def. of unzipr }
let (fa', fb') = unzipf (index (tabulate (zipf (index fa, index fb))))
in (tabulate fa', tabulate fb')
= { index . tabulate = id }
let (fa', fb') = unzipf (zipf (index fa, index fb))
in (tabulate fa', tabulate fb')
= { unzipf . zipf = id }
let (fa', fb') = (index fa, index fb)
in (tabulate fa', tabulate fb')
= { def. of fa' and fb' }
(tabulate (index fa), tabulate (index fb))
= { tabulate . index = id }
(fa, fb)

В даний час роздумував: instance StrongApplicative f => Representable f where type Rep f = forall x. f x -> x. indexлегко. Я ще не розробив трюк tabulate, але це здається відчутно близьким.
Даніель Вагнер

В обговоренні з @AsadSaeeduddin мені вдалося знайти і цей самий StrongApplicativeекземпляр, але не зміг довести закони. Вітаємо, що з’ясували це! Я намагався зробити цей Representableекземпляр добре StrongApplicative, але не міг придумати хорошого Repтипу - мені було б цікаво дізнатись, як ви forall x. f x -> xцього досягаєте?
bradrn

@bradrn Нагадаємо, що гіпотеза полягає в тому, що ці речі мають фіксований набір "дірок", в які прорізуються елементи. Тоді функції типу - forall x. f x -> xце саме ті функції, які вибирають отвір і повертають значення в цьому отворі. (І, думаючи про те, як реалізувати tabulate, я виступив із запереченням щодо типу unhusk; див. Коментарі до самого питання для деталей.)
Даніель Вагнер,

Дякую @DanielWagner! Це дійсно розумний підхід - я б не думав про це.
bradrn

Спробувавши це здійснити, я не думаю, що переконаний, що forall x. f x -> xце спрацює Rep. Мої міркування полягають у тому, що, використовуючи це Rep, ви можете писати indexдля будь-якого типу, а не лише для StrongApplicativeних, тому я підозрюю, що це forall x. f x -> xможе бути занадто загальним.
bradrn
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.