Клас 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:
IdentityVoidF(->) r(див. відповіді)Monoid m => (,) mVec (n :: Nat)Stream(нескінченно)
Ось декілька Applicatives, для яких ми не можемо:
[]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клас Хаскелла має таємничий tabulate . return = returnзакон (який насправді навіть не має сенсу для немонадних функціонерів), але він дає нам 1/4 умов, про які нам потрібно сказати, tabulateі zipце морфізми відповідної категорії моноїдів . Інші 3 - це додаткові закони, які потрібно вимагати.
tabulateі indexце морфізми відповідної категорії ..."
returnне є серйозною проблемою. cotraverse getConst . Constє реалізацією за замовчуванням для return/ pureз точки зору Distributiveі, оскільки дистрибутори / представники мають фіксовану форму, ця реалізація є унікальною.