Rollback IO Actions

I have a very simple abstraction for processing a sequence of I / O operations that can be discarded (to some extent), that is, if the action writes a file, then the rollback will delete this file or if the action creates a tree directory, trimming it will be a rollback, etc. .

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

This is pretty much what I want, but I have a strong hunch that there are more complex and more reliable (in the sense of handling exceptions) ways to do this. So my question is: can I use, say, a well-known monad transformer from some library to implement the same idea?

Sort of

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

will be more convenient than the current solution.

+4
source share
1 answer

WriterT 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. .

+1

All Articles