问题
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?
回答1:
liftBase
is part of MonadBase which is a generalization of MonadIO
for any base monad and, as you said, MonadBase IO
provides the same functionality as MonadIO
.
However, MonadBaseControl is a bit more complicated beast. In MonadBaseControl IO m
you have
liftBaseWith :: ((forall a. m a -> IO (StM m a)) -> IO a) -> m a
restoreM :: StM m a -> m a
It's easiest to see what the practical uses are by looking at examples. For example, the bracket from base
has the signature
bracket :: IO a -> (a -> IO b) -> (a -> IO c) -> IO c
With just MonadBase IO m
(or MonadIO m
) you can lift the main bracket
invocation into m
but the bracketing actions still need to be in plain old IO
.
throw
and catch
are maybe even better examples:
throw :: Exception e => e -> a
catch :: Exception e => IO a -> (e -> IO a) -> IO a
You can easily thrown an exception from any MonadIO m
and you can catch exception from IO a
inside MonadIO m
but again, both the action being run in catch
and the exception handler itself need to be IO a
not m a
.
Now MonadBaseControl IO
makes it possible to write bracket
and catch
in a way that allows the parameter actions to also be of type m 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 package lifted-base. For example:
catch :: (MonadBaseControl IO m, Exception e) => m a -> (e -> m a) -> m a
bracket :: MonadBaseControl IO m => m a -> (a -> m b) -> (a -> m c) -> m c
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
and MonadBaseControl IO m
since MonadBaseControl IO m
should imply MonadBase 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
calls sourceSocket
and sinkSocket
internally and those require MonadIO
. I'm guessing that the reason why all the functions in the package don't simply use MonadBase IO
is that MonadIO
is more familiar to people and most monad transformers have a instance defined for MonadIO m => MonadIO (SomeT m)
but users might have to write their own instance for MonadBase IO
.
来源:https://stackoverflow.com/questions/20931277/is-there-any-difference-between-monadio-m-and-monadbasecontrol-io-m