我有一个异常类型UnknownException
,我想在抛出该异常时将其包括在内。
module Main where
import Control.Exception (Exception, throw)
newtype UnknownException = UnknownException
{ caller :: String
} deriving (Show)
instance Exception UnknownException
main :: IO ()
main = willThrow
willThrow :: IO ()
willThrow = throw $ UnknownException "willThrow"
我希望上面的示例打印这样的日志
example-exe: UnknownException {caller = "willThrow"}
CallStack (from HasCallStack):
willThrow, called at app/Main.hs:16:13 in main:Main
main, called at app/Main.hs:13:8 in main:Main
但实际上已打印:
example-exe: UnknownException {caller = "willThrow"}
此外,在Haskell的异常中包括CallStack是一种好习惯吗?
答案 0 :(得分:4)
是的,我认为这是一种好习惯。如果要进行概要分析,则只需使用
currentCallStack :: IO [String]
来自GHC.Stack
。请注意,它位于IO
中,但是如果使用纯代码,则抛出错误时,我认为unsafePerformIO
很好。因为所有底部在符号上都是相等的,所以实际上并没有违反纯度。
但是,如果您想不进行概要分析而直接获得调用堆栈(例如,您想将其包含在生产中的日志消息中),则必须做更多的事情。您必须在要报告堆栈的所有位置都包含HasCallStack
约束。所以
main :: IO ()
main = print f
f :: Int
f = g
g :: HasCallStack => Int
g = h
h :: HasCallStack => Int
h = error (show callStack)
将最多报告g
的呼叫堆栈,但将省略f
。遗憾的是
如果作用域中没有CallStack并且封闭的定义具有显式类型签名,则GHC将为仅包含当前呼叫站点的单例CallStack解决HasCallStack约束。
这意味着它将f
的所有调用者忽略,即使它们确实具有HasCallStack
只是因为f
没有这样的约束。我觉得这很麻烦。
这是一个相当新的功能,所以我希望GHC团队能够更好地记住这一点。