抛出自定义单子堆栈中的错误。

huangapple go评论55阅读模式
英文:

servant throwError with custom monad stack

问题

给定一个类似这样的单子:

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

newtype AppEnv = AppEnv -- 应用程序的环境

我想要使用 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 派生这样的实例?我需要改变单子堆栈吗?

我可以使用 throw,但这会改变 servant-client 的行为,我想避免这种情况。

英文:

given a monad like this:


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

I'd like to use the standard error handling from Servant using throwError:

foo :: App ()
foo = throwError err404

which doesn't compile

    • 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

and I can't find a way to make that work. Can I derive such an instance for App? Do I need to change the monad stack?

I could use throw, but that does change the behavior of servant-client, which I want to avoid.

答案1

得分: 2

If we want to throwError values of type ServerError, our monad needs to be an instance of MonadError ServerError. Looking at the available instances, we can get an idea of what we would need to add to our monad stack.

这段意思是如果我们想要抛出类型为 ServerError 的错误值,我们的 Monad 需要是 MonadError ServerError 的实例。查看可用实例,我们可以了解需要在 Monad 堆栈中添加什么内容。

These instances won't work because they are for specific error types different from ServerError:

这些实例不适用,因为它们是针对与 ServerError 不同的特定错误类型的:

MonadError IOException IO
MonadError () Maybe

This instance would force us to use Either as our base monad:

这个实例将强制我们使用 Either 作为基本 Monad:

MonadError e (Either e)

Then there are a whole bunch of "passthrough" instances that propagate the MonadError constraint from the base monad, but don't introduce it:

然后还有一堆“传递”的实例,它们从基本 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)
...

This is the instance we need:

这就是我们需要的实例:

Monad m => MonadError e (ExceptT e m)

We need to add an ExceptT ServerError transformer somewhere in our monad stack, and newtype derive MonadError ServerError.

我们需要在我们的 Monad 堆栈中的某个地方添加一个 ExceptT ServerError 转换器,并使用 newtype 派生 MonadError ServerError

An annoyance with this solution is that introducing ExceptT might make operations like catch (for runtime exceptions) more difficult to perform.

这个解决方案的一个不便之处是,引入 ExceptT 可能会使像 catch(用于运行时异常)等操作更难执行。

英文:

If we want to throwError values of type ServerError, our monad needs to be an instance of MonadError ServerError. Looking at the available instances, we can get an idea of what we would need to add to our monad stack.

These instances won't work because they are for specific error types different from ServerError:

MonadError IOException IO
MonadError () Maybe

This instance would force us to use Either as our base monad:

MonadError e (Either e)

Then there are a whole bunch of "passthrough" instances that propagate the MonadError constraint from the base monad, but don't introduce it:

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

This is the instance we need:

Monad m => MonadError e (ExceptT e m)

We need to add an ExceptT ServerError transformer somewhere in our monad stack, and newtype derive MonadError ServerError.

An annoyance with this solution is that introducing ExceptT might make operations like catch (for runtime exceptions) more difficult to perform.

huangapple
  • 本文由 发表于 2023年5月10日 16:32:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/76216402.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定