6

servant-server Handler是一个新类型的包装器,具有, ,等ExceptT的实例。MonadThrowMonadCatchMonadError

这可能是一个有点做作的例子,但它显示了我经常面临的一个问题:

在处理程序中,我想调用三个返回的函数Either String Int,然后执行 type 的计算,从之前Int -> Int -> Int -> IO (Either SomeError Text)获取三个Ints。

我应该如何构建此代码以确保尽早返回错误?

我看到我可以使用Either'Monad实例将前三个Either String Int计算“折叠”到 egEither String (Int,Int,Int)中,然后将IO计算绑定到某个结果值,然后使用case来决定是返回成功结果还是使用throwError抛出SomeError类型(在转换后?),但我希望能够做到以下几点:

f, g, h :: Either String Int
a :: Int -> Int -> Int -> IO (Either SomeError Text) 

myHandler :: Handler Text
myHandler = do
    x1 <- f
    x2 <- g
    x3 <- h
    liftIO $ convertError $ (a x1 x2 x3)

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

4

2 回答 2

2

我相信 hnefatl 的回答中缺少 Handler 构造(假设该问题是关于 Servant 0.15 及左右的问题)。注意

newtype Handler a = Handler { runHandler' :: ExceptT ServantErr IO a }

这就是我必须做的

eitherToHandler :: (e -> ServantErr) -> Either e a -> Handler a
eitherToHandler f = Handler . withExceptT f . liftEither
于 2022-01-19T18:56:18.860 回答
2

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

  • liftEitherEither String Ints 变成ExceptT Strings。
  • withExceptT根据 的要求从 转换ExceptT String为。ExceptT ServantErrHandler
x1 <- withExceptT strToServantErr $ liftEither f

当您这样做三遍时,我们可以使用以下方法使其更整洁mapM

[x1, x2, x3] <- mapM (withExceptT strToServantErr . liftEither) [f, g, h]

现在我们已经对参数进行了排序,我们可以使用相同的想法来修复返回。convertError将您的函数重命名someErrorToServantErr为统一并假设它具有 type SomeError -> ServantErr,那么我们可以这样做:

result <- liftIO $ a x1 x2 x3
withExceptT someErrorToServantErr $ liftEither result

我们解开 的IO计算a,然后将其提升为ExceptT并转换异常类型。

在将一些代码整理成一个辅助函数之后,这给了我们类似的东西:

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

这将尽快失败,并根据需要转换错误,虽然它很密集,但希望不是那么难以理解。


你也可以走 Applicative 路线,虽然我找不到让它特别好的方法(虽然我没有使用过 applicative functors,但我可能错过了一些有用的技巧):

myHandler :: Handler Text
myHandler = do
    let [x1, x2, x3] = map (liftMapE strToServantErr) [f, g, h] -- [Handler Int]
    tmp <- a <$> x1 <*> x2 <*> x3 -- IO (Either SomeError Text)
    eitherResult <- liftIO $ tmp
    liftMapE someErrorToServantErr eitherResult
  where liftMapE f = withExceptT f . liftEither

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

于 2018-03-28T09:46:11.603 回答