3

我将Servant与自定义 monad 堆栈一起使用:

newtype AppHandler a = AppHandler { runHandler :: ReaderT Config (ExceptT ServantErr IO) a }
  deriving (Functor, Applicative, Monad, MonadReader Config, MonadError ServantErr, MonadIO)

data Config = Config
    { getPool :: ConnectionPool }

现在,在许多处理程序中,我只需要从 db 获取一些数据(持久)并对其采取行动,所以我得到了:

runDb :: (MonadReader Config m, MonadIO m) => SqlPersistT IO b -> m b
runDb query = do
  pool <- asks getPool
  liftIO $ runSqlPool query pool

事实证明,当从 db 获取时,您一定会使用Maybe,并且通常当 Maybe 是 时Nothing,您只想抛出错误,以便 Servant 服务器将其转换为正确的 HTTP 响应。这使我发现了Control.Error.Util(!?) :: Applicative m => m (Maybe a) -> e -> ExceptT e m a帮助者。所以我尝试了以下操作:

someHandler :: AppHandler NoContent
someHandler = do
  entity <- (runDb $ getCompanyByName companyName) !? err400
  -- some more logic
  return NoContent

但这不会编译,!?这里的结果是ExceptT ServantErr m0 (Entity SomeEntity)但我不再使用这种处理程序类型,它需要AppHandler (Entity SomeEntity). 我如何将这样的值转换回我的处理程序类型?

4

2 回答 2

3

你想要一个变体,(!?)它是多态的,它返回的 monad。例如:

(!??) :: MonadError e m => m (Maybe a) -> e -> m a
act !?? err = act >>= maybe (throwError err) return

然后,提供err400 :: ServantError- 这是您声明的错误类型AppHandler- 您将能够编写

runDb (getCompanyByName companyName) !?? err400
于 2016-05-29T23:13:09.747 回答
1

通常将类型的值转换m aReaderT r m a仅使用lift.

所以也许这对你有用:

entity <- lift $ (runDb $ getCompanyByName companyName) !? err400

如果整个(runDb ...) !? err400是一个ExceptT ServantError ...值。

另外,这个仆人问题讨论:

https://github.com/haskell-servant/servant/issues/286

可能会有所帮助。

于 2016-05-29T22:54:47.230 回答