2010-09-25 14 views
11

Estoy tratando de entender el manejo de errores en Haskell. Encontré el artículo "8 ways to report errors in Haskell" pero estoy confundido sobre por qué Maybe y Oither se comportan de manera diferente.Haskell: ¿Por qué los tipos Maybe y Either se comportan de manera diferente cuando se usan como Monads?

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 

Calling testMyDiv2 1 0 da un resultado de "An error", pero llamar testMyDiv1 1 0 da:

"*** Exception: My divison by zero 

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

¿Qué ofrece?

+1

Obtengo '" Mi división por cero "' para 'testMyDiv1 1 0' usando mtl-1.1.0.2. ¿Qué 'Control.Monad.Error' estás usando? – sepp2k

+0

Como dijo sepp2k, depende de las declaraciones 'instancia Monad' para 'Oither' y' Maybe'. Idealmente, debería usar la 'instancia Monad (Either String)' disponible en la base más reciente o una de 'mtl' o' mónadas- {fd, tf} '. –

+0

Me di cuenta de que obtengo un montón de "Estás utilizando la versión anterior de la base del paquete 3.x. Las versiones futuras de GHC no admitirán la versión base 3.x. Debes actualizar tu código para usar la nueva versión base 4.x." errores Entonces, ¿mi instalación ghc está desactualizada? actualización cabal no aplica nada. Estoy pensando en destruir mi instalación y comenzar de nuevo. – stusmith

Respuesta

14

La respuesta corta es que la clase Monad en Haskell agrega la operación fail a la idea matemática original de mónadas, que hace que sea algo controvertido cómo hacer que el tipo O en un (Haskell) Monad, porque hay muchas maneras de hazlo.

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 debería declararse como instance Monad (Either String), lo cual no es legal en H98 porque menciona un tipo particular para uno de los parámetros de Either s (en GHC, la extensión FlexibleInstances haría que el compilador la acepte).
  • 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 fail llama a alguna otra clase para convertir una cadena en cualquier tipo.Esto se hace en el módulo Control.Monad.Error de MTL, que declara 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.
+1

Otra propuesta sobre la discusión de LD mencionada anteriormente fue 'fail msg = Left (error msg)', pero eso es feo por muchas razones. –

4

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 transformers, 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 

Por lo tanto, no existe una definición de lo que falla-para-siempre. En general, se desaconseja fail, ya que no siempre se garantiza que fallará limpiamente en una mónada (a muchas personas les gustaría ver que fail fuera eliminado de la clase Monad).

EDITAR: Debo añadir que ciertamente no está claro fail fue la intención de dejarse como la llamada predeterminada error. Un ping a haskell-cafe o al mantenedor puede valer la pena.

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

+0

La clase de mónada debe limitarse a solo (>> =) si dependía de mí. – kmm

+0

¿Qué tienes contra 'return'? ;-) –

+0

@kmm En realidad, la definición matemática no contiene 'fail', pero se agregó para proporcionar la sintaxis' do 'pura y, por lo tanto, es un mal necesario. – fuz

Cuestiones relacionadas