tipos opciones numero leer imprimir impresion hacer funtores funciones ejemplos como ciclos haskell error-handling monads

opciones - Haskell: ¿Por qué los tipos Maybe y Either se comportan de manera diferente cuando se usan como Monads?



imprimir en haskell (2)

Estoy tratando de entender el manejo de errores en Haskell. Encontré el artículo " 8 formas de informar errores en Haskell " pero estoy confundido sobre por qué Maybe y Oither se comportan de manera diferente.

Por ejemplo:

import Control.Monad.Error myDiv :: (Monad m) => Float -> Float -> m Float myDiv x 0 = fail "My divison by zero" myDiv x y = return (x / y) testMyDiv1 :: Float -> Float -> String testMyDiv1 x y = case myDiv x y of Left e -> e Right r -> show r testMyDiv2 :: Float -> Float -> String testMyDiv2 x y = case myDiv x y of Nothing -> "An error" Just r -> show r

Llamar a testMyDiv2 1 0 da un resultado de "An error" , pero al llamar a testMyDiv1 1 0 obtiene:

"*** Exception: My divison by zero

(Tenga en cuenta la falta de cotización de cierre, lo que indica que esto no es una cadena sino una excepción).

¿Lo que da?


Supongo que estás usando monads-fd .

$ ghci t.hs -hide-package mtl *Main Data.List> testMyDiv1 1 0 "*** Exception: My divison by zero *Main Data.List> :i Either ... instance Monad (Either e) -- Defined in Control.Monad.Trans.Error ...

Mirando en el paquete de transformadores , que es donde monads-fd obtiene la instancia, vemos:

instance Monad (Either e) where return = Right Left l >>= _ = Left l Right r >>= k = k r

Entonces, no hay definición para Fail what-so-ever. En general, se desaconseja el fail , ya que no siempre se garantiza que fallará limpiamente en una mónada (a mucha gente le gustaría ver que se elimine el fail de la clase Monad).

EDITAR: debería agregar que ciertamente no está claro el fail fue intencionado que se dejara como la llamada de error predeterminada. Un ping a haskell-cafe o al mantenedor puede valer la pena.

EDIT2: La instancia mtl se ha movido a la base , este movimiento incluye eliminar la definición de fail = Left y discusión sobre por qué se tomó esa decisión. Presumiblemente, quieren que las personas usen ErrorT más cuando las mónadas fallan, reservando así fail para situaciones más catastróficas como coincidencias de patrones incorrectos (ej .: Just x <- e donde e -->* m Nothing ).


La respuesta corta es que la clase Monad en Haskell agrega la operación fail a la idea matemática original de las mónadas, lo que hace que sea un tanto controvertido cómo hacer que cualquiera de los tipos se convierta en una Monad (Haskell), porque hay muchas formas de hacerlo.

Hay varias implementaciones flotando que hacen cosas diferentes. Los 3 enfoques básicos que conozco son:

  • fail = Left . Esto parece ser lo que la mayoría de la gente espera, pero en realidad no se puede hacer en el estricto Haskell 98. La instancia tendría que declararse como instance Monad (Either String) , que no es legal en H98 porque menciona un tipo particular para uno de los Either parámetros (en GHC, la extensión FlexibleInstances haría que el compilador la aceptara).
  • Ignore fail , utilizando la implementación predeterminada que solo llama a error . Esto es lo que está sucediendo en tu ejemplo. Esta versión tiene la ventaja de ser compatible con H98, pero la desventaja de ser bastante sorprendente para el usuario (con la sorpresa en el tiempo de ejecución).
  • La implementación de fail llama a otra clase para convertir una cadena en cualquier tipo. Esto se hace en el módulo Control.Monad.Error de MTL, que declara la instance Error e => Monad (Either e) . En esta implementación, fail msg = Left (strMsg msg) . Este es nuevamente legal H98, y de vez en cuando sorprende a los usuarios porque introduce otra clase de tipo. Sin embargo, a diferencia del último ejemplo, la sorpresa llega en el momento de la compilación.