我正在试图弄清楚管道是如何工作的,并且我正陷入涉及的单子和变形金刚。
我将一些示例代码简化为以下代码:
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
这并不能帮助我理解正在发生的事情。
答案 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