Średnia Biblioteka Haskell typeclasses MonadPlus
, Alternative
i Monoid
każdy zapewniają dwie metody z zasadniczo tą samą składnię:
- Pusta wartość:
mzero
,empty
, lubmempty
. - Operator
a -> a -> a
, który łączy wartości w typeclass razem:mplus
,<|>
, lubmappend
.
Wszystkie trzy określają te prawa, których powinny przestrzegać instancje:
mempty `mappend` x = x
x `mappend` mempty = x
Dlatego wydaje się, że wszystkie trzy typeklasy zapewniają te same metody.
( Alternative
zawiera również some
i many
, ale ich domyślne definicje są zwykle wystarczające, więc nie są zbyt ważne w kontekście tego pytania).
Więc moje zapytanie brzmi: dlaczego te trzy niezwykle podobne klasy? Czy jest między nimi jakaś prawdziwa różnica, poza ich różnymi ograniczeniami dla nadklasy?
Applicative
iMonadPlus
wydają się być dokładnie takie same (ograniczenia modulo nadklasy).ArrowZero
iArrowPlus
na strzały. Mój zakład: uczynić podpisy czystszymi (co sprawia, że różne ograniczenia nadklasy są prawdziwą różnicą).ArrowZero
iArrowPlus
miej rodzaj* -> * -> *
, co oznacza, że możesz przekazać je dla typu strzałki raz dla funkcji, która musi ich używać dla wielu typów, aby użyćMonoid
, musiałbyś wymagać wystąpieniaMonoid
dla każdego konkretnego tworzenie instancji i nie miałbyś gwarancji, że były obsługiwane w podobny sposób, instancje mogą być niepowiązane!Odpowiedzi:
MonadPlus
iMonoid
służą różnym celom.A
Monoid
jest sparametryzowany względem rodzaju*
.class Monoid m where mempty :: m mappend :: m -> m -> m
dzięki czemu można go utworzyć dla prawie każdego typu, dla którego istnieje oczywisty operator, który jest asocjacyjny i który ma jednostkę.
Jednak
MonadPlus
nie tylko określa, że masz monoidalną strukturę, ale także, że ta struktura jest związana z tym, jakMonad
działa, i że ta struktura nie dba o wartość zawartą w monadzie, jest to (częściowo) wskazywane przez fakt toMonadPlus
wymaga pewnego argumentu* -> *
.class Monad m => MonadPlus m where mzero :: m a mplus :: m a -> m a -> m a
Oprócz praw monoidowych mamy dwa potencjalne zbiory praw, do których możemy się zastosować
MonadPlus
. Niestety społeczność nie zgadza się co do tego, czym powinny być.Przynajmniej wiemy
mzero >>= k = mzero
ale istnieją dwa inne konkurujące ze sobą rozszerzenia, lewe (sic) prawo dystrybucji
mplus a b >>= k = mplus (a >>= k) (b >>= k)
i lewe prawo catch
mplus (return a) b = return a
Zatem każdy przypadek
MonadPlus
powinien spełniać jedno lub oba z tych dodatkowych praw.Więc o co chodzi
Alternative
?Applicative
została zdefiniowana późniejMonad
i logicznie należy do nadklasyMonad
, ale głównie ze względu na różne naciski na projektantów w Haskell 98, nawetFunctor
nie była superklasąMonad
aż do 2015 roku. Teraz w końcu mamyApplicative
nadklasęMonad
w GHC (jeśli nie jeszcze w standardzie językowym.)Skutecznie
Alternative
jest doApplicative
tego, coMonadPlus
jestMonad
.Za to dostaniemy
empty <*> m = empty
analogicznie do tego, co mamy
MonadPlus
i istnieją podobne właściwości dystrybucyjne i przechwytujące, z których przynajmniej jedną należy spełnić.Niestety, nawet
empty <*> m = empty
prawo jest zbyt mocnym roszczeniem. Na przykład nie sprawdza się w przypadku Backwards !Kiedy patrzymy na MonadPlus, puste >> = f = puste prawo jest nam prawie narzucone. Pusta konstrukcja nie może zawierać żadnego „a”, aby wywołać funkcję
f
.Jednak, ponieważ
Applicative
jest nie nadklasąMonad
iAlternative
to nie nadklasąMonadPlus
, możemy skończyć zdefiniowania obu instancji oddzielnie.Co więcej, nawet gdyby
Applicative
była superklasąMonad
, i tak potrzebowałbyś tejMonadPlus
klasy, ponieważ nawet gdybyśmy byli posłuszniempty <*> m = empty
to nie wystarczy, aby to udowodnić
empty >>= f = empty
Twierdzenie, że coś jest a,
MonadPlus
jest silniejsze niż twierdzenie, że jestAlternative
.Teraz umownie,
MonadPlus
iAlternative
dla danego typu powinny się zgadzać, aleMonoid
może być zupełnie inaczej.Na przykład
MonadPlus
iAlternative
dlaMaybe
zrobić oczywistą rzecz:instance MonadPlus Maybe where mzero = Nothing mplus (Just a) _ = Just a mplus _ mb = mb
ale
Monoid
instancja podnosi półgrupę doMonoid
. Niestety, ponieważSemigroup
w tym czasie w Haskell 98 nie istniała klasa, robi to, żądającMonoid
, ale nie używając swojej jednostki. ಠ_ಠ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
jest twierdzeniem silniejszym niżAlternative
, które z kolei jest twierdzeniem silniejszym niżMonoid
, i chociaż wystąpieniaMonadPlus
iAlternative
dla typu powinny być powiązane,Monoid
może być (a czasami jest) czymś zupełnie innym.źródło
mempty `mappend` x ≡ x
.MonadPlus
iAlternative
implementacje?Monad
który jest,Alternative
ale nieMonadPlus
. I zadał pytanie o znalezienie konkretnego przykładu tego; jeśli znasz jeden, chciałbym to zobaczyć.MonadPlus
naprawdę dwie klasy są przebrane za jedną, ponieważ większości ludzi to nie obchodzi.