I'm trying to use the enter
function to allow me to run my API handlers with one set of exceptions that I'll translate to Servant at a high level, but I'm having troubles with a type match.
Given this minimal set of definitions:
-- server :: Config -> Server Routes
server :: Config -> ServerT Routes (ExceptT ServantErr IO)
server c = enter runApp (handlers c)
-- runApp :: AppM :~> Handler
runApp :: ExceptT AppErr IO :~> ExceptT ServantErr IO
runApp = Nat undefined
handlers :: Config -> ServerT Routes (ExceptT AppErr IO)
handlers = undefined
I end up with this type error:
Couldn't match type `IO' with `ExceptT ServantErr IO'
arising from a functional dependency between:
constraint `servant-0.7.1:Servant.Utils.Enter.Enter
(IO ResponseReceived)
(ExceptT AppErr IO :~> ExceptT ServantErr IO)
(IO ResponseReceived)'
arising from a use of `enter'
instance `servant-0.7.1:Servant.Utils.Enter.Enter
(m a) (m :~> n) (n a)'
at <no location info>
In the expression: enter runApp (handlers c)
In an equation for `server': server c = enter runApp (handlers c)
The exception comes from the line with the call to enter
. For reference, the declaration of enter
:
class Enter typ arg ret | typ arg -> ret, typ ret -> arg where
enter :: arg -> typ -> ret
instance Enter (m a) (m :~> n) (n a) where
-- enter :: (m :~> n) -> m a -> n a
So, when I call enter runApp
, I expect the types to go as such:
enter :: (m :~> n) -> m a -> n a
enter (runApp :: m ~ ExceptT AppErr IO :~> n ~ ExceptT ServantErr IO) :: ExceptT AppErr IO a -> ExceptT ServantErr IO a
(where above I used n ~ ExceptT ServantErr IO
to illustrate my type substitutions)
And in reality, I know from other code (that I have tried to mimic, and I don't understand where I went wrong), that enter runApp
should/must have this type:
enter runApp :: ServerT Routes (ExceptT AppErr IO a) -> ServerT Routes (ExceptT ServantErr IO a)
So, the questions are legion:
enter runApp
(not what ghci will give me, but a more descriptive explanation)?You cannot have a Raw
endpoint in the Api type. The way to do it is to get the Raw endpoint out of the handlers
and call it e.g.
server c = enter runApp (handlers c) :<|> handleRaw