use*_*184 5 haskell transactions atomic
我想编写可能失败的操作,但有一种方法可以回滚.
例如 - 预订酒店房间的外部电话,以及向信用卡收取的外部电话.这两个电话都可能失败,例如没有房间,信用卡无效.两者都有办法回滚 - 取消酒店房间,取消信用卡收费.
STM
.我觉得你可以编写一个monad Atomic T
来跟踪这些操作,如果有异常则将它们回滚.
编辑:
这些操作可以是IO
操作.如果操作只是内存操作,正如两个答案所示,STM就足够了.
例如,通过HTTP请求预订酒店.数据库操作,例如通过套接字通信插入记录.
在现实世界中,对于不可逆转的操作,在操作完成之前有一段宽限期 - 例如,信用卡付款和酒店预订可以在当天结束时结算,因此在此之前可以取消.
这正是STM的目的.组成动作以便它们自动地一起成功或失败.
与您的酒店房间问题非常相似的是Simon Peyton-Jones在"Beautiful Code"中的章节中的银行交易示例:http://research.microsoft.com/en-us/um/people/simonpj/papers/stm/beautiful. PDF格式
如果您需要使用自己的monad,它将看起来像这样:
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)
Run Code Online (Sandbox Code Playgroud)
(你可能也想要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
Run Code Online (Sandbox Code Playgroud)
然后像这样运行它们:
runRollbackable
$ do price <- rollbackableReserveRoom roomRequirements when
paymentRef <- rollbackableChargeCreditCard ccno price
-- etc
Run Code Online (Sandbox Code Playgroud)