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
這是最簡單的,看看有什麼實際用途是通過看實例。例如,從base
的bracket
具有簽名
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
使得有可能的方式,允許參數行動也有m a
型的,而不是被限制到基單子寫bracket
和catch
。上述功能(以及其他功能)的通用實現可以在包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
編輯:現在,我居然重讀你的問題正確...
不,我看不出有任何理由簽名既需要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
。
謝謝,現在它是有道理的。令我困惑的是[_conduit_中的'MonadBaseControl'](http://hackage.haskell.org/package/conduit-1.0.9.3/docs/Data-Conduit.html#t:MonadBaseControl)缺少任何定義,可能只有名稱在那裏被導入/重新導出。 –