我正在使用 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中获取一些数据(Persistent)并对其进行操作,所以我得到了:
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)
。我如何将这样的值转换回我的处理程序类型?
答案 0 :(得分: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
答案 1 :(得分:1)
通常,要将m a
类型的值转换为ReaderT r m a
,只需使用lift
。
所以这可能适合你:
entity <- lift $ (runDb $ getCompanyByName companyName) !? err400
如果整个(runDb ...) !? err400
是ExceptT ServantError ...
值。
此外,这个仆人问题讨论:
https://github.com/haskell-servant/servant/issues/286
可能会有所帮助。