Unterscheidung zwischen Typklassen MonadPlus, Alternative und Monoid?

83

Die Standard-Bibliothek Haskell typeclasses MonadPlus, Alternativeund Monoidliefern jeweils zwei Verfahren mit im Wesentlichen gleicher Semantik:

  • Ein leerer Wert: mzero, emptyoder mempty.
  • Eine Bedienungsperson, a -> a -> adie Werte in der typeclass miteinander verbindet: mplus, <|>, oder mappend.

Alle drei spezifizieren diese Gesetze, an die sich Instanzen halten sollten:

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

Es scheint also, dass die drei Typklassen alle die gleichen Methoden bereitstellen .

( Alternativebietet auch someund many, aber ihre Standarddefinitionen sind normalerweise ausreichend und daher für diese Frage nicht allzu wichtig.)

Meine Frage lautet also: Warum haben diese drei extrem ähnlichen Klassen? Gibt es einen wirklichen Unterschied zwischen ihnen, abgesehen von ihren unterschiedlichen Einschränkungen der Oberklasse?

00dani
quelle
Das ist eine gute Frage. Insbesondere Applicativeund MonadPlusscheinen genau gleich zu sein (Modulo-Superklassen-Einschränkungen).
Peter
1
Es gibt auch ArrowZeround ArrowPlusfür Pfeile. Meine Wette: Typensignaturen sauberer zu machen (was unterschiedliche Einschränkungen der Oberklasse zum wirklichen Unterschied macht).
Cat Plus Plus
2
@CatPlusPlus: gut, ArrowZeround ArrowPlushat Art * -> * -> *, welche Mittel Sie sie in für den Pfeiltyp einmal für eine Funktion , die Bedürfnisse sie für eine Vielzahl von Typen verwenden passieren können, ein verwenden MonoidSie eine Instanz verlangen müßten Monoidfür jeden einzelnen Instanziierung, und Sie hätten keine Garantie, dass sie auf ähnliche Weise behandelt wurden, die Instanzen könnten nicht miteinander zusammenhängen!
Edward KMETT

Antworten:

119

MonadPlusund Monoiddienen verschiedenen Zwecken.

A Monoidwird über eine Art von Art parametrisiert *.

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

und so kann es für fast jeden Typ instanziiert werden, für den es einen offensichtlichen Operator gibt, der assoziativ ist und der eine Einheit hat.

Doch MonadPlusnicht nur gibt an, dass Sie eine monoidal Struktur, sondern auch , dass diese Struktur auf , wie die in Beziehung steht Monadfunktioniert, und dass diese Struktur nicht über den Wert schert in der Monade enthalten ist , das ist (teilweise ) , die durch die Tatsache , das MonadPlusbraucht ein Argument der Art * -> *.

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

Zusätzlich zu den monoiden Gesetzen gibt es zwei mögliche Gesetze, auf die wir uns anwenden können MonadPlus. Leider ist sich die Community nicht einig, was sie sein sollte.

Zumindest wissen wir es

mzero >>= k = mzero

Es gibt jedoch zwei weitere konkurrierende Erweiterungen, das linke (sic) Verteilungsgesetz

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

und das linke Fanggesetz

mplus (return a) b = return a

Daher sollte jeder Fall MonadPluseines oder beide dieser zusätzlichen Gesetze erfüllen.

Was ist also Alternative?

Applicativewurde nach definiert Monadund gehört logischerweise als Oberklasse von Monad, aber hauptsächlich aufgrund des unterschiedlichen Drucks auf die Designer in Haskell 98, Functorwar Monadbis 2015 nicht einmal eine Oberklasse von . Jetzt haben wir endlich Applicativeals Oberklasse von Monadin GHC (wenn nicht noch in einem Sprachstandard.)

Effektiv Alternativeist zu Applicativewas MonadPlusist zu Monad.

Für diese würden wir bekommen

empty <*> m = empty

analog zu dem, was wir haben MonadPlusund es gibt ähnliche Verteilungs- und Fangeigenschaften, von denen Sie mindestens eine erfüllen sollten.

Leider ist auch das empty <*> m = emptyRecht ein zu starker Anspruch. Es gilt zum Beispiel nicht für Rückwärts !

Wenn wir uns MonadPlus ansehen, wird uns fast das leere >> = f = leere Gesetz aufgezwungen. Die leere Konstruktion kann fsowieso kein 'a' enthalten, um die Funktion aufzurufen.

Da jedoch Applicativeist nicht eine übergeordnete Klasse von Monadund Alternativeist nicht eine übergeordnete Klasse von MonadPlus, wickeln wir separat beiden Instanzen bis zu definieren.

Selbst wenn Applicativees eine Superklasse von wäre Monad, würden Sie die MonadPlusKlasse sowieso brauchen , denn selbst wenn wir gehorchen würden

empty <*> m = empty

das ist nicht streng genug, um das zu beweisen

empty >>= f = empty

Die Behauptung, dass etwas ein MonadPlusist, ist stärker als die Behauptung, dass es ein ist Alternative.

Konventionell sollte das MonadPlusund Alternativefür einen bestimmten Typ übereinstimmen, aber das Monoidkann völlig anders sein.

Zum Beispiel das MonadPlusund Alternativefür Maybedas Offensichtliche tun:

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

aber die MonoidInstanz hebt eine Halbgruppe in eine Monoid. Da es Semigroupzu diesem Zeitpunkt in Haskell 98 noch keine Klasse gab, wird leider eine Klasse angefordert Monoid, deren Einheit jedoch nicht verwendet. ಠ_ಠ

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 ist eine stärkere Behauptung als Alternative, was wiederum eine stärkere Behauptung ist als Monoid, und während die MonadPlusund AlternativeInstanzen für einen Typ in Beziehung gesetzt werden sollten, Monoidkann (und ist manchmal) etwas völlig anderes sein.

Edward KMETT
quelle
2
Ausgezeichnete Antwort, aber die letzte Definition scheint falsch zu sein, sie befriedigt nicht mempty `mappend` x ≡ x.
Vitus
2
Gute Antwort. Kennt jemand einen (häufig verwendeten) Typ, der andere MonadPlus und AlternativeImplementierungen hat?
Peter
7
@ EdwardKmett: Diese Antwort scheint zu implizieren, dass es eine geben könnte, die eine Monadist, Alternativeaber keine MonadPlus. Ich stellte eine Frage, um ein konkretes Beispiel dafür zu finden. Wenn Sie eines kennen, würde ich es gerne sehen.
Antal Spector-Zabusky
2
Können Sie das linke Fanggesetz für Monadplus erklären? Es wird anscheinend von [] verletzt; sollte [] sein zweites Argument wirklich ignorieren, wenn sein erstes nicht leer ist?
Ben w
4
@benw left Distribution ist wohl das vernünftigere Gesetz, aber es gilt in einigen Fällen nicht. left catch ist ein alternatives Gesetz, das diese anderen Instanzen tendenziell unterstützen, aber von den meisten anderen nicht unterstützt werden. Folglich haben wir wirklich zwei weitgehend unabhängige Sätze von Gesetzen, die von verschiedenen Instanzen umgesetzt werden, also MonadPlussind wirklich zwei Klassen als eine getarnt, weil es den meisten Menschen egal ist.
Edward KMETT