MonadPlus
y Monoid
sirven para diferentes propósitos.
A Monoid
se parametriza sobre un tipo de tipo *
.
class Monoid m where
mempty :: m
mappend :: m -> m -> m
y así se puede instanciar para casi cualquier tipo para el que exista un operador obvio que sea asociativo y que tenga una unidad.
Sin embargo, MonadPlus
no sólo especifica que tiene una estructura monoidal, sino también que esa estructura se relaciona con la forma en las obras, Monad
y que esa estructura no se preocupa por el valor contenido en la mónada, esto es (en parte) indicado por el hecho de que MonadPlus
toma un argumento del tipo * -> *
.
class Monad m => MonadPlus m where
mzero :: m a
mplus :: m a -> m a -> m a
Además de las leyes monoides, tenemos dos conjuntos potenciales de las leyes que podemos aplicar a 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 de la competencia, la ley izquierdo (sic) la distribución
mplus a b >>= k = mplus (a >>= k) (b >>= k)
y la ley de pestillo izquierdo
mplus (return a) b = return a
Por lo tanto, cualquier instancia de MonadPlus
debe satisfacer una o ambas de estas leyes adicionales.
¿Y qué hay de Alternative
?
Applicative
se definió después de Monad
, y lógicamente pertenece como una superclase de Monad
, pero en gran parte debido a las diferentes presiones sobre los diseñadores de espalda en Haskell 98, aun Functor
no era una superclase de Monad
hasta 2015. Ahora por fin Applicative
tiene como superclase de Monad
en GHC (si aún no está en un lenguaje estándar.)
Efectivamente, Alternative
es Applicative
lo MonadPlus
es Monad
.
Para estos conseguiríamos
empty <*> m = empty
de forma análoga a lo que tenemos con MonadPlus
y existen propiedades distributivas y de captura similares, al menos uno de los cuales debe satisfacer.
Lamentablemente, incluso la ley empty <*> m = empty
es un reclamo demasiado fuerte. ¡No es válido para Backwards, por ejemplo!
Cuando miramos a MonadPlus, la ley vacía >> = f = empty casi nos es forzada. La construcción vacía no puede tener ninguna 'a' para llamar a la función f
de todos modos.
Sin embargo, ya que es Applicative
no una superclase de Monad
y Alternative
es no una superclase de MonadPlus
, que terminan definiendo ambos casos por separado.
Por otra parte, incluso si Applicative
era una superclase de Monad
, que acabaría necesidad de la clase MonadPlus
de todos modos, porque incluso si obedecimos
empty <*> m = empty
que no sea estrictamente suficiente para probar que
empty >>= f = empty
Por lo tanto, afirmar que algo es un MonadPlus
es más fuerte que afirmar que es Alternative
.
Ahora, por convención, el MonadPlus
y Alternative
para un determinado tipo deberían estar de acuerdo, pero el Monoid
puede haber completamente diferente.
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 instancia Monoid
levanta un semigrupo en un Monoid
. Tristemente porque no existía una clase Semigroup
en ese momento en Haskell 98, lo hace solicitando un Monoid
, pero no está usando 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; DRMonadPlus
es una demanda más fuerte que Alternative
, que a su vez es una demanda más fuerte que Monoid
, y mientras los MonadPlus
y Alternative
casos para un tipo deben estar relacionados, la Monoid
puede ser (y a veces es) algo completamente diferente.
Esa es una buena pregunta. En particular, 'Applicative' y' MonadPlus' parecen ser * exactamente * las mismas (restricciones de superclases de módulo). – Peter
También hay 'ArrowZero' y' ArrowPlus' para las flechas. Mi apuesta: hacer que las firmas de tipo sean más limpias (lo que hace que las diferentes restricciones de superclase sean * la * diferencia real). –
@CatPlusPlus: bueno, 'ArrowZero' y' ArrowPlus' tienen kind '* -> * -> *', lo que significa que puede pasarlos por el tipo de flecha una vez para una función que necesita usarlos para una multitud de tipos , para usar un 'Monoid' tendrías que requerir una instancia de 'Monoid' para cada instanciación particular, y no tendrías garantía de que se manejaran de manera similar, ¡las instancias podrían no estar relacionadas! –