2011-11-17 16 views
30

Soy bastante nuevo para Haskell y lentamente me he estado dando cuenta de que algo anda mal con la falla de Monad. Real World Haskell warns against its use ("¡Una vez más, recomendamos que casi siempre evite usar el error!"). Me di cuenta hoy de que Ross Paterson lo llamó "una verruga, no un patrón de diseño" back in 2008 (y parecía llegar a un acuerdo en ese hilo).¿Debo evitar el uso de la mónada?

Mientras veía al Dr. Ralf Lämmel talk on the essence of functional programming, comencé a entender una posible tensión que pudo haber provocado que la Mónada fallara. 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 básico y, en ocasiones, 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 tan común y quieres evitar los cambios en el analizador 'base' (o lo que sea) tanto como sea posible. Por supuesto, algún tipo de 'falla' tiene sentido para analizadores pero no siempre, por ejemplo, poner/obtener de Estado o preguntar/local de Reader.

Avíseme si podría ir por el camino equivocado.

¿Debo evitar el uso de Monad? ¿Cuáles son las alternativas a la falla de Monad? ¿Hay alguna biblioteca alternativa de mónadas que no incluya esta "verruga de diseño"? ¿Dónde puedo obtener más información sobre el historial de esta decisión de diseño?

+4

[Mundo real Haskell dice] (http://book.realworldhaskell.org/read/monads.html#monads.monad.fail) "[en muchas mónadas]' fail' usa 'error'. Llamando' error' por lo general es altamente indeseable, ya que arroja una excepción que los llamadores no pueden captar o no esperan ". – Miikka

+3

Respuesta corta: sí. –

+2

Creo que se requiere "falla" para manejar fallas de coincidencia de patrones cuando cosas como: "Solo x <- algo que devuelve ImoMaybe". –

Respuesta

27

Algunas mónadas tienen un mecanismo de falla sensible, p. la mónada de terminal:

data Fail x = Fail 

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

data Return x = Return x 

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

I ver menos objeciones a la utilización de fail (especialmente indirectamente, por búsqueda de Pat <- computation) cuando se trabaja en una mónada específico para el que un buen comportamiento fail se ha especificado claramente. 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 en lugar de solo Monad.

Se podría argumentar que la mejor disciplina siempre es tratar los casos de falla de manera explícita. Me opongo a esta posición en dos aspectos: (1) que el punto de la programación monádica es evitar ese 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 horrible. 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 

que pueden ayudar un poco.

Pero toda esta situación es un desastre. A menudo es útil caracterizar las mónadas por las operaciones que apoyan. Puede ver fail, throw, etc. como operaciones soportadas por algunas mónadas pero no otras. Haskell hace que sea bastante torpe y costoso soportar pequeños cambios localizados en el conjunto de operaciones disponibles, introduciendo nuevas operaciones explicando cómo manejarlas en términos de las anteriores. Si queremos seriamente hacer un trabajo más limpio aquí, tenemos que replantearnos cómo funciona el catch, para que sea un traductor entre diferentes mecanismos de manejo de errores locales.A menudo quiero poner entre corchetes un cálculo que puede fallar de manera no informativa (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 mejorarse, pero al menos, use fail solo para mónadas específicas que ofrecen una implementación sensata y manejan las "excepciones" correctamente.

+0

Disculpe, ¿qué es ese "SHE" que menciona? –

+1

Es la Mejora Strathclyde Haskell, mi preprocesador soporta características experimentales (tipos dependientes falsos, principalmente). Pero en estos días, SHE se ha vuelto redundante por la adopción de gran parte de esa funcionalidad en GHC. – pigworker

24

En Haskell 1.4 (1997) no había fail. En cambio, había una clase de tipo MonadZero que contenía un método zero. Ahora, la notación do usó zero para indicar una falla de coincidencia de patrón; esto causó sorpresas a las personas: si su función necesitaba Monad o MonadZero dependía de cómo usaban la notación do en ella.

Cuando se diseñó Haskell 98 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. Del mismo modo, para eliminar el problema de clase de tipo do, se eliminó la clase MonadZero; para el uso de do, se agregó el método fail al Monad; y para otros usos de zero, se agregó un método mzero al MonadPlus.

Creo que es un buen argumento que fail no se debe usar para nada explícitamente; su único uso previsto está en la traducción de la notación do. Sin embargo, yo mismo soy a menudo travieso y también uso fail explícitamente.

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.

+0

" Yo mismo soy a menudo travieso y uso 'fail' explícitamente". ¿A menudo? Explique. –

+6

@ DanBurton: ¿Qué hay para explicar? Si estoy en un contexto monádico, tiendo a usar 'fail' para señalar la falla, a menos que tenga una necesidad específica de usar otro mecanismo. – ibid

+0

Gracias. Es bueno escuchar sobre la historia. No sabía que Haskell originalmente solo tenía notación do (sin listas de comprensión). Encontré un hilo interesante en el archivo de la lista de correo http://www.mail-archive.com/[email protected]/msg03002.html –

6

Trato de evitar que Monad falle siempre que sea posible, y hay una gran variedad de formas de capturar fallas dependiendo de su circunstancia. Edward Yang ha escrito una buena visión general en su blog en el artículo titulado 8 ways to report errors in Haskell revisited.

En resumen, las diferentes maneras de informar errores que identifica son:

  1. Uso de error
  2. Uso Tal vez un
  3. utilizar cualquiera de cuerdas de una
  4. Uso Mónada y no generalizarse 1- 3
  5. Use MonadError y un tipo de error personalizado
  6. Use throw in the IO monad
  7. Uso ioError y la captura
  8. tuercas Go con transformadores monad
  9. excepciones comprobadas
  10. fracaso

De éstos, estaría tentado a usar la opción 3, con Either e b si sé cómo manejar el error , pero necesito un poco más de contexto.

+1

O 'Tal vez' ... Usar cualquiera de ellos es bueno. Sin embargo, es molesto utilizar una biblioteca que usa Oither y una biblioteca que usa Maybe en el mismo código. Los transformadores de Mónada no son tan agradables ... – alternative

Cuestiones relacionadas