英文:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论