【问题标题】:Using failWith with Servant and custom monad stack将 failWith 与 Servant 和自定义 monad 堆栈一起使用
【发布时间】:2016-05-29 21:40:35
【问题描述】:

我正在使用带有自定义 monad 堆栈的 Servant

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 =&gt; m (Maybe a) -&gt; e -&gt; 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)。我如何将这样的值转换回我的处理程序类型?

【问题讨论】:

    标签: haskell monad-transformers servant


    【解决方案1】:

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

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

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

    runDb (getCompanyByName companyName) !?? err400
    

    【讨论】:

    • 这行得通,但是,我有点困惑,我不得不为此编写自己的助手,我希望我可以使用现有的东西并进行组合(可能需要一些提升)。不写新的助手就不行吗?
    • @Bartosz,您可以在此处使用 Daniel 的代码打开问题或提交 pull request:github.com/Gabriel439/Haskell-Errors-Library/issues,这样以后每个人都不必编写自己的帮助程序了 :)
    【解决方案2】:

    通常要将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

    可能会有所帮助。

    【讨论】:

    • 是的,lift 是我的第一直觉,但是,编译器无法匹配 t0 (ExceptT ServantErr m0)AppHandler。 AppHandler 没有 MonadTrans 实例,我在派生/制作实例时遇到了麻烦,因为 AppHandler 的类型与 MonadTrans 的预期不符。
    • 我认为你可以只用一个type for AppHandler - 例如。 type AppHandler = ReaderT Config (ExceptT ServantErr IO)。不过你仍然需要使用!??
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-08-30
    • 2018-12-17
    • 1970-01-01
    • 2014-07-21
    • 1970-01-01
    • 1970-01-01
    • 2016-09-25
    相关资源
    最近更新 更多