Стиль приложений Haskell Servant и MTL - PullRequest
0 голосов
/ 15 сентября 2018

После того, как я посмотрел хорошие разговоры о Джордже Уилсоне ( Следующий уровень MTL https://github.com/gwils/next-level-mtl-with-classy-optics/blob/master/Slides.pdf) Я пытаюсь создать приложение, которое использует дизайн в стиле MTL и решил использовать слугу, похоже, что эта библиотека не 'не подходит для такого дизайна.
Ниже код не компилируется, потому что я не могу преобразовать m в обработчик.

getItems :: (MonadIO m, MonadReader r m, HasNetworkConfig r) => m [Item]
getItems =
   return [Item "foo" "bar"]

mkApp :: Application
mkApp = serve itemApi getItems

Вы можете найти полный пример здесь: https://github.com/paweln1986/ServantMTLStackOverflowExample

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

Ошибка компиляции:

   • No instance for (MonadReader r0 Handler)
    arising from a use of ‘getItems’
   • In the second argument of ‘serve’, namely ‘getItems’
     In the expression: serve itemApi getItems
     In an equation for ‘mkApp’: mkApp = serve itemApi getItems
   |
40 | mkApp = serve itemApi getItems
   |                       ^^^^^^^^

Короче пример:

type ReaderAPI = "ep1" :> Get '[JSON] Int :<|> "ep2" :> Get '[JSON] String    :<|> Raw :<|> EmptyAPI

readerApi = Proxy :: Proxy ReaderAPI

readerServer :: (MonadIO m, HasNetworkConfig r, MonadReader r m) => ServerT ReaderAPI (AppT m)
readerServer = return 1797 :<|> view (networkConfig . host) :<|> Tagged (error "raw server") :<|> emptyServer

nt x = return undefined

mainServer = hoistServer readerApi nt readerServer :: Server ReaderAPI

Это дает мне ниже ошибку компиляции

    • Ambiguous type variable ‘m0’ arising from a use of ‘readerServer’
  prevents the constraint ‘(MonadIO m0)’ from being solved.
  Probable fix: use a type annotation to specify what ‘m0’ should be.
  These potential instances exist:
    instance [safe] MonadIO IO -- Defined in ‘Control.Monad.IO.Class’
    instance MonadIO m => MonadIO (AppT m) -- Defined in ‘Types’
    instance MonadIO Handler
      -- Defined in ‘Servant.Server.Internal.Handler’
    ...plus 18 instances involving out-of-scope types
    (use -fprint-potential-instances to see them all)
• In the third argument of ‘hoistServer’, namely ‘readerServer’
  In the expression:
      hoistServer readerApi nt readerServer :: Server ReaderAPI
  In an equation for ‘mainServer’:
      mainServer
        = hoistServer readerApi nt readerServer :: Server ReaderAPI
   |
64 | mainServer = hoistServer readerApi nt readerServer :: Server ReaderAPI
   |                                       ^^^^^^^^^^^^

1 Ответ

0 голосов
/ 15 сентября 2018

Наконец мне удалось решить мою проблему.

run :: (MonadIO m, MonadReader r m, HasNetworkConfig r) => AppConfig -> m ()
run config = do
  serverPort <- view (networkConfig . port)
  let settings =
        setPort serverPort $
        setBeforeMainLoop (liftIO $ hPutStrLn stderr ("listening on port " ++ show serverPort)) defaultSettings
  liftIO $ runSettings settings (mainServer config)

printM :: (MonadIO m, Show a) => a -> m ()
printM a = liftIO $ print a

type ReaderAPI = "ep1" :> Get '[ JSON] String :<|> "ep2" :> Get '[ JSON] String :<|> Raw :<|> EmptyAPI

readerApi :: Proxy ReaderAPI
readerApi = Proxy :: Proxy ReaderAPI

fromConfig :: (Functor m, MonadReader r m, HasNetworkConfig r) => m String
fromConfig = view (networkConfig . host)

rawValue :: (Applicative m) => m String
rawValue = pure "1797"

readerServer :: (Monad m) => ServerT ReaderAPI (AppT m)
readerServer = rawValue :<|> fromConfig :<|> Tagged (error "raw server") :<|> emptyServer

nt :: AppConfig -> AppT IO x -> Handler x
nt config x = do
  res <- liftIO $ runExceptT $ runReaderT (runApp x) config
  case res of
    Left e       -> throwError e
    Right result -> return result

mainServer :: AppConfig -> Application
mainServer config = serve readerApi api
  where
    api = hoistServer readerApi (nt config) readerServer

Одна вещь, которую я пропустил, это правильное использование hoistServer.После того, как я создал правильное естественное преобразование из моей монады (AppT) в Handler, все скомпилировалось и работает как положено.

...