【问题标题】:rollback IO action回滚 IO 动作
【发布时间】:2016-09-27 09:28:44
【问题描述】:

我有一个非常简单的抽象来处理可以回滚(在某种程度上)的一系列 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
  ....

会比目前的解决方案更方便。

【问题讨论】:

  • 像这样的 monad 看起来类似于 ResourceT:“一个修改后的 ReaderT monad 转换器,持有对所有仍待执行的释放操作的可变引用”。不同之处在于,只有在出现错误的情况下才会执行清理操作。鉴于ResourceT 允许用户访问其internal state,也许它可以作为RollbackT 实现的基础。

标签: haskell io-monad


【解决方案1】:

这看起来很像 WriterT monad 与 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

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

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

【讨论】:

  • 一旦成功就删除回滚操作似乎对这种方法很烦人;你能简单评论一下如何做到这一点吗?
  • 我不明白;如果计算成功,则忽略回滚列表。
  • tell [rollaction] 不应该在 Left case 中吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多