Різниця між типовими класами MonadPlus, Alternative та Monoid?


83

Стандартна бібліотека Haskell класів типів MonadPlus, Alternativeі Monoidкожен з них надає два методи з практично тією ж самою семантикою:

  • Пусте значення: mzero, emptyабо mempty.
  • Оператор , a -> a -> aякий з'єднує значення в класі типів разом: mplus, <|>або mappend.

Усі три визначають ці закони, яких слід дотримуватися:

mempty `mappend` x = x
x `mappend` mempty = x

Таким чином, здається, усі три типи класів забезпечують однакові методи.

( Alternativeтакож надає someі many, але їх визначень за замовчуванням зазвичай достатньо, і тому вони не надто важливі з точки зору цього питання.)

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


Це гарне запитання. Зокрема, Applicativeі, MonadPlusздається, абсолютно однакові (обмеження за модулем суперкласу).
Пітер

1
Є також ArrowZeroі ArrowPlusдля стрілок. Моя ставка: зробити підписи типу прибиральника (що робить суперклас відрізняючись обмежень , на реальну різницю).
Cat Plus Plus

2
@CatPlusPlus: ну, ArrowZeroі ArrowPlusмають kind * -> * -> *, що означає, що ви можете передати їх для типу стрілки один раз для функції, яка повинна використовувати їх для безлічі типів, щоб використовувати Monoidвам потрібно буде екземпляр Monoidдля кожного конкретного екземпляра, і ви не мали б гарантії, що вони оброблялись подібним чином, екземпляри могли бути не пов’язані!
Едвард КМЕТТ,

Відповіді:


120

MonadPlusі Monoidслужать різним цілям.

A Monoidпараметризується над типом виду *.

class Monoid m where
    mempty :: m
    mappend :: m -> m -> m

і тому його можна створити для майже будь-якого типу, для якого існує очевидний оператор, який є асоціативним і який має одиницю виміру.

Однак MonadPlusне тільки вказує, що у вас моноїдальна структура, але й те, що ця структура пов’язана з тим, як Monadпрацює, і що ця структура не дбає про значення, що міститься в монаді, це (частково) вказує факт що MonadPlusприймає аргумент виду * -> *.

class Monad m => MonadPlus m where
    mzero :: m a
    mplus :: m a -> m a -> m a

На додаток до моноїдних законів, ми маємо два потенційні набори законів, до яких ми можемо застосовуватись MonadPlus. На жаль, громада не погоджується щодо того, якими вони повинні бути.

Принаймні ми знаємо

mzero >>= k = mzero

але є ще два конкуруючих розширення - лівий (sic) закон розподілу

mplus a b >>= k = mplus (a >>= k) (b >>= k)

і закон лівого вилову

mplus (return a) b = return a

Тож будь-який приклад MonadPlusповинен задовольняти одному або обом цим додатковим законам.

То що Alternative?

Applicativeбув визначений після Monad, і логічно належить до суперкласу Monad, але в значній мірі завдяки різному тиску на дизайнерів ще в Haskell 98, навіть Functorне був суперкласом Monadдо 2015 року. Тепер, нарешті, ми маємо Applicativeстатус суперкласу MonadGHC (якщо ні ще в мовному стандарті.)

Фактично, Alternativeце Applicativeте, що MonadPlusмає бути Monad.

За них ми отримали б

empty <*> m = empty

аналогічно тому, що ми маємо, MonadPlusі існують подібні властивості розподілу та лову, принаймні одне з яких ви повинні задовольнити.

На жаль, навіть empty <*> m = emptyзакон є занадто вагомою вимогою. Наприклад, це не стосується Назад !

Коли ми дивимося на MonadPlus, порожній >> = f = порожній закон майже нав'язується нам. Порожня конструкція не може містити символів "a", щоб викликати функцію fв будь-якому випадку.

Однак, так як Applicativeце НЕ суперклас Monadі Alternativeце НЕ суперклас MonadPlus, ми заводитися визначення обох примірників окремо.

Більше того, навіть якби це Applicativeбув суперклас Monad, ти все одно потребував би MonadPlusклас, бо навіть якби ми слухалися

empty <*> m = empty

цього недостатньо строго, щоб довести це

empty >>= f = empty

Тож стверджувати, що щось MonadPlusє сильнішим, ніж стверджувати, що воно є Alternative.

Тепер, за домовленістю, MonadPlusі Alternativeдля даного типу слід узгодити, але це Monoidможе бути зовсім іншим.

Наприклад, for MonadPlusі роблять очевидне:AlternativeMaybe

instance MonadPlus Maybe where
    mzero = Nothing
    mplus (Just a) _  = Just a
    mplus _        mb = mb

але Monoidекземпляр піднімає напівгрупу в Monoid. На жаль, оскільки Semigroupна той час у Haskell 98 не існувало класу, він робить це, вимагаючи a Monoid, але не використовуючи його одиницю. ಠ_ಠ

instance Monoid a => Monoid (Maybe a) where
    mempty = Nothing
    mappend (Just a) (Just b) = Just (mappend a b)
    mappend Nothing x = x
    mappend x Nothing = x
    mappend Nothing Nothing = Nothing

TL; DR MonadPlus є більш сильним , ніж вимога Alternative, яке в свою чергу є більш сильним , ніж вимога Monoid, і в той час , MonadPlusі Alternativeекземпляри для типу повинні бути пов'язані, то Monoidможе бути (а іноді) щось зовсім інше.


2
Відмінна відповідь, проте останнє визначення здається помилковим, воно не задовольняє mempty `mappend` x ≡ x.
Вітус,

2
Чудова відповідь. Хтось знає про (загальновживаний) тип, який має різні MonadPlus та Alternativeреалізації?
Пітер,

7
@EdwardKmett: Ця відповідь, мабуть, натякає на те, що міг бути a, Monadякий є, Alternativeале не a MonadPlus. Я задав питання про пошук конкретного прикладу цього; якщо ви знаєте про одного, я б із задоволенням його побачив.
Antal Spector-Zabusky

2
Чи можете ви пояснити закон лівого вилову для monadplus? Це, очевидно, порушено []; чи повинен [] справді ігнорувати свій другий аргумент, якщо перший не порожній?
ben w

4
Лівий розподіл @benw - це, мабуть, більш розумний закон, але він не діє в деяких випадках. left catch - це альтернативний закон, який ті інші випадки, як правило, підтримують, але який не підтримується більшістю інших. Отже, у нас дійсно є два в основному не пов’язані між собою набори законів, що виконуються різними інстанціями, тож MonadPlusнасправді два класи маскуються під один, тому що більшості людей все одно.
Едвард КМЕТТ
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.