liftBase
是其一部分MonadBase http://hackage.haskell.org/package/transformers-base-0.4.1/docs/Control-Monad-Base.html#t:MonadBase这是一个概括MonadIO
对于任何基本单子,正如你所说,MonadBase IO
提供与以下相同的功能MonadIO
.
然而,MonadBaseControl http://hackage.haskell.org/package/monad-control-0.3.2.2/docs/Control-Monad-Trans-Control.html#t:MonadBaseControl是有点复杂的野兽。在MonadBaseControl IO m
你有
liftBaseWith :: ((forall a. m a -> IO (StM m a)) -> IO a) -> m a
restoreM :: StM m a -> m a
通过查看示例最容易了解实际用途。例如,bracket http://hackage.haskell.org/package/base-4.6.0.1/docs/Control-Exception-Base.html#v:bracket from base
有签名
bracket :: IO a -> (a -> IO b) -> (a -> IO c) -> IO c
只要MonadBase IO m
(or MonadIO m
)你可以举起主要bracket
调用到m
但包围动作仍然需要是普通的旧式IO
.
throw
and catch
也许是更好的例子:
throw :: Exception e => e -> a
catch :: Exception e => IO a -> (e -> IO a) -> IO a
您可以轻松地从任何MonadIO m
你可以捕获异常IO a
inside MonadIO m
但同样,两个动作都在运行catch
并且异常处理程序本身需要IO a
not m a
.
Now MonadBaseControl IO
使得可以写bracket
and catch
以允许参数操作也是类型的方式m a
而不是仅限于基本单子。上述函数(以及许多其他函数)的通用实现可以在包中找到lifted-base http://hackage.haskell.org/package/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
and MonadBaseControl IO m
since MonadBaseControl IO m
应该暗示MonadBase IO m
这可以实现完全相同的功能。所以也许这只是一些旧版本的遗留物。
看看源码,可能只是因为runTCPClient
calls sourceSocket
and sinkSocket
内部和那些需要MonadIO
。我猜测包中的所有函数不简单地使用的原因MonadBase IO
就是它MonadIO
人们更熟悉,大多数 monad 转换器都有一个定义为的实例MonadIO m => MonadIO (SomeT m)
但用户可能必须编写自己的实例MonadBase IO
.