In Haskell, extracting out some functionality involving a large monad transformer into a separate function requires me to declare a number of hidden packages as project dependencies. This is a pain and takes time. Is there a better way of declaring the type of the extracted functionality, such that I don't have to add packages to my package.yaml dependencies?
Here is the sub-optimal workflow described in detail.
I have a Haskell persistent application that looks like this:
app :: IO ()
app = runStderrLoggingT $ withMySQLConn connectInfo $ runSqlConn $ do
entries <- selectFirst [UserId ==. 0 ] []
liftIO $ print entries
I want to extract out the sql connection so I can use it for other queries. So I make this function:
runDb = runStderrLoggingT . withMySQLConn connectInfo . runSqlConn
Now the compiler complains about ambiguous types in the runDb function:
• Ambiguous type variable ‘m0’ arising from a use of ‘runStderrLoggingT’
prevents the constraint ‘(MonadIO m0)’ from being solved.
...
• Ambiguous type variable ‘m0’ arising from a use of ‘withMySQLConn’
prevents the constraint ‘(unliftio-core-0.2.0.1:Control.Monad.IO.Unlift.MonadUnliftIO
m0)’ from being solved.
So I comment out runDb fire up the REPL with stack ghci and query the type: :t runStderrLoggingT . withMySQLConn connectInfo . runSqlConn, to which ghci tells me:
runStderrLoggingT . withMySQLConn connectInfo . runSqlConn
:: unliftio-core-0.2.0.1:Control.Monad.IO.Unlift.MonadUnliftIO m =>
transformers-0.5.6.2:Control.Monad.Trans.Reader.ReaderT
SqlBackend (LoggingT m) a
-> m a
So I now write my runDb function with a type definition:
runDb :: MonadUnliftIO m => ReaderT SqlBackend (LoggingT m) a -> m a
runDb = runStderrLoggingT . withMySQLConn connectInfo . runSqlConn
But now stack complains that it cannot find type constructors MonadUnliftIO, ReaderT, and LoggingT. So I have to make sure I import them:
import Control.Monad.Trans.Reader (ReaderT)
import Control.Monad.IO.Unlift (MonadUnliftIO)
import Control.Monad.Logger (LoggingT, runStderrLoggingT)
However, this doesn't work, since Control.Monad.Trans.Reader and Control.Monad.IO.Unlift are hidden packages. So I have to add them to my dependencies in package.yaml, and now everything works.
Whew! This seems like a long process. Is there a better way of handling the ambiguous type problem that arose when I extracted the runDb function?