4

我有一个非常简单的抽象来处理可以回滚(在某种程度上)的一系列 IO 动作,即如果一个动作写入一个文件,那么回滚将删除这个文件,或者如果一个动作创建一个目录树,修剪这将是回滚等。

data IOAction = IOAction {
  execute  :: IO (),
  rollback :: IO ()
}

executeAll :: [IOAction] -> IO ()
executeAll [] = return ()
executeAll (a : as) = do
  execute a
  executeAll as `catch` rollbackAndRethrow
  where
    rollbackAndRethrow :: SomeException -> IO ()
    rollbackAndRethrow e = rollback a >> throw e

它几乎可以满足我的要求,但我有一种强烈的预感,即有更多可组合和更可靠(在异常处理的意义上)的方法来做到这一点。所以我的问题是我可以使用某个库中已知的 monad 转换器来实现相同的想法吗?

有类似的东西

writeFilesAtomically :: CanRollbackT IO ()
writeFilesAtomically = do
  a1 <- (writeFile a str1) `orRollback` removeFile a
  a2 <- (writeFile x str2) `orRollback` removeFile x
  ....

将比当前的解决方案更方便。

4

1 回答 1

1

这看起来很像WriterTmonad 与ExceptT. 你可能会做这样的事情:

orRollback action rollaction = do
    eres <- liftIO $ try action 
    case eres of
       Right res -> do
          tell [rollaction]
          return res
       Left (e :: SomeException) -> throwE e

然后这样称呼它:

runMyComputation computation = do
   (res, rolls) <- runWriterT $ runExceptT $ computation
   case res of
       Right r -> return r
       Left e -> do
           sequence_ (reverse rolls) -- Handle errors in rollbacks?
           throwIO e

我没有测试它,但这个想法应该可行。[]与经常看到异常相比,您可能需要一些更好的幺半群。

这并不完全组成,因为您无法onRollback对非 IO 操作进行操作。但这可能完全没问题。

于 2016-05-29T16:00:38.383 回答