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?
liftBase
is part ofMonadBase
which is a generalization ofMonadIO
for any base monad and, as you said,MonadBase IO
provides the same functionality asMonadIO
.However,
MonadBaseControl
is a bit more complicated beast. InMonadBaseControl IO m
you haveIt's easiest to see what the practical uses are by looking at examples. For example, the
bracket
frombase
has the signatureWith just
MonadBase IO m
(orMonadIO m
) you can lift the mainbracket
invocation intom
but the bracketing actions still need to be in plain oldIO
.throw
andcatch
are maybe even better examples:You can easily thrown an exception from any
MonadIO m
and you can catch exception fromIO a
insideMonadIO m
but again, both the action being run incatch
and the exception handler itself need to beIO a
notm a
.Now
MonadBaseControl IO
makes it possible to writebracket
andcatch
in a way that allows the parameter actions to also be of typem a
instead 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 m
andMonadBaseControl IO m
sinceMonadBaseControl IO m
should implyMonadBase IO m
which 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
runTCPClient
callssourceSocket
andsinkSocket
internally and those requireMonadIO
. I'm guessing that the reason why all the functions in the package don't simply useMonadBase IO
is thatMonadIO
is 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
.