72

El-biblioteca estándar Haskell clases de tipos MonadPlus, Alternative, y Monoid cada proporcionan dos métodos con esencialmente la misma semántica:Distinción entre las clases de tipos MonadPlus, Alternativa y Monoid?

  • Un valor vacío: mzero, empty, o mempty.
  • Un operador a -> a -> a que une los valores en la clase de tipos juntos: mplus, <|> o mappend.

Los tres especificar estas leyes a las que deben adherirse casos:

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

Por lo tanto, parece que las tres clases de tipos están ofreciendo los mismos métodos.

(Alternative proporciona también some y many, pero sus definiciones por defecto suelen ser suficientes, por lo que no se es demasiado importante en términos de esta pregunta.)

Por lo tanto, mi pregunta es: ¿por qué tiene estos tres extremadamente clases similares? ¿Hay alguna diferencia real entre ellos, además de sus diferentes restricciones de superclase?

+0

Esa es una buena pregunta. En particular, 'Applicative' y' MonadPlus' parecen ser * exactamente * las mismas (restricciones de superclases de módulo). – Peter

+1

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). –

+1

@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! –

Respuesta

96

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 Applicativeno 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.

+1

¡Una respuesta muy perspicaz! En particular, no me había dado cuenta de que '<*> vacío m = vacío 'no implica' empty >> = f = empty'. – 00dani

+2

Excelente respuesta, sin embargo, la última definición parece ser incorrecta, no cumple 'mempty \' mappend \ 'x ≡ x'. – Vitus

+0

@Vitus: Correcto. La definición es '' 'Nada' mappend' x = x''' y viceversa. – hammar

Cuestiones relacionadas