仆人处理程序中的计算

bet*_*eta 6 haskell monad-transformers servant

仆人服务器处理程序是 , 的新类型包装器ExceptT,并且具有MonadThrow, MonadCatch,MonadError等的实例。

\n\n

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

\n\n

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

\n\n

我应该如何构造这段代码以确保尽早返回错误?

\n\n

我看到我可以使用Either\'sMonad实例将 \xe2\x80\x9ccollapse\xe2\x80\x9d 的前三个Either String Int计算放入 eg 中Either String (Int,Int,Int),然后将IO计算绑定到某个结果值,然后用于case决定是否返回成功结果或用于throwError抛出SomeError类型(转换后?),但我希望能够执行以下操作:

\n\n
f, g, h :: Either String Int\na :: Int -> Int -> Int -> IO (Either SomeError Text) \n\nmyHandler :: Handler Text\nmyHandler = do\n    x1 <- f\n    x2 <- g\n    x3 <- h\n    liftIO $ convertError $ (a x1 x2 x3)\n
Run Code Online (Sandbox Code Playgroud)\n\n

是否可以像上面的代码那样写?

\n

hne*_*atl 2

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

  • liftEitherEither String Ints 变成ExceptT Strings。
  • withExceptT根据 的要求从 转换ExceptT String为。ExceptT ServantErrHandler
x1 <- withExceptT strToServantErr $ liftEither f
Run Code Online (Sandbox Code Playgroud)

当您执行此操作三次时,我们可以使用以下命令使其更简洁mapM

[x1, x2, x3] <- mapM (withExceptT strToServantErr . liftEither) [f, g, h]
Run Code Online (Sandbox Code Playgroud)

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

result <- liftIO $ a x1 x2 x3
withExceptT someErrorToServantErr $ liftEither result
Run Code Online (Sandbox Code Playgroud)

我们解开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
Run Code Online (Sandbox Code Playgroud)

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


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

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
Run Code Online (Sandbox Code Playgroud)

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

  • 您可以更进一步,在 exceptT monad(或类似的东西)中使用自定义错误类型并使用 `hoistServer` (请参阅[此处](https://haskell-servant.readthedocs.io/en/stable/tutorial/Server .html#using-another-monad-for-your-handlers))。 (2认同)