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

それは私が望んでいることのほとんどを行いますが、それを行うには、より構成可能で信頼性の高い (例外処理の意味で) 方法があるという強い予感があります。私の質問は、たとえば、ライブラリの既知のモナド変換子を使用して、同じアイデアを実装できますか?

のようなものを持つ

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

現在のソリューションよりも便利です。

4

1 に答える 1

1

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アクションでは実行できないため、これは完全には構成されません。しかし、それはまったく問題ないかもしれません。

于 2016-05-29T16:00:38.383 に答える