一个简单的例子rest api in servant or "how to mix monads properly"?
A simple example of rest api in servant or "how to mix monads properly"?
我想在 servant 0.5 中构建一个休息 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
由于"Couldn't match expected type"在不同地方有很多错误,我无法编译它。我想这是因为我在 "getItems" 中混合了 2 个不同的单子。但不仅如此。
此处:
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
包装器 "just put the result of the IO
action in a success context".
因为你的整个 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
。
如果你有纯Maybe
,使用failWith :: Applicative m => e -> Maybe a -> ExceptT e m a
并提供错误。
如果您有 IO (Maybe a)
,请使用 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
.
所有这些函数都非常简单,查看它们的源代码很有启发性。
我想在 servant 0.5 中构建一个休息 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
由于"Couldn't match expected type"在不同地方有很多错误,我无法编译它。我想这是因为我在 "getItems" 中混合了 2 个不同的单子。但不仅如此。
此处:
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
包装器 "just put the result of the IO
action in a success context".
因为你的整个 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
。如果你有纯
Maybe
,使用failWith :: Applicative m => e -> Maybe a -> ExceptT e m a
并提供错误。如果您有
IO (Maybe a)
,请使用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
.
所有这些函数都非常简单,查看它们的源代码很有启发性。