MonadPlus
i Monoid
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, jak Monad
działa, i że ta struktura nie dba o wartość zawartą w monadzie, jest to (częściowo) wskazywane przez fakt to MonadPlus
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óźniej Monad
i logicznie należy do nadklasy Monad
, ale głównie ze względu na różne naciski na projektantów w Haskell 98, nawet Functor
nie była superklasą Monad
aż do 2015 roku. Teraz w końcu mamy Applicative
nadklasę Monad
w GHC (jeśli nie jeszcze w standardzie językowym.)
Skutecznie Alternative
jest do Applicative
tego, co MonadPlus
jest Monad
.
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
i Alternative
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ś tej MonadPlus
klasy, ponieważ nawet gdybyśmy byli posłuszni
empty <*> m = empty
to nie wystarczy, aby to udowodnić
empty >>= f = empty
Twierdzenie, że coś jest a, MonadPlus
jest silniejsze niż twierdzenie, że jest Alternative
.
Teraz umownie, MonadPlus
i Alternative
dla danego typu powinny się zgadzać, ale Monoid
może być zupełnie inaczej.
Na przykład MonadPlus
i Alternative
dla Maybe
zrobić oczywistą rzecz:
instance MonadPlus Maybe where
mzero = Nothing
mplus (Just a) _ = Just a
mplus _ mb = mb
ale Monoid
instancja podnosi półgrupę do Monoid
. Niestety, ponieważ Semigroup
w tym czasie w Haskell 98 nie istniała klasa, robi to, żądając Monoid
, 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ąpienia MonadPlus
i Alternative
dla typu powinny być powiązane, Monoid
może być (a czasami jest) czymś zupełnie innym.
Applicative
iMonadPlus
wydają się być dokładnie takie same (ograniczenia modulo nadklasy).