Function runTCPClient from network-conduit has the following signature:
runTCPClient :: (MonadIO m, MonadBaseControl IO m)
=> ClientSettings m -> Application m -> m ()
MonadIO m provides
liftIO :: IO a -> m a
and MonadBaseControl IO m provides
liftBase :: IO a -> m a
There is no visible difference. Do they provide the same functionality? If yes, why the duplication in the type signature? If not, what's the difference?
liftBaseis part ofMonadBasewhich is a generalization ofMonadIOfor any base monad and, as you said,MonadBase IOprovides the same functionality asMonadIO.However,
MonadBaseControlis a bit more complicated beast. InMonadBaseControl IO myou haveIt's easiest to see what the practical uses are by looking at examples. For example, the
bracketfrombasehas the signatureWith just
MonadBase IO m(orMonadIO m) you can lift the mainbracketinvocation intombut the bracketing actions still need to be in plain oldIO.throwandcatchare maybe even better examples:You can easily thrown an exception from any
MonadIO mand you can catch exception fromIO ainsideMonadIO mbut again, both the action being run incatchand the exception handler itself need to beIO anotm a.Now
MonadBaseControl IOmakes it possible to writebracketandcatchin a way that allows the parameter actions to also be of typem ainstead of being restricted to the base monad. The generic implementation for the above functions (as well as many others) can be found in the packagelifted-base. For example:EDIT: And now that I actually re-read your question properly...
No, I don't see any reason why the signature requires both
MonadIO mandMonadBaseControl IO msinceMonadBaseControl IO mshould implyMonadBase IO mwhich enables the exact same functionality. So maybe it's just a left-over from some older version.Looking at the source, it's probably just because
runTCPClientcallssourceSocketandsinkSocketinternally and those requireMonadIO. I'm guessing that the reason why all the functions in the package don't simply useMonadBase IOis thatMonadIOis more familiar to people and most monad transformers have a instance defined forMonadIO m => MonadIO (SomeT m)but users might have to write their own instance forMonadBase IO.