使用不同类型的 Monad Transformer 的策略是什么?
What is the strategy for working with different types of Monad Transformers?
我正在尝试实现一个简单的 Web 服务器,它与其他 API 交互并在进行一些处理后存储响应。
为了封装失败的可能性(空响应、不正确的请求等),我使用 ExceptT
如下:
getExample
:: (MonadIO m, MonadReader ApplicationConfig m)
=> ExceptT ApplicationError m [Example]
getExample = do
partOfReq <- asks requestForSometing
fn1 =<< fn2 partOfReq
我还有另一个函数,使用来自 Persistent 的 insertMany_ 将响应存储在数据库中。
storeExample
:: ( MonadIO m
, PersistStoreWrite backend
, PersistEntityBackend Example ~ BaseBackend backend
)
=> [Example]
-> ReaderT backend m ()
storeExample = insertMany_
现在我想写一个函数
getResponseAndStore = ... {- A combination of getExample and storeExample -}
这将完成这两件事,并将 ApplicationConfig
和 PersistEntityBackend
要求提升到顶部,用户可以在其中捆绑提供它们。
可以吗?
如果是这样 - strategy/implementation 会是什么?
如果否 - 我应该考虑哪些改变?
编辑:这就是我目前正在做的事情。
getResponseAndStore
:: ( MonadIO m
, MonadReader ApplicationConfig m
, PersistStoreWrite backend
, PersistEntityBackend Example ~ BaseBackend backend
)
=> ReaderT backend (ExceptT ApplicationError m) ()
getResponseAndStore = storeExample =<< lift getExample
你不能改用 MonadError
语法吗?
getExample
:: (MonadIO m, MonadReader ApplicationConfig m, MonadError ApplicationError m)
=> [Example]
getExample = -- ...
getResponseAndStore :: (MonadIO m, MonadReader ApplicationConfig m, PersistStoreWrite backend, PersistEntityBackend Example ~ BaseBackend backend, MonadError ApplicationError m) => -- etc.
我能够制作一个我想要的功能。秘诀是使用 withPostgresqlConn.
process :: ReaderT ApplicationConfig IO (Either ApplicationError ())
process = do
appConfig <- ask
connStr <- asks connectionString
runStdoutLoggingT
$ withPostgresqlConn connStr
$ flip ($) appConfig
. runReaderT
. runExceptT
. runReaderT getResponseAndStore
我正在尝试实现一个简单的 Web 服务器,它与其他 API 交互并在进行一些处理后存储响应。
为了封装失败的可能性(空响应、不正确的请求等),我使用 ExceptT
如下:
getExample
:: (MonadIO m, MonadReader ApplicationConfig m)
=> ExceptT ApplicationError m [Example]
getExample = do
partOfReq <- asks requestForSometing
fn1 =<< fn2 partOfReq
我还有另一个函数,使用来自 Persistent 的 insertMany_ 将响应存储在数据库中。
storeExample
:: ( MonadIO m
, PersistStoreWrite backend
, PersistEntityBackend Example ~ BaseBackend backend
)
=> [Example]
-> ReaderT backend m ()
storeExample = insertMany_
现在我想写一个函数
getResponseAndStore = ... {- A combination of getExample and storeExample -}
这将完成这两件事,并将 ApplicationConfig
和 PersistEntityBackend
要求提升到顶部,用户可以在其中捆绑提供它们。
可以吗?
如果是这样 - strategy/implementation 会是什么?
如果否 - 我应该考虑哪些改变?
编辑:这就是我目前正在做的事情。
getResponseAndStore
:: ( MonadIO m
, MonadReader ApplicationConfig m
, PersistStoreWrite backend
, PersistEntityBackend Example ~ BaseBackend backend
)
=> ReaderT backend (ExceptT ApplicationError m) ()
getResponseAndStore = storeExample =<< lift getExample
你不能改用 MonadError
语法吗?
getExample
:: (MonadIO m, MonadReader ApplicationConfig m, MonadError ApplicationError m)
=> [Example]
getExample = -- ...
getResponseAndStore :: (MonadIO m, MonadReader ApplicationConfig m, PersistStoreWrite backend, PersistEntityBackend Example ~ BaseBackend backend, MonadError ApplicationError m) => -- etc.
我能够制作一个我想要的功能。秘诀是使用 withPostgresqlConn.
process :: ReaderT ApplicationConfig IO (Either ApplicationError ())
process = do
appConfig <- ask
connStr <- asks connectionString
runStdoutLoggingT
$ withPostgresqlConn connStr
$ flip ($) appConfig
. runReaderT
. runExceptT
. runReaderT getResponseAndStore