2011-05-15 51 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擴展,但我認爲美學這是值得的。