鉴于数据结构:
data CustomError = FooError | BarError deriving Show
然后执行IO
的两个函数:
foo :: IO (Either CustomError Int)
foo = return $ Right 100
bar :: IO (Either CustomError Int)
bar = return $ Left BarError
添加两个Either
的方法。
add :: Either CustomError Int -> Either CustomError Int -> Either CustomError Int
add e1 e2 = (+) <$> e1 <*> e2
最后,该函数执行两个IO
操作,然后尝试将add
应用于其提取的Right
值:
f :: IO (Either CustomError Int)
f = do
x <- foo
y <- bar
return $ add x y
运行它显示:
λ: f
Left BarError
但是,假设调用foo
将数据持久存储到数据库中。如果foo
成功,但bar
失败,则会出现不规则状态。换句话说,我希望f
像交易一样运作 - 一切都成功或什么都没有。
我想做类似的事情:
fWithRecovery:: IO (Either CustomError Int)
fWithRecovery = do
x <- foo
y <- bar
case y of (Right _) -> return $ add x y
(Left FooError) -> fmap Right recoverFoo
(Left BarError) -> fmap Right recoverBar
recoverFoo :: IO Int
recoverFoo = do
_ <- undefined -- clean up DB
return 666 -- using an 'evil' value
-- Note - I know using this value is horrible, but
-- I'm using for this simple example
recoverBar :: IO Int
recoverBar = do
_ <- undefined -- clean up DB
return 42 -- Note - I know using this value is horrible, but
-- I'm using for this simple example
但是,我很好奇是否有一种惯用的方式来处理do notation
案例的回滚。