首页 > 解决方案 > 如何在 Haskell 中记录所有异常?

问题描述

原标题检查所有异常时如何处理多个异常类型实例?

我有以下导入(注意我的前奏实际上是 ClassyPrelude,它使用UnliftIO.Exception)。请注意, System.Logger 来自tinylog ,它是fast-logger之上的一个精简库。

import           Prelude                   hiding(log)
import           System.Logger             hiding(log)
import qualified System.Logger             as TL

以及以下功能:

logExceptions :: MonadUnliftIO m => Logger -> m a -> m a
logExceptions logger program = withException program
  (\ex -> do
    logIt Warn logger ["Logging exception: ", (show ex)]
    flush logger
    )

将 lambda 放入具有类型的本地函数可能会使其更加清晰:

logExceptions :: MonadUnliftIO m => Logger -> m a -> m a
logExceptions logger program = withException program logEx
  where
    logEx :: (MonadUnliftIO m, Exception e) => e -> m ()
    logEx ex = do
      logIt Warn logger ["Logging exception: ", (show ex)]
      flush logger

这会导致以下编译错误:

    * Could not deduce (Exception e0)
        arising from a use of `withException'
      from the context: MonadUnliftIO m
        bound by the type signature for:
                   logExceptions :: forall (m :: * -> *) a.
                                    MonadUnliftIO m =>
                                    Logger -> m a -> m a
        at src/FDS/Logging.hs:19:1-56
      The type variable `e0' is ambiguous
      These potential instances exist:
        instance Exception SomeException -- Defined in `GHC.Exception.Type'
        instance Exception IOException -- Defined in `GHC.IO.Exception'
        instance Exception SomeAsyncException
          -- Defined in `GHC.IO.Exception'
        ...plus four others         
        ...plus 30 instances involving out-of-scope types
        (use -fprint-potential-instances to see them all)
    * In the expression: withException program logEx
      In an equation for `logExceptions':
          logExceptions logger program
            = withException program logEx
            where                   
                logEx :: (MonadUnliftIO m, Exception e) => e -> m ()
                logEx ex            
                  = do logIt Warn logger ...
                       ....         
   |                                
20 | logExceptions logger program = withException program logEx
   |         

最令人担忧的是plus 30 instances involving out-of-scope types。我可以隐藏这些导入以稍微改善这种情况:

import           GHC.Exception.Type        hiding(SomeException)
import           GHC.IO.Exception          hiding(IOException, SomeAsyncException)

但是,通过这种方式找到所有 30 多种异常类型并将它们全部屏蔽似乎并不合理。我假设我在这里做错了什么,或者我真的需要经历并掩盖一切吗?

注意

  1. 我的logIt函数只是logtinylog 函数的一个薄包装 - 随意替换任何符合人体工程学的东西。

标签: haskellclassy-prelude

解决方案


我现在明白我的问题是Exception参数需要具体类型,因为它是我的问题中所述的多态函数,并且没有调用站点可以将其缩小到特定类型。Catch 'em all 中描述了正确的答案!这里,就是使用具体SomeException类型。结果代码是:

logExceptions :: MonadUnliftIO m => Logger -> m a -> m a
logExceptions logger program = withException program logEx
  where
    logEx :: MonadUnliftIO m => SomeException -> m ()
    logEx ex = do
      logIt Warn logger ["Logging exception: ", (show ex)]
      flush logger

推荐阅读