you monad learn example monads haskell

monads - learn - monad example



¿Debería evitar el uso de la mónada? (3)

Soy bastante nuevo en Haskell y poco a poco me he dado cuenta de que hay algo mal con la existencia de la falla de Monad. Real World Haskell advierte sobre su uso ("¡Una vez más, recomendamos que casi siempre evite usar fail!"). Acabo de darme cuenta de que Ross Paterson lo llamó "una verruga, no un patrón de diseño" en 2008 (y parecía haber un cierto acuerdo en ese hilo).

Mientras miraba al Dr. Ralf Lämmel hablar sobre la esencia de la programación funcional , comencé a entender una posible tensión que pudo haber llevado a Monad a fallar. En la conferencia, Ralf habla acerca de agregar varios efectos monádicos a un analizador monádico de base (registro, estado, etc.). Muchos de los efectos requerían cambios en el analizador base y, a veces, los tipos de datos utilizados. Pensé que la adición de "fallar" a todas las mónadas podría haber sido un compromiso porque "fallar" es muy común y usted quiere evitar los cambios al analizador "base" (o lo que sea) tanto como sea posible. Por supuesto, algún tipo de "falla" tiene sentido para los analizadores, pero no siempre, por ejemplo, poner / obtener de State o ask / local de Reader.

Déjame saber si podría estar en el camino equivocado.

¿Debería evitar el uso de la mónada? ¿Cuáles son las alternativas a la falla de Monad? ¿Existen bibliotecas de mónadas alternativas que no incluyan esta "verruga de diseño"? ¿Dónde puedo leer más sobre la historia en torno a esta decisión de diseño?


Algunas mónadas tienen un mecanismo de falla sensible, por ejemplo, la mónada terminal:

data Fail x = Fail

Algunas mónadas no tienen un mecanismo de falla sensible ( undefined no es sensible), por ejemplo, la mónada inicial:

data Return x = Return x

En ese sentido, es claramente una verruga para exigir que todas las mónadas tengan un método fail . Si está escribiendo programas que se resumen en mónadas (Monad m) => , no es muy saludable utilizar el método de fail m de ese genérico. Eso daría como resultado una función que puede crear instancias con una mónada donde el fail no debería existir realmente.

Veo menos objeciones al uso de fail (especialmente de manera indirecta, al hacer coincidir Pat <- computation ) cuando se trabaja en una mónada específica para la cual se ha especificado claramente un buen comportamiento de fail . Es de esperar que dichos programas sobrevivan a un retorno a la antigua disciplina donde la coincidencia de patrones no trivial creó una demanda de MonadZero lugar de solo Monad .

Se podría argumentar que la mejor disciplina es siempre tratar los casos de fallas explícitamente. Me opongo a esta posición en dos aspectos: (1) que el punto de la programación monádica es evitar tal desorden, y (2) que la notación actual para el análisis de casos sobre el resultado de un cálculo monádico es tan terrible. La próxima versión de SHE admitirá la notación (también se encuentra en otras variantes)

case <- computation of Pat_1 -> computation_1 ... Pat_n -> computation_n

Lo que podría ayudar un poco.

Pero toda esta situación es un desastre lamentable. A menudo es útil caracterizar las mónadas por las operaciones que apoyan. Puedes ver fail , throw , etc. como operaciones soportadas por algunas mónadas pero no por otras. Haskell hace que sea bastante torpe y costoso soportar pequeños cambios localizados en el conjunto de operaciones disponibles, introduciendo nuevas operaciones al explicar cómo manejarlas en términos de las antiguas. Si queremos seriamente hacer un trabajo más ordenado aquí, tenemos que replantearnos cómo funciona la catch , para que sea un traductor entre los diferentes mecanismos locales de manejo de errores. A menudo quiero poner entre corchetes un cálculo que puede fallar sin información (por ejemplo, por falla de coincidencia de patrones) con un controlador que agrega más información contextual antes de transmitir el error. No puedo evitar sentir que a veces es más difícil hacer eso de lo que debería ser.

Por lo tanto, este es un problema que podría mejorar, pero al menos, el uso fail solo para mónadas específicas que ofrecen una implementación sensata y manejan las "excepciones" de manera adecuada.


En Haskell 1.4 (1997) no hubo fail . En su lugar, había una clase de tipo MonadZero que contenía un método zero . Ahora, la notación do utiliza zero para indicar un fallo de coincidencia de patrón; Esto causó sorpresas a las personas: si su función era necesaria, MonadZero o MonadZero dependían de cómo usaban la notación de do en ella.

Cuando Haskell 98 fue diseñado un poco más tarde, hicieron varios cambios para simplificar la programación al principiante. Por ejemplo, las comprensiones de mónadas se convirtieron en listas de comprensión. De manera similar, para eliminar el problema de clase de tipo do , se MonadZero clase MonadZero ; para el uso de do , el método fail se agregó a Monad ; y para otros usos de zero , se agregó un método MonadPlus a MonadPlus .

Creo que hay un buen argumento que hacer que fail debe usar para nada explícitamente; Su único uso pretendido es en la traducción de la notación do . Sin embargo, yo mismo a menudo soy travieso y el uso fail explícitamente, también.

Puede acceder a los informes originales 1.4 y 98 here . Estoy seguro de que la discusión que conduce al cambio se puede encontrar en algunos archivos de listas de correo electrónico, pero no tengo enlaces a mano.


Intento evitar el fallo de Monad siempre que sea posible, y hay una gran variedad de formas de capturar el fallo según su circunstancia. Edward Yang ha escrito un buen resumen en su blog en el artículo titulado 8 maneras de informar errores en Haskell revisado .

En resumen, las diferentes formas de informar los errores que él identifica son:

  1. Usar error
  2. Use Tal vez un
  3. Use Either String a
  4. Usa la mónada y no generalizas 1-3
  5. Utilice MonadError y un tipo de error personalizado
  6. Utilice throw en la mónada IO
  7. Use ioError y atrape
  8. Se vuelven locos con transformadores monad
  9. Excepciones comprobadas
  10. Fracaso

De estos, me sentiría tentado a usar la opción 3, con Either eb si sé cómo manejar el error, pero necesito un poco más de contexto.