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.
If we want to
throwErrorvalues of typeServerError, our monad needs to be an instance ofMonadError 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:This instance would force us to use
Eitheras our base monad:Then there are a whole bunch of "passthrough" instances that propagate the
MonadErrorconstraint from the base monad, but don't introduce it:This is the instance we need:
We need to add an
ExceptT ServerErrortransformer somewhere in our monad stack, and newtype deriveMonadError ServerError.An annoyance with this solution is that introducing
ExceptTmight make operations likecatch(for runtime exceptions) more difficult to perform.