3

I wrote a function

app :: Request -> H.Session H.Postgres IO Response

which accepts web requests and builds responses (consulting the database as needed). To actually send out the responses I made a wrapper

runApp :: H.Postgres -> H.SessionSettings -> Application
runApp pg sess req respond =
  respond =<< H.session pg sess (app req)

I pass this function to Warp’s runSettings to loop forever and handle requests:

runSettings appSettings $ runApp pgSettings sessSettings

However this is really bad because it creates a new session for every request which defeats the purpose of the connection pool and prepared statements.

I would like to call runSettings inside H.session rather than the other way around. However runSettings has a signature Settings -> Application -> IO () and once inside IO I have lost access to the session. Is there a way to get back inside Session b m r?

This is a repost of a question from a private email.

Nikita Volkov
  • 42,792
  • 11
  • 94
  • 169

1 Answers1

3

Yes, in your example you create a new session for every request, which is unacceptable.

First of all, Session is just and alias to the reader monad transformer, which gives you a direct access to the pool. So you can always do:

session postgresSettings sessionSettings $ do
  -- session' :: H.Session b m r -> m r
  session' <- flip runReaderT <$> ask
  let runApp request respond = 
        respond =<< session' (app request)
  liftIO $ do
    -- run warp here 

Secondly, ReaderT has a MonadBaseControl instance, which is intended for similar patterns.

Nikita Volkov
  • 42,792
  • 11
  • 94
  • 169