使用ResourceT时需要解除的说明

时间:2013-12-16 23:39:10

标签: haskell conduit

我正在试图弄清楚管道是如何工作的,并且我正陷入涉及的单子和变形金刚。

我将一些示例代码简化为以下代码:

import Control.Monad.Trans.Class (lift)
import Data.Conduit
import Data.Conduit.Binary (sinkFile)
import Data.Conduit.List
import Network.HTTP.Conduit

downloadContent manager = do
  mx <- await
  case mx of
    Nothing   -> return ()
    Just name -> do
      req <- lift $ parseUrl $ "http://" ++ name ++ ".com/"
      res <- lift $ http req manager
      lift $ responseBody res $$+- sinkFile $ name ++ ".html"
      downloadContent manager

main = do
  runResourceT $ do
    withManager $ \manager -> do
      sourceList ["google", "yahoo"] $$ downloadContent manager

我不明白为什么我需要在liftdownloadContent为什么我需要在上面的代码中使用lift?我将提升到是什么?如果我看一下签名:

parseUrl
  :: failure-0.2.0.1:Control.Failure.Failure HttpException m =>
     String -> m (Request m')

http
  :: (MonadResource m, MonadBaseControl IO m) =>
     Request m
     -> Manager
     -> m (Response
             (ResumableSource m Data.ByteString.Internal.ByteString))

($$+-) :: Monad m => ResumableSource m a -> Sink a m b -> m b

downloadContent
  :: (MonadResource m, MonadBaseControl IO m,
      failure-0.2.0.1:Control.Failure.Failure HttpException m) =>
     Manager -> ConduitM [Char] o m ()

class (MonadThrow m, MonadUnsafeIO m, MonadIO m, Applicative m) => MonadResource m

这并不能帮助我理解正在发生的事情。

1 个答案:

答案 0 :(得分:3)

lift采取未经转换的monadic动作并将其包装,以便您可以在变换器内运行它:

lift :: (MonadTrans t, Monad m) => m a -> t m a

在这种情况下,变换器为ConduitM [Char] o,在Data.Conduit.Internal中定义为:

newtype ConduitM i o m r = ConduitM { unConduitM :: Pipe i i o () m r }
    deriving (..., MonadTrans, ...)

MonadTrans的实例使用GeneralizedNewtypeDeriving派生Pipe实例:

instance MonadTrans (Pipe l i o u) where
    lift mr = PipeM (Done `liftM` mr)

这是一个更简单的例子:

action :: ReaderT Int (State Int) ()
action = do           -- In the 'ReaderT Int (State Int)' monad.
  x <- ask            -- Ask for the (outer) 'Reader' environment.
  lift $ do           -- In the 'State Int' monad.
    modify (+x)       -- Modify the (inner) 'State' a couple of times.
    modify (+x)

main = print $ execState (runReaderT action 1) 1

我们位于ReaderT Int (State Int),我们的modify操作位于State Int,因此我们需要lift操作才能在变换器中运行它。请注意,与上面的示例一样,您应该能够将一系列lift ed操作合并到一个lift下:

Just name -> do
  lift $ do
    req <- parseUrl $ "http://" ++ name ++ ".com/"
    res <- http req manager
    responseBody res $$+- sinkFile $ name ++ ".html"
  downloadContent manager