I'm trying to implement a simple web server that interacts with some other API and stores the response after doing some processing.
To encapsulate the possibility of failure (empty response, incorrect request, etc) I am using ExceptT
as following:
getExample
:: (MonadIO m, MonadReader ApplicationConfig m)
=> ExceptT ApplicationError m [Example]
getExample = do
partOfReq <- asks requestForSometing
fn1 =<< fn2 partOfReq
and I have another function that stores the response in a database using insertMany_ from Persistent.
storeExample
:: ( MonadIO m
, PersistStoreWrite backend
, PersistEntityBackend Example ~ BaseBackend backend
)
=> [Example]
-> ReaderT backend m ()
storeExample = insertMany_
Now I want to write a function
getResponseAndStore = ... {- A combination of getExample and storeExample -}
that will do both of these things and bubble up ApplicationConfig
and PersistEntityBackend
requirements to the top where the user could provide them in a bundle.
Would that be possible?
If so - What would the strategy/implementation be?
If no - What changes shall I consider?
Edit: This is what I'm doing currently.
getResponseAndStore
:: ( MonadIO m
, MonadReader ApplicationConfig m
, PersistStoreWrite backend
, PersistEntityBackend Example ~ BaseBackend backend
)
=> ReaderT backend (ExceptT ApplicationError m) ()
getResponseAndStore = storeExample =<< lift getExample
I was able to make a function that does just want I want to. The secret sauce was using withPostgresqlConn.
process :: ReaderT ApplicationConfig IO (Either ApplicationError ())
process = do
appConfig <- ask
connStr <- asks connectionString
runStdoutLoggingT
$ withPostgresqlConn connStr
$ flip ($) appConfig
. runReaderT
. runExceptT
. runReaderT getResponseAndStore