servant-server Handler是ExceptT
上的新类型包装器,包含MonadThrow
,MonadCatch
,MonadError
等的实例。
这可能是一个有点人为的例子,但它显示了我经常遇到的问题:
在一个处理程序中,我想调用三个返回Either String Int
的函数,然后执行类型Int -> Int -> Int -> IO (Either SomeError Text)
的计算,从中获取三个Int
。
我应该如何构造此代码以确保尽早返回错误?
我发现我可以使用Either
的{{1}}个实例将前三个Monad
计算“折叠”为例如Either String Int
,然后将Either String (Int,Int,Int)
计算绑定到某个结果值,然后使用IO
来决定是返回成功结果还是使用case
抛出throwError
}类型(转换后?),但我希望能够得到以下内容:
SomeError
是否可以将其编写为类似于上面的代码?
答案 0 :(得分:1)
假设您有一个函数strToServantErr :: String -> ServantErr
用于将f,g,h
返回的错误转换为处理程序可以返回的错误,那么我们可以使用:
liftEither
将Either String Int
转换为ExceptT String
s。withExceptT
根据ExceptT String
的要求将ExceptT ServantErr
转换为Handler
。x1 <- withExceptT strToServantErr $ liftEither f
当你三次这样做时,我们可以使用mapM
使它更整洁:
[x1, x2, x3] <- mapM (withExceptT strToServantErr . liftEither) [f, g, h]
现在我们已经对参数进行了排序,我们可以使用相同的想法来修复返回。将convertError
函数重命名为someErrorToServantErr
以保持一致性并假设其类型为SomeError -> ServantErr
,我们可以这样做:
result <- liftIO $ a x1 x2 x3
withExceptT someErrorToServantErr $ liftEither result
我们打开IO
的{{1}}计算,然后将其提升到a
并转换异常类型。
在将一些代码整理成辅助函数之后,这给了我们类似的东西:
ExceptT
ASAP会根据需要尽快转换错误,虽然它很密集,但希望不是所有这些都不可读。
你也可以去申请路线,虽然我找不到让它变得特别好的方法(尽管我还没有使用过应用程序,我可能会错过一些有用的技巧) :
myHandler :: Handler Text
myHandler = do
[x1, x2, x3] <- mapM (liftMapE strToServantErr) [f, g, h]
eitherResult <- liftIO $ a x1 x2 x3
liftMapE someErrorToServantErr eitherResult
where liftMapE f = withExceptT f . liftEither
欢迎对上述代码进行任何改进!