haskell - promocionado - Mensaje de error variable de tipo ambiguo
la columna en field list es ambigua (5)
El tipo de Control.Exception.handle
es:
handle :: Exception e => (e -> IO a) -> IO a -> IO a
El problema que está viendo es que la expresión lambda (/_ -> return "err")
no es del tipo e -> IO a
donde e
es una instancia de Exception
. ¿Claro como el barro? Bueno. Ahora voy a proporcionar una solución que en realidad debería ser útil :)
Sucede que en su caso e
debe ser Control.Exception.ErrorCall
ya que undefined
usa un 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
Es esencialmente un alias Control.Exception.handle
con e
corregido como ErrorCall
que es lo que arroja el error
.
Se ve así cuando se ejecuta en GHCi 7.4.1 :
ghci> handleError (/_ -> return "err") undefined
"err"
Para manejar todas las excepciones, se puede escribir una función handleAll
siguiente manera:
handleAll :: (SomeException -> IO a) -> IO a -> IO a
handleAll = handle
La captura de todas las excepciones tiene las consecuencias descritas bien en este extracto de la documentación de Control.Exception
:
Captura todas las excepciones
Es posible detectar todas las excepciones, utilizando el tipo
SomeException
:
catch f (/e -> ... (e :: SomeException) ...)
¡SIN EMBARGO, esto normalmente no es lo que quieres hacer!
Por ejemplo, supongamos que quiere leer un archivo, pero si no existe, continúe como si contenga
""
. 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ónUserInterrupt
y el programa continuará ejecutándose bajo la creencia de que el archivo contiene""
. De forma similar, si otro hilo intenta matar el hilo que lee el archivo, seThreadKilled
excepciónThreadKilled
.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 cambio, probablemente quieras algo como:
e <- tryJust (guard . isDoesNotExistError) (readFile f) let str = either (const "") id e
Hay ocasiones en las que realmente necesita detectar 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
yonException
, 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 necesitas 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 tipoSomeException
.
Fuente: http://www.haskell.org/ghc/docs/latest/html/libraries/base/Control-Exception.html#g:4
No creo que sea un error, pero estoy un poco desconcertado sobre por qué eso no funciona. Una pregunta extra es por qué menciona la variable e? No hay variable e.
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>
Aparentemente funciona bien en ghci 6.8, estoy usando 6.10.1.
Editar: he minimizado el código. Espero que tenga el mismo resultado tanto en 6.8 como en 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
tratando 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>
Este problema aparece solo en GHC 6.10; no se puede duplicar en GHC 6.8 porque el tipo de handle
es diferente:
: nr@homedog 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>
OK, tal vez pueda hacer esto finalmente. Creo que el problema no es la 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 `manejar, 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"
Por cierto, el uso de ejemplo de handle
en la documentación de la biblioteca de GHC no se compila en 6.10. He archivado un informe de error.
Intente darle a su controlador el tipo SomeException -> IO x
, donde x es un tipo concreto, por ejemplo
import Control.Exception
let f _ = putStrLn "error" :: SomeException -> IO ()
in handle f undefined
La "excepción e" es probable a partir de la firma tipo de "manejar".
La documentación dice:
handle :: Exception e => (e -> IO a) -> IO a -> IO a
En GHC 6.8 solía ser diferente, lo que explicaría por qué no obtengo ese error.
handle :: (Exception -> IO a) -> IO a -> IO a
Parece que te encuentras 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
Una solución alternativa es usar Control.OldException
en ghc 6.10. * En lugar de Control.Exception
.