2009-01-10 21 views
21

No creo que sea un error, pero estoy un poco confundido de por qué eso no funciona. Una pregunta extra es por qué menciona la variable e? No hay variable e.Error de variable de tipo ambiguo msg

 
    Prelude> :m +Control.Exception 
    Prelude Control.Exception> handle (\_-> return "err") undefined 

    <interactive>:1:0: 
     Ambiguous type variable `e' in the constraint: 
      `Exception e' 
      arising from a use of `handle' at <interactive>:1:0-35 
     Probable fix: add a type signature that fixes these type variable(s) 
    Prelude Control.Exception> 

Apparently it works fine in ghci 6.8, estoy usando 6.10.1.

Editar: He reducido al mínimo el código. Yo esperaría que para tener el mismo resultado en ambos 6,8 y 6,10

class C a                          

foo :: C a => (a -> Int)-> Int                     
foo _ = 1                          

arg :: C a => a -> Int                       
arg _ = 2                          

bar :: Int                          
bar = foo arg 

tratar de compilarlo:

 
[1 of 1] Compiling Main    (/tmp/foo.hs, interpreted) 

/tmp/foo.hs:12:10: 
    Ambiguous type variable `a' in the constraint: 
     `C a' arising from a use of `arg' at /tmp/foo.hs:12:10-12 
    Probable fix: add a type signature that fixes these type variable(s) 
Failed, modules loaded: none. 
Prelude Control.Exception> 

Respuesta

10

Este problema aparece sólo en GHC 6,10; que no se puede duplicar en GHC 6.8 debido a que el tipo de handle es diferente:

: [email protected] 620 ; ghci 
GHCi, version 6.8.2: http://www.haskell.org/ghc/ :? for help 
Loading package base ... linking ... done. 
Prelude> :m +Control.Exception 
Prelude Control.Exception> handle (\_ -> return "err") undefined 
"err" 
Prelude Control.Exception> 

Aceptar tal vez pueda obtener este derecho en el último. Creo que el problema es no restricción de monomorfismo, sino que has tocado una instancia del problema de Leer/Mostrar: estás ofreciendo manejar algún tipo de excepción, en la nueva versión de `handle, hay más de un tipo de excepción, y el tipo de esa excepción no aparece en su resultado. Por lo tanto, el compilador no tiene forma de saber qué tipo de excepción está tratando de manejar. Una forma de trabajar esto es elegir uno. Aquí hay un código que funciona:

Prelude Control.Exception> let alwaysError :: SomeException -> IO String; alwaysError = \_ -> return "err" 
Prelude Control.Exception> handle alwaysError undefined 
"err" 

A propósito, el ejemplo de uso de handle en la documentación de la biblioteca GHC no compila bajo 6,10. He archivado un informe de error.

+0

¿Por qué el compilador necesita saber qué excepción? La función maneja todos los tipos de la clase. – luntain

+0

Debido al tipo de 'handle'; cualquier uso de 'handle' debe aplicarse a * un * tipo particular de la clase Exception. Como su manejador funciona para todos los tipos en la clase, no hay forma de que el compilador asigne un tipo a 'handle'. (La 'e' viene del tipo de' handle'). –

+0

Si usa la extensión ScopedTypeVariables puede simplemente 'manejar (\ (_ :: SomeException) -> return' err ') undefined'. – porges

1

"Exception e" es probable que provenga del tipo de firma de "handle".

The documentation dice:

handle :: Exception e => (e -> IO a) -> IO a -> IO a 

En GHC 6.8 que solía ser diferente, lo que explicaría por qué no consigo ese error.

handle :: (Exception -> IO a) -> IO a -> IO a 

Parece que se está encontrando con la restricción de monomorfismo. Ese "_" - Patrón debe ser monomórfico (que es con ghc 6.8) o explícitamente escrito. Una "solución alternativa" es poner el patrón en el lado izquierdo de una definición, donde constituye un "enlace de patrón simple" como se especifica en el Informe Haskell.

Prueba esto:

let f _ = return "err" 
handle f undefined 

http://www.haskell.org/haskellwiki/Monomorphism_restriction

+0

Sea f _ = retorno 'err' mango f indefinido da el mismo error – luntain

+0

¿Y el pragma {- # -XNoMonomorphismRestriction # -} – Waquo

+0

Creo que es {- # LANGUAGE NoMonomorphismRestriction # -} – Waquo

3

Una solución consiste en utilizar Control.OldException en GHC 6,10 * en lugar de Control.Exception..

2

Intente darle a su controlador el tipo SomeException -> IO x, donde x es un tipo concreto, p. Ej.

import Control.Exception 
let f _ = putStrLn "error" :: SomeException -> IO() 
in handle f undefined 
11

El tipo de Control.Exception.handle es:

handle :: Exception e => (e -> IO a) -> IO a -> IO a 

El problema que estamos viendo es que la expresión lambda (\_ -> return "err") no es de tipo e -> IO a donde e es una instancia de Exception. ¿Claro como el barro? Bueno. Ahora voy a ofrecer una solución que en realidad debería ser útil :)

Lo que pasa es que en su caso e debería ser Control.Exception.ErrorCall desde undefined utiliza error que arroja ErrorCall (una instancia de Exception).

Para manejar usos de undefined puede definir algo como handleError:

handleError :: (ErrorCall -> IO a) -> IO a -> IO a 
handleError = handle 

Básicamente se trata de un alias Control.Exception.handle con e fijados según ErrorCall que es lo que error tiros.

Parece que este cuando se ejecuta en GHCi 7.4.1:

ghci> handleError (\_ -> return "err") undefined 
"err" 

Para manejar todas las excepciones en función handleAll se puede escribir de la siguiente manera:

handleAll :: (SomeException -> IO a) -> IO a -> IO a 
handleAll = handle 

La captura todas las excepciones tiene consecuencias bien descrito en este extracto de la documentación Control.Exception:

La captura de todas las excepciones

Es posible capturar todas las excepciones, utilizando el tipo de SomeException:

catch f (\e -> ... (e :: SomeException) ...) 

Sin embargo, esto no es normalmente lo que quieres hacer!

Por ejemplo, supongamos que desea leer un archivo, pero si no existe, continúe como si contuviera "". Es posible que sienta la tentación de atrapar todas las excepciones y devolver "" en el controlador. Sin embargo, esto tiene todo tipo de consecuencias indeseables. Por ejemplo, si el usuario presiona el control C en el momento justo, se capturará la excepción UserInterrupt, y el programa continuará ejecutándose bajo la creencia de que el archivo contiene "". De forma similar, si otro subproceso intenta matar el subproceso que lee el archivo, se ignorará la excepción ThreadKilled.

En su lugar, solo debe detectar exactamente las excepciones que realmente desea. En este caso, esto probablemente sería más específico que incluso "cualquier excepción IO"; un error de permisos probablemente también desee ser manejado de manera diferente. En su lugar, es probable que desee algo como:

e <- tryJust (guard . isDoesNotExistError) (readFile f) 
let str = either (const "") id e 

Hay ocasiones cuando realmente necesita para recoger cualquier tipo de excepción. Sin embargo, en la mayoría de los casos esto es solo para que pueda hacer un poco de limpieza; usted no está realmente interesado en la excepción en sí misma.Por ejemplo, si abre un archivo, quiere volver a cerrarlo, ya sea que el procesamiento del archivo se ejecute normalmente o genere una excepción. Sin embargo, en estos casos puede usar funciones como bracket, finally y onException, que en realidad nunca le pasan la excepción, pero solo llame a las funciones de limpieza en los puntos apropiados.

Pero a veces realmente es necesario detectar cualquier excepción, y realmente ver cuál es la excepción. Un ejemplo es en el nivel superior de un programa, es posible que desee detectar cualquier excepción, imprimirlo en un archivo de registro o en la pantalla, y luego salir con gracia. Para estos casos, puede usar catch (o una de las otras funciones de captura de excepciones) con el tipo SomeException.

Fuente: http://www.haskell.org/ghc/docs/latest/html/libraries/base/Control-Exception.html#g:4

Cuestiones relacionadas