72

Standardowe Biblioteka Haskell typeclasses MonadPlus, Alternative i Monoid siebie są dwa sposoby o zasadniczo takich samych semantyki:Rozróżnienie między typami MonadPlus, Alternative i Monoid?

  • pustą wartość: mzero, empty lub mempty.
  • Operator a -> a -> a, który łączy wartości w typografii razem: mplus, <|> lub mappend.

wszystkich trzech określić te prawa, do których przypadki powinny stosować:

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

Zatem wydaje się, że trzy typeclasses są świadczenia same metod.

(Alternative zapewnia również some i many, ale ich definicje domyślne są zazwyczaj wystarczające, a więc nie są one zbyt ważne, jeśli chodzi o ten temat.)

Więc moje zapytanie brzmi: dlaczego mają te trzy niezwykle podobne klasy? Czy istnieje między nimi ogromna różnica, oprócz różnych ograniczeń nadklasowych?

+0

To dobre pytanie. W szczególności, 'Applicative' i' MonadPlus' wydają się być dokładnie takie same (ograniczenia superklasy modulo). – Peter

+1

Istnieje również strzałka "ArrowZero" i "ArrowPlus". Mój zakład: sprawić, by podpisy były czystsze (co sprawia, że ​​różnice w klasie nadrzędnej są * rzeczywistą różnicą). –

+1

@CatPlusPlus: Cóż, 'ArrowZero' i' ArrowPlus' mają rodzaj '* -> * -> *', co oznacza, że ​​możesz przekazać je dla typu strzałki raz dla funkcji, która musi ich użyć dla wielu typów , aby użyć 'Monoid', musiałbyś wymagać instancji' Monoid' dla każdej konkretnej instancji i nie miałbyś żadnej gwarancji, że będą one obsługiwane w podobny sposób, instancje mogą być niezwiązane! –

Odpowiedz

96

MonadPlus i Monoid służą do różnych celów.

Parametr Monoid jest parametryzowany w odniesieniu do rodzaju *.

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

i tak można utworzyć instancję dla prawie każdego typu, dla którego istnieje oczywisty operator, który jest skojarzony i który ma jednostkę.

Jednak MonadPlus określa nie tylko, które mają strukturę monoidal, ale również, że struktura jest związana z jak Monad prace, i że struktura nie dba o wartości zawartej w monady, to jest (częściowo) wskazane przez fakt, że MonadPlus przyjmuje argument rodzaju * -> *.

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

Poza prawami monoid mamy dwa potencjalne zestawy przepisów możemy zastosować do MonadPlus. Niestety, społeczność nie zgadza się co do tego, czym powinny być.

Przynajmniej wiemy

mzero >>= k = mzero 

ale istnieją dwa inne konkurujące rozszerzenia, prawa (sic!) Dystrybucja lewej

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

i lewo prawo połowu

mplus (return a) b = return a 

Tak więc każdy przypadek MonadPlus powinien spełniać jedno lub oba te dodatkowe prawa.

A co z Alternative?

Applicative został zdefiniowany po Monad i logicznie należy jako nadklasą Monad, ale w dużej mierze z powodu różnych nacisków na projektantów z powrotem w Haskell 98, nawet Functor nie było nadklasą Monad aż 2015. Teraz możemy wreszcie mają Applicative jako nadklasą Monad w GHC (jeśli nie są jeszcze w normie językowej.)

Skutecznie Alternative jest Applicative co MonadPlus ma Monad.

dla tych chcielibyśmy dostać

empty <*> m = empty 

analogicznie do tego, co mamy z MonadPlus i istnieją podobne właściwości rozdzielcze i połowowe, z których przynajmniej jeden powinien spełniać.

Niestety, nawet prawo empty <*> m = empty jest zbyt silnym roszczeniem. Na przykład nie ma dla Backwards!

Kiedy patrzymy na MonadPlus, puste >> = f = puste prawo jest prawie na nas wymuszone. Pusta konstrukcja nie może zawierać żadnych znaków "a", aby wywołać funkcję f.

Jednak od Applicative jest nie nadklasą Monad i Alternative jest nie nadklasą MonadPlus, możemy skończyć zdefiniowania obu instancji oddzielnie.

Ponadto, nawet jeśli Applicative był nadklasą Monad, że można skończyć konieczności klasę MonadPlus anyways, bo nawet jeśli nie słuchać

empty <*> m = empty 

że nie jest na tyle ściśle, aby udowodnić, że

empty >>= f = empty 

A więc twierdzenie, że coś jest jest silniejsze niż twierdzenie, że jest to Alternative.

Obecnie przyjmuje się, że MonadPlus i Alternative dla danego typu powinny uzgodnić, ale Monoid może być całkowicie 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 instancja Monoid podnosi półgrupa w Monoid. Niestety, ponieważ w tym czasie nie było klasy Semigroup w Haskell 98, robi się to przez żądanie Monoid, ale nie używając jej 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; DRMonadPlus jest silniejsze żądanie niż Alternative, które z kolei jest mocniejsze żądanie niż Monoid i a MonadPlus i Alternative przykłady dla typu należy pokrewnej, Monoid może być (a czasami jest) coś zupełnie innego.

+1

Bardzo wnikliwa odpowiedź! W szczególności nie zdawałem sobie sprawy, że 'pusty <*> m = pusty' nie implikuje' pustego >> = f = pustego'. – 00dani

+2

Doskonała odpowiedź, jednak ostatnia definicja wydaje się błędna, nie spełnia 'mempty \' mappend \ 'x ≡ x'. – Vitus

+0

@Vitus: Prawidłowo. Definicja to '' 'Nothing' mappend' x = x''' i na odwrót. – hammar