在servant处理程序中的计算

时间:2018-03-28 06:43:23

标签: haskell monad-transformers servant

servant-server HandlerExceptT上的新类型包装器,包含MonadThrowMonadCatchMonadError等的实例。

这可能是一个有点人为的例子,但它显示了我经常遇到的问题:

在一个处理程序中,我想调用三个返回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

是否可以将其编写为类似于上面的代码?

1 个答案:

答案 0 :(得分:1)

假设您有一个函数strToServantErr :: String -> ServantErr用于将f,g,h返回的错误转换为处理程序可以返回的错误,那么我们可以使用:

  • liftEitherEither 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

欢迎对上述代码进行任何改进!