haskell servant throw自定义monad堆栈出错

odopli94  于 2023-05-18  发布在  Vant
关注(0)|答案(1)|浏览(118)

给定一个monad如下:

newtype App a = App
  { runApp :: ReaderT AppEnv (LoggingT IO) a
  }
  deriving newtype
    ( Functor,
      Applicative,
      Monad,
      MonadIO, 
      MonadReader AppEnv,
      MonadLogger
    )

newtype AppEnv = AppEnv -- environment for the app

我想使用Servant中的标准错误处理,使用throwError

foo :: App ()
foo = throwError err404

编译不了

• No instance for (Control.Monad.Error.Class.MonadError
                         ServerError App)
        arising from a use of ‘throwError’
    • In the expression: throwError err404
      In an equation for ‘foo’: foo = throwError err404

但我找不到一个方法来实现我可以为App派生这样的示例吗?我是否需要更改monad堆栈?
我可以使用throw,但这确实会改变servant-client的行为,这是我希望避免的。

kgqe7b3p

kgqe7b3p1#

如果我们想要ServerError类型的throwError值,我们的monad需要是MonadError ServerError的示例。看看available instances,我们可以知道我们需要向monad堆栈添加什么。
这些示例将无法工作,因为它们用于与ServerError不同的特定错误类型:

MonadError IOException IO
MonadError () Maybe

这个示例将迫使我们使用Either作为我们的基本monad:

MonadError e (Either e)

然后有一大堆“passthrough”示例从基本monad传播MonadError约束,但不引入它:

MonadError e m => MonadError e (IdentityT m)
MonadError e m => MonadError e (ReaderT r m)
MonadError e m => MonadError e (StateT s m)
...

这是我们需要的示例:

Monad m => MonadError e (ExceptT e m)

我们需要在monad堆栈中的某个地方添加一个ExceptT ServerError转换器,并通过newtype派生MonadError ServerError
这种解决方案的一个缺点是,引入ExceptT可能会使像catch(用于运行时异常)这样的操作更难执行。

相关问题