Haskell仆人和MTL应用程序样式



在我看了乔治·威尔逊(下一级MTLhttps://github.com/gwils/next-level-mtl-with-classy-optics/blob/master/Slides.pdf(的好演讲后,我尝试创建使用MTL样式设计的应用程序并决定使用servant,看起来这个库不适合这样的设计。
下面的代码无法编译,因为我无法将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

是否可以将任何单子与仆人一起使用?如何实现这一点?我尝试使用提升机没有成功。你知道我在这里错过了什么吗?

编译错误:

• 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
|                                       ^^^^^^^^^^^^

最后我设法解决了我的问题。

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。在我创建了从 monad (AppT( 到 Handler 的正确自然转换后,所有内容都按预期编译和工作。

最新更新