What is the strategy for working with different types of Monad Transformers?

89 Views Asked by At

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
2

There are 2 best solutions below

1
atis On BEST ANSWER

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
4
Sir4ur0n On

can't you use the MonadError syntax instead?

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.