5

失敗する可能性のある操作を作成したいのですが、ロールバックする方法があります。

たとえば、ホテルの部屋を予約するための外部呼び出し、およびクレジットカードに請求するための外部呼び出し。部屋が残っていない、クレジットカードが無効であるなど、これらの通話は両方とも失敗する可能性があります。どちらにもロールバックする方法があります。ホテルの部屋をキャンセルし、クレジット料金をキャンセルします。

  1. このタイプの(実際ではない)アトミックの名前はありますか?haskellトランザクションを検索するたびに、が表示されますSTM
  2. 抽象化、それらを構成する方法、またはhaskellや他の言語のライブラリはありますか?

Atomic Tこれらの操作を追跡し、例外がある場合はロールバックするモナドを作成できると思います。

編集:

これらの操作は操作である可能性がありIOます。2つの答えが示唆するように、操作がメモリ操作のみである場合、STMで十分です。

たとえば、ホテルの予約はHTTPリクエストを介して行われます。ソケット通信を介したレコードの挿入などのデータベース操作。

現実の世界では、不可逆的な操作の場合、操作が行われる前に猶予期間があります。たとえば、クレジットカードの支払いやホテルの予約は、1日の終わりに決済される場合があるため、それまでにキャンセルしても問題ありません。

4

4 に答える 4

6

これがまさにSTMの目的です。アクションは、自動的に一緒に成功または失敗するように構成されています。

ホテルの部屋の問題と非常によく似ているのは、「BeautifulCode」のSimon Peyton-Jonesの章の銀行取引の例です:http://research.microsoft.com/en-us/um/people/simonpj/papers/stm/beautiful。 pdf

于 2012-07-12T18:38:19.537 に答える
5

独自のモナドを作成する必要がある場合は、次のようになります。

import Control.Exception (onException, throwIO)

newtype Rollbackable a = Rollbackable (IO (IO (), a))

runRollbackable :: Rollbackable a -> IO a
runRollbackable (Rollbackable m) = fmap snd m
    -- you might want this to catch exceptions and return IO (Either SomeException a) instead

instance Monad Rollbackable where
    return x = Rollbackable $ return (return (), x)
    Rollbackable m >>= f
       = do (rollback, x) <- m
            Rollbackable (f x `onException` rollback)

Functor(おそらくインスタンスも必要になるでしょうApplicativeが、それらは些細なことです。)

ロールバック可能なプリミティブアクションを次のように定義します。

rollbackableChargeCreditCard :: CardNumber -> CurrencyAmount -> Rollbackable CCTransactionRef
rollbackableChargeCreditCard ccno amount = Rollbackable
   $ do ref <- ioChargeCreditCard ccno amount
        return (ioUnchargeCreditCard ref, ref)

ioChargeCreditCard :: CardNumber -> CurrencyAmount -> IO CCTransactionRef
-- use throwIO on failure
ioUnchargeCreditCard :: CCTransactionRef -> IO ()
-- these both just do ordinary i/o

次に、次のように実行します。

runRollbackable
   $ do price <- rollbackableReserveRoom roomRequirements when
        paymentRef <- rollbackableChargeCreditCard ccno price
        -- etc
于 2012-07-12T22:39:00.390 に答える
1

あなたの計算がTVar同じようなものでのみ行うことができれば、それSTMは完璧です。

副作用(「Bob $ 100の請求」など)が必要で、後でエラーが発生した場合(「Bob $ 100の払い戻し」など)に撤回を発行する必要がある場合は、ドラムロールしてください:Control.Exceptions.bracketOnError

bracketOnError
        :: IO a         -- ^ computation to run first (\"acquire resource\")
        -> (a -> IO b)  -- ^ computation to run last (\"release resource\")
        -> (a -> IO c)  -- ^ computation to run in-between
        -> IO c         -- returns the value from the in-between computation

と同様Control.Exception.bracketですが、中間の計算によって発生した例外があった場合にのみ、最終アクションを実行します。

したがって、私はこれを次のように使用することを想像できます:

let safe'charge'Bob = bracketOnError (charge'Bob) (\a -> refund'Bob)

safe'charge'Bob $ \a -> do
   rest'of'transaction
   which'may'throw'error

Control.Exception.maskマルチスレッドプログラムを使用している場合は、操作の使用場所を理解し、次のようなことを試してください。

また、GHCでこれがどのように行われるかを確認するには、 Control.ExceptionおよびControl.Exception.Baseのソースコードを読むことができ、読む必要があることを強調する必要があります。

于 2012-07-12T19:37:14.437 に答える
0

あなたは本当にSTMの巧妙なアプリケーションでこれを行うことができます。重要なのは、IOパーツを分離することです。問題は、トランザクションが最初は成功し、後で失敗するように見える可能性があることだと思います。(失敗をすぐに、またはすぐに認識できる場合は、状況はより単純です):

main = do
   r   <- reserveHotel
   c   <- chargeCreditCard

   let room         = newTVar r
       card         = newTVar c
       transFailure = newEmptyTMVar

   rollback <- forkIO $ do 
       a <- atomically $ takeTMVar transFailure --blocks until we put something here
       case a of
         Left  "No Room"       -> allFullRollback
         Right "Card declined" -> badCardRollback

  failure <- listenForFailure -- A hypothetical IO action that blocks, waiting for
                              -- a failure message or an "all clear"
  case failures of
      "No Room"       -> atomically $ putTMVar (Left "No Room")
      "Card Declined" -> atomically $ putTMVar (Right "Card declined")
      _               -> return ()

さて、ここではMVarsが処理できなかったものは何もありません。私たちが行っているのは、スレッドをフォークして待機し、問題を修正する必要があるかどうかを確認することだけです。しかし、おそらくあなたはあなたのカード料金とホテルの予約で他のことをしているでしょう...

于 2012-07-12T23:17:16.417 に答える