尝试在Haskell中抛出异常:
import Control.Exception
import Data.Typeable
data MyException = ThisException | ThatException deriving (Show, Typeable)
instance Exception MyException
data CellPos = CellPos Int Int deriving (Show, Read)
test :: String -> IO CellPos
test str = do
{
if length str == 0
then
throw ThisException;
else
return (CellPos 0 0);
}
编译说:
Can't make a derived instance of `Typeable MyException':
You need -XDeriveDataTypeable to derive an instance for this class
In the data type declaration for `MyException'
我该如何解决?
您是否还可以编写在调用测试函数时如何捕获此类异常?
答案 0 :(得分:12)
您收到此错误是因为您尝试为您的数据类型派生Typeable类的实例(使用deriving (Show, Typeable)
;异常类型需要Typeable实例),但这不是可能在标准的Haskell中;你需要一个GHC扩展来做到这一点。
您可以手动编写Typeable实例,但实际上建议使用DeriveDataTypeable
。要启用扩展程序,您可以输入:
{-# LANGUAGE DeriveDataTypeable #-}
位于源文件的顶部。在命令行上传递-XDeriveDataTypeable
也可以,但不建议这样做;最好记录您在文件顶部使用的语言扩展,并且还简化了编译,因为您不必记住标志。 (它还将扩展名隔离到需要它们的文件。)
此外,您应该将throw
替换为throwIO
的定义test
,因为它在IO monad中为guarantees the correct ordering。
您还应该添加
import Prelude hiding (catch)
在你的导入之上,因为Prelude的catch
用于旧的异常处理机制,否则当你尝试捕获异常时它会与Control.Exception冲突。
捕捉异常很简单;你只需使用catch
:
example :: IO ()
example = do
result <- test "hello" `catch` handler
...
where handler ThisException = putStrLn "Oh no!" >> exitFailure
handler ThatException = putStrLn "Yikes!" >> exitFailure
(foo `catch` bar
语法与catch foo bar
相同;它适用于任何函数。)
请注意,您的异常处理程序必须与您正在运行的操作具有相同的返回类型;你可以return
一个合适的CellPos
,将异常气泡传递给下一个处理程序(可能是全局异常处理程序,它只是打印异常并停止程序),将它传递给throwIO
,或以其他方式逃离该程序,例如本例中的System.Exit.exitFailure
。
答案 1 :(得分:4)
在命令行传递-XDeriveDataTypeable
,或者放行
{-# LANGUAGE DeriveDataTypeable #-}
位于文件顶部。这些方法中的任何一个都指定GHC需要派生数据和可输入实例的语言扩展。我更喜欢第二种方法,因为它将扩展的范围限制在需要它的文件中。