MonadPlus
y Monoid
sirven para diferentes propósitos.
A Monoid
está parametrizado sobre un tipo de género *
.
class Monoid m where
mempty :: m
mappend :: m -> m -> m
y así se puede instanciar para casi cualquier tipo para el que existe un operador obvio que es asociativo y que tiene una unidad.
Sin embargo, MonadPlus
no solo especifica que tienes una estructura monoidal, sino también que esa estructura está relacionada con cómo Monad
funciona, y que a esa estructura no le importa el valor contenido en la mónada, esto está (en parte) indicado por el hecho eso MonadPlus
requiere un argumento de tipo * -> *
.
class Monad m => MonadPlus m where
mzero :: m a
mplus :: m a -> m a -> m a
Además de las leyes de monoides, tenemos dos conjuntos de leyes potenciales a las que podemos aplicar MonadPlus
. Lamentablemente, la comunidad no está de acuerdo con lo que deberían ser.
Al menos sabemos
mzero >>= k = mzero
pero hay otras dos extensiones en competencia, la ley de distribución izquierda (sic)
mplus a b >>= k = mplus (a >>= k) (b >>= k)
y la ley de captura de la izquierda
mplus (return a) b = return a
Por tanto, cualquier instancia de MonadPlus
debería satisfacer una o ambas de estas leyes adicionales.
¿Y qué pasa Alternative
?
Applicative
se definió después Monad
, y lógicamente pertenece como una superclase de Monad
, pero en gran parte debido a las diferentes presiones sobre los diseñadores en Haskell 98, ni siquiera Functor
fue una superclase de Monad
hasta 2015. Ahora finalmente tenemos Applicative
como superclase de Monad
en GHC (si no aún en un idioma estándar.)
Efectivamente, Alternative
es a Applicative
lo que MonadPlus
es Monad
.
Por estos obtendríamos
empty <*> m = empty
de manera análoga a lo que tenemos con MonadPlus
y existen propiedades distributivas y de captura similares, al menos una de las cuales debe satisfacer.
Desafortunadamente, incluso la empty <*> m = empty
ley es una afirmación demasiado fuerte. ¡No es válido para Backwards , por ejemplo!
Cuando miramos a MonadPlus, la ley de vacío >> = f = vacío casi se nos impone. La construcción vacía no puede tener ninguna 'a' para llamar a la función f
de todos modos.
Sin embargo, dado que Applicative
es no una superclase de Monad
y Alternative
es no una superclase de MonadPlus
, terminamos definiendo ambos casos por separado.
Además, incluso si Applicative
fuera una superclase de Monad
, terminarías necesitando la MonadPlus
clase de todos modos, porque incluso si obedeciéramos
empty <*> m = empty
eso no es estrictamente suficiente para demostrar que
empty >>= f = empty
Entonces, afirmar que algo es un MonadPlus
es más fuerte que afirmar que lo es Alternative
.
Ahora, por convención, MonadPlus
y Alternative
para un tipo dado deberían coincidir, pero Monoid
pueden ser completamente diferentes.
Por ejemplo, el MonadPlus
y Alternative
para Maybe
hacer lo obvio:
instance MonadPlus Maybe where
mzero = Nothing
mplus (Just a) _ = Just a
mplus _ mb = mb
pero la Monoid
instancia eleva un semigrupo a un Monoid
. Lamentablemente, debido a que no existía una Semigroup
clase en ese momento en Haskell 98, lo hace solicitando a Monoid
, pero sin usar su unidad. ಠ_ಠ
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
es un reclamo más fuerte que Alternative
, que a su vez es un reclamo más fuerte que Monoid
, y aunque las instancias MonadPlus
y Alternative
para un tipo deben estar relacionadas, Monoid
puede ser (y a veces es) algo completamente diferente.
Applicative
yMonadPlus
parecen ser exactamente iguales (restricciones de superclase de módulo).