Ответ 1
liftBase
является частью MonadBase
, который является обобщением MonadIO
для любой базовой монады и, как вы сказали, MonadBase IO
обеспечивает ту же функциональность, что и MonadIO
.
Однако MonadBaseControl
является немного более сложным зверем. В MonadBaseControl IO m
у вас есть
liftBaseWith :: ((forall a. m a -> IO (StM m a)) -> IO a) -> m a
restoreM :: StM m a -> m a
Проще всего понять, что такое практическое использование, глядя на примеры. Например, bracket
из base
имеет подпись
bracket :: IO a -> (a -> IO b) -> (a -> IO c) -> IO c
С помощью всего лишь MonadBase IO m
(или MonadIO m
) вы можете поднять основной вызов bracket
в m
, но действия брекетинга по-прежнему должны быть в обычном старом IO
.
throw
и catch
являются, возможно, еще лучшими примерами:
throw :: Exception e => e -> a
catch :: Exception e => IO a -> (e -> IO a) -> IO a
Вы можете легко выбросить исключение из любого MonadIO m
, и вы можете поймать исключение из IO a
внутри MonadIO m
, но опять же, действие, выполняемое в catch
, и сам обработчик исключений должен быть IO a
не m a
.
Теперь MonadBaseControl IO
позволяет писать bracket
и catch
таким образом, чтобы действия параметра также имели тип m a
вместо того, чтобы ограничиваться базовой монадой. Общая реализация указанных выше функций (как и многих других) может быть найдена в пакете lifted-base
. Например:
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: И теперь, когда я действительно перечитаю ваш вопрос правильно...
Нет, я не вижу причин, почему для подписи требуются как MonadIO m
, так и MonadBaseControl IO m
, так как MonadBaseControl IO m
должен подразумевать MonadBase IO m
, что позволяет использовать ту же функциональность. Так что, может быть, это просто осталось от старой версии.
Глядя на источник, возможно, только потому, что runTCPClient
вызывает sourceSocket
и sinkSocket
внутренне, а те требуют MonadIO
. Я предполагаю, что причина, по которой все функции в пакете не просто используют MonadBase IO
, состоит в том, что MonadIO
более знакома людям, и большинство монад-трансформаторов имеют экземпляр, определенный для MonadIO m => MonadIO (SomeT m)
, но пользователям, возможно, придется писать их собственный экземпляр для MonadBase IO
.