2011-05-15 60 views
65

处理我需要帮助了解三个哈斯克尔的使用功能异常哈斯克尔

  • 试(Control.Exception.try :: Exception e => IO a -> IO (Either e a)
  • 赶上(Control.Exception.catch :: Exception e => IO a -> (e -> IO a) -> IO a
  • 手柄(Control.Exception.handle :: Exception e => (e -> IO a) -> IO a -> IO a

我需要知道几件事:

  1. 什么时候使用哪个功能?
  2. 如何用一个简单的例子来使用这个函数?
  3. catch和handle的区别在哪里?他们只有不同的顺序才具有几乎相同的签名。

我会尽量记下我的考验,希望你能帮助我:

尝试

我有这样一个例子:

x = 5 `div` 0 
test = try (print x) :: IO (Either SomeException()) 

我有两个问题:

  1. 如何设置自定义错误输出?

  2. 我能做些什么来设置所有错误SomeException,所以我不应该写:: IO (Either SomeException())

捕获/尝试

你能告诉我有一个自定义的错误输出一个简短的例子?

+2

Re:3 - 阅读[精细手册](http://hackage.haskell.org/packages/archive/base/latest/doc/html/Control-Exception-Base.html):[handle - 一个版本与争论交换的争论;在处理程序的代码更短的情况下很有用](http://hackage.haskell.org/packages/archive/base/latest/doc/html/Control-Exception-Base.html#v:handle)。 – 2011-05-15 16:26:02

回答

110

什么时候使用这功能?

下面是来自Control.Exception文档推荐:

  • 如果你想要做一些清理工作的事件将引发异常,使用finallybracketonException
  • 要在发生异常并执行其他操作后恢复,最好的选择是使用try系列之一。
  • ......除非您正在从异步异常中恢复,在这种情况下请使用catchcatchJust

试::例外五=> IO一个 - > IO(任E中的)

try需要一个IO操作来运行,并返回一个Either。如果计算成功,则结果将被包装在一个Right构造函数中。 (认为​​正确而不是错误)。如果该操作抛出了指定类型的异常,则它将返回到Left构造函数中。如果该例外是而不是,则它会继续向上传播堆栈。指定SomeException作为类型将捕获所有异常,这可能是也可能不是一个好主意。

请注意,如果您想从纯计算中发现异常,您必须使用evaluate来强制在try之内进行评估。

main = do 
    result <- try (evaluate (5 `div` 0)) :: IO (Either SomeException Int) 
    case result of 
     Left ex -> putStrLn $ "Caught exception: " ++ show ex 
     Right val -> putStrLn $ "The answer was: " ++ show val 

捕获::例外五=> IO一个 - >(E - > IO一) - > IO一个

catch类似于try。它首先尝试运行指定的IO操作,但是如果抛出异常,处理程序会被赋予异常以获得备用答案。

main = catch (print $ 5 `div` 0) handler 
    where 
    handler :: SomeException -> IO() 
    handler ex = putStrLn $ "Caught exception: " ++ show ex 

然而,有一个重要的区别。当使用catch时,处理程序不能被异常异常中断(即通过throwTo从另一个线程抛出)。试图引发异常异常会阻止,直到处理程序完成运行。

请注意,在Prelude中有不同的catch,所以你可能想要做import Prelude hiding (catch)

手柄::例外五=>(E - > IO一) - > IO一个 - > IO一个

handle是简单地与catch以相反的顺序的参数。使用哪一个取决于什么使得你的代码更具可读性,或者如果你想使用部分应用程序,哪一个更适合。他们是相同的。

tryJust,catchJust和handleJust

注意trycatchhandle将赶上指定/推断类型的所有例外。 tryJust和朋友允许你指定一个选择器函数,它筛选出你特别想处理的异常。例如,所有算术错误的类型为ArithException。如果你只是想赶上DivideByZero,你可以这样做:

main = do 
    result <- tryJust selectDivByZero (evaluate $ 5 `div` 0) 
    case result of 
     Left what -> putStrLn $ "Division by " ++ what 
     Right val -> putStrLn $ "The answer was: " ++ show val 
    where 
    selectDivByZero :: ArithException -> Maybe String 
    selectDivByZero DivideByZero = Just "zero" 
    selectDivByZero _ = Nothing 

了一份关于纯度

注意,这种类型的异常处理可以在不纯的代码(即IO单子)才会发生。如果您需要处理纯代码中的错误,则应该使用MaybeEither代替(或其他代数数据类型)来查看返回值。这通常是可取的,因为它更加明确,所以你总是知道哪里会发生什么。像Control.Monad.Error这样的monads使得这种类型的错误处理更容易处理。


参见:

+6

相当丰富,但我很惊讶你忽略了Control.Exception文档中的经验法则。即“使用'try',除非你正在从异步异常中恢复,在这种情况下使用'catch'” – 2011-05-15 20:05:17

+1

@John L:好点。添加 :) – hammar 2011-05-15 20:11:35

1

回复:问题3:catch和handle是same(通过hoogle找到)。选择哪个参数通常取决于每个参数的长度。如果行动较短,则使用catch,反之亦然。从简单的文档处理例如:

do handle (\NonTermination -> exitWith (ExitFailure 1)) $ ... 

此外,你可以想见,咖喱手柄功能,使自定义处理程序,然后你可以通过周围,例如。 (改编自文档):

let handler = handle (\NonTermination -> exitWith (ExitFailure 1)) 

定义错误消息:

do  
    let result = 5 `div` 0 
    let handler = (\_ -> print "Error") :: IOException -> IO() 
    catch (print result) handler 
1

我看到一两件事,也惹恼你(你的第二个问题)是:: IO (Either SomeException())的写作,它让我生气了。

现在我改变了一些代码从这个:

let x = 5 `div` 0 
result <- try (print x) :: IO (Either SomeException()) 
case result of 
    Left _ -> putStrLn "Error" 
    Right() -> putStrLn "OK" 

要这样:

let x = 5 `div` 0 
result <- try (print x) 
case result of 
    Left (_ :: SomeException) -> putStrLn "Error" 
    Right() -> putStrLn "OK" 

要做到这一点,你必须使用ScopedTypeVariables GHC扩展,但我认为美学这是值得的。