我有异常类型 UnknownException
,我希望在抛出该异常时包含 CallStack。
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 包含在异常中是一个好习惯吗?
最佳答案
是的,我认为这是一个很好的做法。如果您使用分析进行编译,则只需使用
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
。可悲的是,
If there is no CallStack in scope and the enclosing definition has an explicit type signature, GHC will solve the HasCallStack constraint for the singleton CallStack containing just the current call-site.
这意味着它将忽略 f
的任何调用者,即使他们确实有 HasCallStack
只是因为 f
没有这样的约束。我觉得这非常麻烦。
这是一个相当新的功能,所以我希望 GHC 团队有更好的想法,他们可以利用它来实现。
关于haskell - 如何使用CallStack抛出异常?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53999891/