Использование маршрутов Servant.Generic с ReaderT (подключение к пулу) IO

Я использовал servant-generic-0.1.0.3 и servant-server-0.13.0.1, чтобы сделать следующее:

data Site route = Site
  { page :: route :-
      "page" :> Capture "x" Int :> Capture "y" Int :> Get '[JSON] [Int]
  , home :: route :-
      Raw
  } deriving (Generic)

type API = ToServant (Site AsApi)

siteServer :: Pool Connection -> Site AsServer
siteServer pool = Site
  { page = \x y ->
      liftIO $ withResource pool $ \conn -> someDbFunction conn x y
  , home = serveDirectoryWebApp "static"
  }

api :: Proxy API
api = Proxy

app :: Pool Connection -> Application
app pool = serve api (toServant $ siteServer pool)

Это работало нормально, затем я попытался использовать ReaderT, чтобы избежать передачи Pool Connection в siteServer, поэтому я добавил AppM и заменил siteServer следующим образом:

type AppM = ReaderT (Pool Connection) IO

siteServer :: ServerT API AppM
siteServer = Site
  { page = do
      pool <- ask
      \x y ->
        liftIO $ withResource pool $ \conn -> someDbFunction conn x y
  , home = serveDirectoryWebApp "static"
  }

но я получил кучу ошибок, когда пытался его скомпилировать.

Я выполнил те же действия, что и в поваренной книге серванта., но мне не удалось заставить это работать с общими маршрутами, хотя это работает при использовании обычных маршрутов.

Я пропустил что-то, что могло бы заставить эту работу работать?


person Gaith    schedule 22.07.2018    source источник
comment
Не отвечает на ваш вопрос, но вы можете попробовать посмотреть сервер в этом шаблоне проекта: github.com/Holmusk/three-layer/blob/master/src/Lib/Server.hs Кроме того, servant-generic был объединен с servant-0.14, а API немного изменился. Так что помните об этом, когда будете поднимать servant версию.   -  person Shersh    schedule 22.07.2018


Ответы (1)


По крайней мере, для маршрутов в стиле записи, поддерживаемых serve-* >= 0.14 (см. здесь), если вы хотите работать с монадой, отличной от Handler, вам следует взглянуть на AsServerT и genericServerT.

Применительно к вашему примеру это означает, что siteServer должно быть определено следующим образом (без проверки типов, но должно быть очень близко к правильному).

siteServer :: Site (AsServerT AppM)
siteServer = Site
  { page = ... something in AppM ...
  , home = ... something in AppM ...
  }

-- turning that into a usual chain of :<|>-separated handlers
oldStyleServer :: ServerT API AppM
oldStyleServer = genericServerT siteServer

-- bringing it all back in Handler
oldStyleServerInHandler :: Pool Connection -> Server API -- same as ServerT API Handler
oldStyleServerInHandler conns = hoistServer (Proxy @API) appToHandler oldStyleServer
  where appToHandler = liftIO . flip runReaderT conns
        -- or something along those lines

-- serving it
app :: Pool Connection -> Application
app conns = serve (Proxy @API) (oldStyleServerInHandler conns)

Изменить: Поскольку вы используете serve-* ‹ 0.14 с serve-generic, вы должны заменить genericServerT на toServant.

person Alp Mestanogullari    schedule 22.07.2018
comment
Спасибо, это работает. Мне просто нужно было сделать page функцией верхнего уровня и указать ее сигнатуру типа, чтобы избежать неоднозначных ошибок типа. Знаете ли вы, можно ли избежать превращения ее в функцию верхнего уровня? Также мне пришлось добавить conns в качестве параметра для app и передать его в oldStyleServerInHandler, и я заменил genericServerT на toServant, чтобы он работал с servant-server-0.13.0.1. home по-прежнему не работает, потому что возвращает Server Raw (но это может быть предметом другого вопроса). - person Gaith; 23.07.2018
comment
Для неоднозначных типов вам, вероятно, нужно где-то дать аннотацию типа, чтобы GHC знал монаду раз и навсегда. Для conns да, извините, я забыл сделать это аргументом app. Для home просто попробуйте вызвать Data.Tagged.retag в обработчике, который обслуживает файлы, чтобы он был помечен вашей монадой вместо Handler. Или используйте servant >= 0.14, где утилиты обслуживания файлов сделаны полиморфными в монаде. - person Alp Mestanogullari; 23.07.2018