我想在servant 0.5中构建一个rest api的简单示例:
data MyData = MyData { var1 :: Int, var2 :: String }
app :: Application
app = serve api server
api :: Proxy API
api = Proxy
server :: Server API
server = getItems
getItems :: EitherT ServantErr IO [MyData]
getItems = runEitherT $ do
aa <- nextRandom -- IO
bb <- getCurrentTime -- IO
cc <- getDataFromDb -- IO
--noteT ??? How???
--MaybeT ??? How???
return $ Just [MyData 111 222]
startApp :: IO ()
startApp = run 8080 app
由于不同地方有很多“无法匹配预期类型”的错误,我无法编译它。我想这是因为我在“getItems”中混合了两个不同的单子(monad)。但不仅如此。
最佳答案
这里:
getItems :: ExceptT ServantErr IO [MyData]
getItems = runExceptT $ do
什么runExceptT
确实是从 ExceptT ServantErr IO [MyData]
到 IO (Either ServantErr [MyData]
)。它消除了 ExceptT
新类型。但是你想要走另一条路!
您可以使用liftIO
将任何 IO a
操作提升为 ExceptT ServantErr IO a
操作。它基本上告诉 ExceptT
包装器“将 IO
操作的结果放入成功上下文中”。
由于您的整个 do-block 似乎都位于 IO
中,因此您可以编写:
getItems :: ExceptT ServantErr IO [MyData]
getItems = liftIO $ do
aa <- nextRandom -- IO
bb <- getCurrentTime -- IO
cc <- getDataFromDb -- IO
...
而不是单独提升每个 IO
操作。
其他常见情况:
如果您有纯
Either
,请使用hoistEither :: Monad m => Either e a -> ExceptT e m a
将其提升到ExceptT
中。如果您有纯粹的
也许
,请使用failWith :: Applicative m => e -> Maybe a -> ExceptT e m a
并提供错误。如果您有一个
IO(也许是一个)
,请使用failWithM :: Applicative m => e -> m (Maybe a) -> ExceptT e m a
并提供错误。如果您有一个
IO (Either e a)
,只需将其包装在ExceptT
构造函数中即可。要更改
ExceptT
携带的错误类型,请使用withExcept :: (e -> e') -> Except e a -> Except e' a
.
所有这些函数都非常简单,查看它们的源代码很有启发性。
关于rest - 一个servant或 "how to mix monads properly"中rest api的简单示例?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36218369/