32 votos

¿Por qué debería Aplicativo ser una superclase de la Mónada?

Dado:

Applicative m, Monad m => mf :: m (a -> b), ma :: m a

parece ser considerada una ley que:

mf <*> ma === do { f <- mf; a <- ma; return (f a) }

o de forma más concisa:

(<*>) === ap

La documentación para Control.Applicative dice que <*> es "aplicación secuencial", y que sugiere que (<*>) = ap. Esto significa que <*> debe evaluar los efectos de forma secuencial de izquierda a derecha, para mantener la coherencia con >>=... Pero que se siente mal. McBride y Paterson original en papel parece implicar que la izquierda-a-derecha de la secuenciación es arbitraria:

La mónada IO, y de hecho a cualquier Mónada, se puede hacer Aplicativo tomando pure= return y <*> = ap. Podemos como alternativa el uso de la variante de ap que realiza los cálculos en el orden opuesto, pero seguiremos a la izquierda-a-derecha en este papel.

Así que hay dos lícito, no trivial derivaciones para <*> que siga de >>= y return, con distinto comportamiento. Y, en algunos casos, ninguno de estos dos derivaciones son deseables.

Por ejemplo, el (<*>) === ap ley de las fuerzas de Datos.Validación para definir dos tipos de datos: Validation y AccValidation. El primero tiene un Monad instancia similar a ExceptT, y una consistente Applicative de instancia que es de utilidad limitada, ya que se detiene después de que el primer error. El último, por el otro lado, no define un Monad de instancia, y por lo tanto es libre de aplicar un Applicative que, mucho más útil, se acumula errores.

Ha habido cierta discusión acerca de este previamente en StackOverflow, pero no creo que realmente tiene a la carne de la pregunta:

¿Por qué debería ser una ley?

Las otras leyes para functors, aplicativos y mónadas-tales como la identidad, la asociatividad, etc.-expresar algunos fundamentales, las propiedades matemáticas de esas estructuras. Podemos implementar varias optimizaciones en el uso de estas leyes y probar cosas acerca de nuestro código a través de ellos. En contraste, se siente a mí como el (<*>) === ap ley impone una restricción arbitraria que no correspondan a ningún beneficio.

Para lo que vale, prefiero la zanja de la ley en favor de algo como esto:

newtype LeftA m a = LeftA (m a)
instance Monad m => Applicative (LeftA m) where
  pure = return
  mf <*> ma = do { f <- mf; a <- ma; return (f a) }

newtype RightA m a = RightA (m a)
instance Monad m => Applicative (RightA m) where
  pure = return
  mf <*> ma = do { a <- ma; f <- mf; return (f a) }

Creo que correctamente captura la relación entre los dos, sin limitar indebidamente.

Así que, un par de ángulos al planteamiento de la pregunta de:

  • Hay otras leyes relativas Monad y Applicative?
  • Es allí cualquier inherente a la razón matemática para efectos de la secuencia de Applicative de la misma manera que lo hacen para Monad?
  • ¿GHC o cualquier otra herramienta para realizar transformaciones de código que asumen que requieren de esta ley, para ser verdad?
  • ¿Por qué es el Functor-Aplicativo-Mónada propuesta considera una población tan bueno? (Citas sería muy apreciada aquí).

Y una pregunta extra:

  • ¿Cómo Alternative y MonadPlus encajan en todo esto?

Nota: la edición principal para aclarar la carne de la pregunta. Respuesta publicada por @duplode cita a una versión anterior.

7voto

mergeconflict Puntos 4233

Bueno, yo no estoy tremendamente satisfecho con las respuestas dadas hasta el momento, pero creo que los comentarios adjuntos a ellos son un poco más convincente. Así que voy a resumir aquí:


Creo que sólo hay una sensata Functor de instancia, que se desprende de Applicative:

fmap f fa = pure f <*> fa

Suponiendo que es único, tiene sentido que Functor debe ser una superclase de Applicative, con la ley. Igualmente, creo que sólo hay una sensata Functor de instancia, que se desprende de Monad:

fmap f fa = fa >>= return . f

Así que, de nuevo, tiene sentido que Functor debe ser una superclase de Monad. La objeción que había (y, realmente, todavía tienen) es que hay dos sensato Applicative casos que se siguen de Monad y, en algunos casos específicos, incluso más que son lícitos; entonces, ¿por qué mandato?

pigworker (primer autor en el original Applicative de papel) escribe:

"Por supuesto que no siga. Es una elección."

(en twitter): "la notación es un castigo injusto para trabajar en una mónada; nos merecemos aplicativo notación"

duplode del mismo modo escribe:

"... es justo decir que pure === return y (<*>) === ap no son leyes en el sentido fuerte que, por ejemplo, la mónada leyes son así ..."

"En la LeftA/RightA idea: hay casos similares en otros lugares en las bibliotecas estándar (por ejemplo, Sum y Product en Data.Monoid). El problema de hacer lo mismo con el Applicative , es que el poder-a-peso de la relación es demasiado bajo para justificar el extra de precisión y flexibilidad. Los newtypes haría aplicativo de estilo mucho menos agradable de usar."

Por lo tanto, estoy feliz de ver que la elección que se indique explícitamente, justificado por el simple razonamiento de que hace la mayoría de los casos comunes más fácil.

6voto

Ørjan Johansen Puntos 6920

Entre otras cosas, se pregunta por qué es el Functor-Applicative-Monad propuesta de una buena cosa. Una de las razones es debido a la falta de unidad significa que hay un montón de duplicación de la API. Considerar el estándar Control.Monad módulo. Las siguientes son las funciones en las que el módulo que, en esencia, el uso de la Monad (no hay ninguno para MonadPlus) restricción:

(>>=) fail (=<<) (>=>) (<=<) join foldM foldM_

Las siguientes son las funciones en las que el módulo donde un Monad/MonadPlus restricción puede como lo que puedo decir fácilmente ser relajado, Applicative/Alternative:

(>>) return mzero mplus mapM mapM_ forM forM_ sequence sequence_ forever
msum filterM mapAndUnzipM zipWithM zipWithM_ replicateM replicateM_ guard
when unless liftM liftM2 liftM3 liftM4 liftM5 ap

Muchos de los de este último grupo ¿ tiene Applicative o Alternative versiones, ya sea en Control.Applicative, Data.Foldable o Data.Traversable – pero, ¿por qué necesitan aprender todo lo que la duplicación en el primer lugar?

5voto

duplode Puntos 3803

y en el mío propio (quizá equivocada) de la intuición, dado pure f <*> ma <*> mb, no tienen que ser de cualquier predeterminada una secuencia ya que ninguno de los valores dependen unos de otros.

Los valores no, pero los efectos de hacer. (<*>) :: t (a -> b) -> t a -> t b significa que usted tiene que de alguna manera se combinan los efectos de los argumentos con el fin de obtener los efectos generales. Si la combinación se conmutativa o no, depende de cómo la instancia se define. Por ejemplo, la instancia de Maybe es conmutativa, mientras que el valor predeterminado, "cross join" ejemplo para las listas no lo es. Por lo tanto, hay casos en los que no se puede evitar la imposición de alguna orden.

¿Cuáles son las leyes, si las hubiere, relativas Mónada y Aplicativo?

Si bien es justo decir que pure === return y (<*>) === ap (citando Control.Applicative) no son leyes en el sentido fuerte que, por ejemplo, la mónada leyes son así, que ayudará a mantener las instancias de sorprendente. Dado que cada Monad da lugar a una instancia de Applicative (en realidad dos instancias, como usted señala), es natural que la instancia real de Applicative coincide con lo Monad nos da. En cuanto a la izquierda-a-derecha convención, siguiendo el orden de ap y liftM2 (que ya existía de nuevo cuando Applicative fue introducido, y que reflejan el orden que impone (>>=)) fue una decisión sensata. (Tenga en cuenta que, si nos ignoró por un momento cuánto (>>=) materia en la práctica, la elección opuesta sería defendible así, como lo haría (<*>) y (=<<), que tienen análogas tipos, los efectos de la secuencia en el mismo orden.)

¿GHC o cualquier otra herramienta para realizar transformaciones de código que asumen que requieren de esta ley, para ser verdad?

Eso suena muy raro dado que Applicative ni siquiera es una superclase de Monad(aún). Estas "leyes", sin embargo, permiten a los lectores de el código para hacer las transformaciones, que importa tanto.

N. B.: Si usted necesita para invertir la secuencia de efectos en un Applicative de instancia, hay Control.Applicative.Backwards, como Gabriel González, ha señalado. También, (<**>) invierte el argumento pero todavía secuencias de efectos de izquierda a derecha, por lo que también puede ser utilizado para revertir la secuenciación. Del mismo modo, (<*) no flip (*>), ya que tanto los efectos de la secuencia de izquierda a derecha.

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X