3

我正试图弄清楚 Conduits 是如何工作的,并且正陷入所涉及的 monads 和 transformers 中。

我将一些示例代码归结为以下内容,它有效:

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

我不明白为什么我需要在lift里面downloadContent为什么我需要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

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

4

1 回答 1

4

lift采用未转换的单子动作并将其包装,以便您可以在转换器中运行它:

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行动才能在变压器中运行它。请注意,就像上面的示例一样,您应该能够将一系列lifted 操作合并到 one 下lift

Just name -> do
  lift $ do
    req <- parseUrl $ "http://" ++ name ++ ".com/"
    res <- http req manager
    responseBody res $$+- sinkFile $ name ++ ".html"
  downloadContent manager
于 2013-12-16T23:57:42.327 回答