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.
source
share