0

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:

  • what are the actual types of enter runApp (not what ghci will give me, but a more descriptive explanation)?
  • where is that (IO ResponseReceived) constraint coming from?
  • how do I tweak the above code to work such that the entire handler gets passed through the natural transformation?
Savanni D'Gerinel
  • 2,379
  • 17
  • 27
  • 1
    How does the `Routes` look like? I'm not sure if this was resolved in the new versions of servant, but you couldn't have a `Raw` endpoint in the routes; if you did, the result was a weird type error. – ondra Jan 21 '17 at 18:51
  • Turns out that is the correct answer! Could you upgrade your reply to an answer so I can give you credit? – Savanni D'Gerinel Jan 21 '17 at 18:52

1 Answers1

4

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
ondra
  • 9,122
  • 1
  • 25
  • 34