Haskell中如何忽略释放资源函数异常?优先抛出核心动作原异常
解决方案
标准Haskell base库中没有直接提供满足你需求的现成函数,但你可以基于bracket和异常处理逻辑自定义一个函数,确保核心动作抛出的原始异常被优先保留,同时保证资源释放操作总能执行。
自定义实现示例
基于base库的版本
import Control.Exception (SomeException, mask, catch, onException) bracketPreserveActionException :: IO a -> (a -> IO ()) -> (a -> IO b) -> IO b bracketPreserveActionException acquire release act = mask $ \restore -> do resource <- acquire -- 执行核心动作,若抛出异常则执行释放操作并忽略释放的异常 result <- restore (act resource) `onException` (release resource `catch` \(_ :: SomeException) -> return ()) -- 核心动作成功时,正常执行释放操作(此时释放异常会被抛出) release resource return result
基于UnliftIO库的版本(适配你的示例代码)
import UnliftIO.Exception (SomeException, mask, catch, onException) import UnliftIO (MonadUnliftIO, withRunInIO) bracketPreserveActionException :: MonadUnliftIO m => m a -> (a -> m ()) -> (a -> m b) -> m b bracketPreserveActionException acquire release act = withRunInIO $ \run -> mask $ \restore -> do resource <- run acquire result <- restore (run $ act resource) `onException` (run (release resource) `catch` \(_ :: SomeException) -> return ()) run $ release resource return result
逻辑说明
- 当核心动作
act抛出异常时,函数会执行资源释放操作release,但捕获并忽略释放过程中产生的任何异常,从而保留核心动作的原始异常。 - 当核心动作成功完成时,函数正常执行释放操作,若此时释放过程抛出异常,该异常会被正常抛出(因为此时核心动作已成功,释放失败属于需要关注的错误)。
为什么原生bracket不符合需求
原生bracket的行为是:如果核心动作抛出异常,执行释放操作后,若释放操作也抛出异常,释放的异常会覆盖核心动作的原始异常,这正是你遇到的核心问题——SQL事务中止后,释放操作的错误掩盖了核心SQL操作的原始错误。
内容的提问来源于stack exchange,提问作者Saurabh Nanda
相关产品推荐
相关产品推荐

