Haskell Tutorial(23)Exception 的 catch 與 handle << 前情
Haskell 中有非常多種處理錯誤的方式,像是 8 ways to report errors in Haskell 就提出了八種方式,有些還涉及到目前我還沒談到的 Monad ,無論如何,這邊先從簡單的開始。
error 與 ioError
自行拋出 Exception 最簡單的方式是使用 error 函式,它的型態是 [Char] -> a ,接受一個字串並拋出 SomeException ,例如〈Haskell Tutorial(18)認識 Data.List 模組〉中就使用過 error 來自訂 init 與 last 函式:
init' :: [a] -> [a]
init' [] = error "empty list"
init' (x:[]) = []
init' (x:xs) = x : init' xs
last' :: [a] -> a
last' [] = error "empty list"
last' (x:[]) = x
last' (_:xs) = last' xs
另一個方式是使用 ioError 函式,它的型態是 IOError -> IO a ,因此,使用了這個函式的地方必然就不是純綷、無副作用的函式,例如:
init' :: [a] -> IO [a]
init' [] = ioError $ userError "empty list"
init' (x:[]) = return []
init' (x:xs) = do lt <- init' xs
return (x : lt)
IOError 實際上是 IOException 的別名,由於沒有導出 IOException 的建構式,若要建立實例,可以使用 userError 函式,它的型態是 String -> IOError ,也就是接受一個字串,傳回 IOError (而不是拋出)。
throw 與 throwIO
如果你取得了一個 Exception ,可以使用 throw 函式將之拋出,它的型態是 Exception e => e -> a ,因此,你可以在純綷的函式中使用 throw 來拋出例外,例如:
import Control.Exception
init' :: [a] -> [a]
init' [] = throw $ userError "empty list"
init' (x:[]) = []
init' (x:xs) = x : init' xs
throw 的兄弟之一是 throwIO ,型態是 Exception e => e -> IO a ,因此跟 ioError 一樣,使用了這個函式的地方必然就不是純綷、無副作用的函式,不同的是, ioError 只接受 IOError ,而 throwIO 可接受任何 Exception 。例如:
import Control.Exception
init' :: [a] -> IO [a]
init' [] = throwIO $ SomeException $ userError "empty list"
init' (x:[]) = return []
init' (x:xs) = do lt <- init' xs
return (x : lt)
自訂 Exception
你可以自訂 Exception 型態,Exception 實際上是個 Typeclass,宣告為 class (Typeable e, Show e) => Exception e ,具有 toException :: e -> SomeException 與 fromException :: SomeException -> Maybe e 兩個行為,可用來自訂 Exception 階層,已經有預設實作,如果想知道如何自訂 Exception 階層,可參考 The Exception type。
因為 Exception 宣告時,限定型態必須為 Typeable 與 Show 的實例,因此,自訂 Exception 時,型態也必須衍生自 Typeable 與 Show 。例如以下是個自訂 Exception 的實例:
import Data.Typeable
import Control.Exception
data ListException = EmptyListException String deriving (Show, Typeable)
instance Exception ListException
init' :: [a] -> [a]
init' [] = throw $ EmptyListException "an empty list has no init"
init' (x:[]) = []
init' (x:xs) = x : init' xs
現在,你可以看到自訂的 Exception 了:
有了這幾篇介紹 Exception 的基礎,如果想要瞭解更多 Exception 的處理,建議參考 Haskell 官方的 Control.Exception。
後續 >> Haskell Tutorial(25)可被映射盒中物的 Functor
|