从 IO Maybe a 转换为 EitherT IO a

Convert from IO Maybe a to EitherT IO a

我正在为 API 使用 servant 库。它运行在:EitherT (Int, String) IO a monad 中。我有一个类型为 IO Maybe a 的函数,我想使用它。

这是一个有效的例子:

sourcesGetAll :: EitherT (Int, String) IO [Source]
sourcesGetAll = liftIO $ sourcesList h

sourcesList :: IO [Source]

但现在我想同时使用这两个功能

sourcesFind :: IO (Maybe Source)
sourcesGetOne :: EitherT (Int, String) IO Source
sourcesGetOne = ???

我想这样做:

maybeNotFound :: Maybe a -> Either (Int, String) a
maybeNotFound Nothing = Left (404, "Not Found")
maybeNotFound Just a  = Right a

如何使用所有花哨的 monad 来做到这一点?

您可以使用 hoistEither :: Monad m => Either a b -> EitherT a m b 来实现:

maybeNotFoundT :: IO (Maybe a) -> EitherT (Int, String) IO a
maybeNotFoundT maAct = do
  ma <- liftIO maAct -- get the Maybe a. Now ma :: Maybe a
  hoistEither $ maybeNotFound ma

您可以将其分解为两个独立的问题:

  • IO (Maybe a)转化为MaybeT IO a

  • MaybeT IO a转化为EitherT (Int, String) a

第一个使用MaybeT构造函数解决:

MaybeT :: IO (Maybe a) -> MaybeT IO a

第二个是使用 errors 库中的 noteT 解决的:

noteT :: Monad m => a -> MaybeT m b -> EitherT a m b

感谢 freenode 上的 mpickering 的另一个答案:

sourcesGetOne = EitherT $ maybeNotFound <$> sourcesFind

So the question is how to write a function.. IO (Maybe a) -> EitherT (Int, String) IO a Given a function f :: Maybe a -> Either (Int, String) a, then one way is..

myFunc action = EitherT (f <$> action)

If you look at the docs for EitherT -- https://hackage.haskell.org/package/either-4.3.3.2/docs/Control-Monad-Trans-Either.html. Then you'll see that EitherT (Int, String) IO a, is actually just a wrapped up IO (Either (Int, String) a)