Haskell 使用“do表示法”处理错误?

Haskell 使用“do表示法”处理错误?,haskell,error-handling,Haskell,Error Handling,鉴于数据结构: 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 以及添加两个或的方法 add :: Either CustomError Int -> E

鉴于数据结构:

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
以及添加两个
的方法

add :: Either CustomError Int -> Either CustomError Int -> Either CustomError Int
add e1 e2 = (+) <$> e1 <*> e2
运行它会显示:

λ: 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
fWithRecovery::IO(自定义错误Int)
fWithRecovery=do
x fmap右恢复foo
(左箭头)->fmap右恢复条
recoverFoo::IO Int
recoverFoo=do

_您看过使用软件事务性内存或STM吗?不相关:嵌套在
IO
中的
可能更好地表示为
ExceptT
monad transformer。Hi@bheklir-我没有看过STM。你能告诉我如何将其应用到这个例子中吗?@bheklir:这个想法是使用
TVar
s来收集这两个结果,然后将它们提交到数据库或完全回滚?那么,仅仅使
foo
bar
纯粹是不够的吗?我还想知道“调用
foo
将数据持久化到数据库”是否意味着可以使用数据库的事务控制。您是否可以将a放入monad transformer堆栈并使用
tell
累积回滚操作?
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