Las clases de tipos de Haskell de la biblioteca estándar MonadPlus, Alternativey Monoidcada una proporciona dos métodos con esencialmente la misma semántica:
- Un valor vacío:
mzero,emptyomempty. - Un operador
a -> a -> aque une los valores de la clase de tipos juntos:mplus,<|>omappend.
Los tres especifican estas leyes a las que deben adherirse las instancias:
mempty `mappend` x = x
x `mappend` mempty = x
Por lo tanto, parece que las tres clases de tipos proporcionan los mismos métodos.
( Alternativetambién proporciona somey many, pero sus definiciones predeterminadas suelen ser suficientes, por lo que no son demasiado importantes en términos de esta pregunta).
Entonces, mi consulta es: ¿por qué estas tres clases extremadamente similares? ¿Existe alguna diferencia real entre ellos, además de sus diferentes restricciones de superclase?

ApplicativeyMonadPlusparecen ser exactamente iguales (restricciones de superclase de módulo).ArrowZeroyArrowPluspara flechas. Mi apuesta: hacer que las firmas de tipos sean más limpias (lo que hace que las diferentes restricciones de superclase sean la diferencia real).ArrowZeroyArrowPlustener tipo* -> * -> *, 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 unMonoidtendría que requerir una instancia deMonoidpara cada particular instanciación, y no tendría garantía de que se manejaran de manera similar, ¡las instancias podrían no estar relacionadas!Respuestas:
MonadPlusyMonoidsirven para diferentes propósitos.A
Monoidestá parametrizado sobre un tipo de género*.class Monoid m where mempty :: m mappend :: m -> m -> my 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,
MonadPlusno solo especifica que tienes una estructura monoidal, sino también que esa estructura está relacionada con cómoMonadfunciona, y que a esa estructura no le importa el valor contenido en la mónada, esto está (en parte) indicado por el hecho esoMonadPlusrequiere un argumento de tipo* -> *.class Monad m => MonadPlus m where mzero :: m a mplus :: m a -> m a -> m aAdemá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 = mzeropero 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 aPor tanto, cualquier instancia de
MonadPlusdebería satisfacer una o ambas de estas leyes adicionales.¿Y qué pasa
Alternative?Applicativese definió despuésMonad, y lógicamente pertenece como una superclase deMonad, pero en gran parte debido a las diferentes presiones sobre los diseñadores en Haskell 98, ni siquieraFunctorfue una superclase deMonadhasta 2015. Ahora finalmente tenemosApplicativecomo superclase deMonaden GHC (si no aún en un idioma estándar.)Efectivamente,
Alternativees aApplicativelo queMonadPlusesMonad.Por estos obtendríamos
empty <*> m = emptyde manera análoga a lo que tenemos con
MonadPlusy existen propiedades distributivas y de captura similares, al menos una de las cuales debe satisfacer.Desafortunadamente, incluso la
empty <*> m = emptyley 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
fde todos modos.Sin embargo, dado que
Applicativees no una superclase deMonadyAlternativees no una superclase deMonadPlus, terminamos definiendo ambos casos por separado.Además, incluso si
Applicativefuera una superclase deMonad, terminarías necesitando laMonadPlusclase de todos modos, porque incluso si obedeciéramosempty <*> m = emptyeso no es estrictamente suficiente para demostrar que
empty >>= f = emptyEntonces, afirmar que algo es un
MonadPluses más fuerte que afirmar que lo esAlternative.Ahora, por convención,
MonadPlusyAlternativepara un tipo dado deberían coincidir, peroMonoidpueden ser completamente diferentes.Por ejemplo, el
MonadPlusyAlternativeparaMaybehacer lo obvio:instance MonadPlus Maybe where mzero = Nothing mplus (Just a) _ = Just a mplus _ mb = mbpero la
Monoidinstancia eleva un semigrupo a unMonoid. Lamentablemente, debido a que no existía unaSemigroupclase en ese momento en Haskell 98, lo hace solicitando aMonoid, 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 = NothingTL; DR
MonadPluses un reclamo más fuerte queAlternative, que a su vez es un reclamo más fuerte queMonoid, y aunque las instanciasMonadPlusyAlternativepara un tipo deben estar relacionadas,Monoidpuede ser (y a veces es) algo completamente diferente.fuente
mempty `mappend` x ≡ x.MonadPluseAlternativeimplementaciones?Monadque es unAlternativepero no unMonadPlus. Hice una pregunta sobre cómo encontrar un ejemplo específico de esto; si conoces alguno, me encantaría verlo.MonadPluslo que en realidad hay dos clases disfrazadas como una porque a la mayoría de las personas no les importa.