2014-01-05 3 views
10

Функция runTCPClient из сети-канала имеет следующую подпись:Есть ли разница между «MonadIO m» и «MonadBaseControl IO m»?

runTCPClient :: (MonadIO m, MonadBaseControl IO m) 
      => ClientSettings m -> Application m -> m() 

MonadIO m обеспечивает

liftIO :: IO a -> m a 

и MonadBaseControl IO m обеспечивает

liftBase :: IO a -> m a 

Там нет никакой видимой разницы. Обеспечивают ли они такую ​​же функциональность? Если да, то почему дублирование в сигнатуре типа? Если нет, какая разница?

ответ

12

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.

+0

Спасибо, теперь это имеет смысл. Меня смутило то, что ['MonadBaseControl' в _conduit_] (http://hackage.haskell.org/package/conduit-1.0.9.3/docs/Data-Conduit.html#t:MonadBaseControl) отсутствует какое-либо определение, возможно, только имя импортируется/реэкспортируется там. –

Смежные вопросы