我不认为这是一个错误,但我有点疑惑,为什么这不起作用。一个额外的问题是它为什么提到变量e?没有变量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>
显然它在ghci 6.8中工作正常,我使用的是6.10.1。
编辑:我已将代码最小化。我希望在6.8和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
尝试编译它:
[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>
答案 0 :(得分:11)
Control.Exception.handle
的类型是:
handle :: Exception e => (e -> IO a) -> IO a -> IO a
您看到的问题是lambda表达式(\_ -> return "err")
不是e -> IO a
类型,其中e
是Exception
的实例。像泥一样清楚?好。现在我将提供一个实际上应该有用的解决方案:)
在你的情况下,e
应该是Control.Exception.ErrorCall
,因为undefined
使用引发error
的{{1}}(ErrorCall
的一个实例Exception
})。
要处理undefined
的使用,您可以定义类似handleError
的内容:
handleError :: (ErrorCall -> IO a) -> IO a -> IO a
handleError = handle
它本质上是一个别名Control.Exception.handle
,e
固定为ErrorCall
,这是error
抛出的内容。
在 GHCi 7.4.1 :
中运行时看起来像这样ghci> handleError (\_ -> return "err") undefined
"err"
要处理所有异常,可以按如下方式编写handleAll
函数:
handleAll :: (SomeException -> IO a) -> IO a -> IO a
handleAll = handle
捕获所有异常会在Control.Exception
文档的摘录中详细描述:
捕获所有异常
使用类型
可以捕获所有异常SomeException
:catch f (\e -> ... (e :: SomeException) ...)
但是,这通常不是你想做的!
例如,假设您要读取文件,但如果它不存在则继续,就像它包含
""
一样。您可能想要捕获所有异常并在处理程序中返回""
。然而,这会产生各种不良后果。例如,如果用户在恰当的时刻按下control-C,则会捕获UserInterrupt
异常,并且程序将继续运行,并认为该文件包含""
。同样,如果另一个线程试图杀死读取该文件的线程,则忽略ThreadKilled
异常。相反,您应该只捕获您真正想要的异常。在这种情况下,这可能比“任何IO异常”更具体;权限错误可能也希望以不同方式处理。相反,你可能想要这样的东西:
e <- tryJust (guard . isDoesNotExistError) (readFile f) let str = either (const "") id e
有些时候你确实需要捕获任何异常。但是,在大多数情况下,这只是为了你可以做一些清理;你实际上对异常本身并不感兴趣。例如,如果您打开一个文件然后再想要关闭它,无论是处理文件还是正常执行还是抛出异常。但是,在这些情况下,您可以使用
bracket
,finally
和onException
之类的函数,它们实际上从未向您传递异常,只是在适当的位置调用清理函数。但有时你确实需要捕获任何异常,并实际看到异常是什么。一个例子是在程序的最顶层,您可能希望捕获任何异常,将其打印到日志文件或屏幕,然后优雅地退出。对于这些情况,您可以使用
catch
类型的SomeException
(或其他一个异常捕获函数)。
来源:http://www.haskell.org/ghc/docs/latest/html/libraries/base/Control-Exception.html#g:4
答案 1 :(得分:10)
此问题仅出现在GHC 6.10中;它不能在GHC 6.8中重复,因为handle
的类型不同:
: 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>
好吧也许我终于可以做到这一点了。我认为问题是不单同性限制,而是你遇到了Read / Show问题的一个实例:你提供了处理某种类型的异常,在新版本的`handle中,有多种类型的异常,并且该异常的类型不会出现在您的结果中。因此,编译器无法知道您尝试处理的类型的异常。一种方法是选择一种方法。以下是一些有效的代码:
Prelude Control.Exception> let alwaysError :: SomeException -> IO String; alwaysError = \_ -> return "err"
Prelude Control.Exception> handle alwaysError undefined
"err"
顺便说一句,GHC库文档中handle
的示例使用不在6.10下编译。我已经提交了一份错误报告。
答案 2 :(得分:3)
解决方法是在ghc 6.10。*而不是Control.OldException
中使用Control.Exception
。
答案 3 :(得分:2)
尝试为处理程序提供类型SomeException -> IO x
,其中x是具体类型,例如
import Control.Exception
let f _ = putStrLn "error" :: SomeException -> IO ()
in handle f undefined
答案 4 :(得分:1)
“异常e”可能来自“句柄”的类型签名。
handle :: Exception e => (e -> IO a) -> IO a -> IO a
在GHC 6.8中它曾经不同,这可以解释为什么我没有得到这个错误。
handle :: (Exception -> IO a) -> IO a -> IO a
似乎你遇到了单态限制。那个“_” - 模式必须是单态的(与ghc 6.8一样)或明确输入。 “解决方法”是将模式放在定义的左侧,它构成Haskell报告指定的“简单模式绑定”。
试试这个:
let f _ = return "err"
handle f undefined