通过 Haskell管道输送 http 流



我正在尝试创建一个管道,该管道将通过管道源从HTTP流式传输数据。这是我到目前为止所拥有的:

import qualified Network.HTTP.Client.Conduit as CC
getStream :: String -> IO (ConduitM () BS.ByteString IO ())
getStream url = do
  req <- parseUrl url
  return $  CC.withResponse req $ res -> do
    responseBody res $= (awaitForever $ bytes -> liftIO $ do
      putStrLn $ "Got " ++ show (BS.length bytes) ++ " but will ignore    them")

但我正在得到

No instance for (Control.Monad.Reader.Class.MonadReader env0 IO) …
      arising from a use of ‘CC.withResponse’
    In the expression: CC.withResponse req
    In the second argument of ‘($)’, namely
      ‘CC.withResponse req
       $  res
           -> do { responseBody res $= (awaitForever $  bytes -> ...) }’
    In a stmt of a 'do' block:
      return
      $ CC.withResponse req
        $  res
            -> do { responseBody res $= (awaitForever $  bytes -> ...) }

为什么会出现MonadReader?这对我来说没有任何意义。

Network.HTTP.Conduit 文档中示例的这种变体怎么样:

{-# LANGUAGE OverloadedStrings #-}
module Lib2 () where
import Data.Conduit (($$+-), awaitForever)
import qualified Network.HTTP.Client.Conduit as CC
import Network.HTTP.Conduit (http, tlsManagerSettings, newManager)
import Control.Monad.IO.Class (liftIO)
import Control.Monad.Trans.Resource (runResourceT)
import Data.Conduit.Binary (sinkFile) -- Exported from the package conduit-extra
main2 :: IO ()
main2 = do
       request <- CC.parseUrl "http://google.com/"
       manager <- newManager tlsManagerSettings
       runResourceT $ do
           response <- http request manager
           CC.responseBody response $$+- (awaitForever $ x -> liftIO $ putStrLn "Chunk")

原答案

getStream的返回类型是错误的。尝试删除类型签名并使用FlexibleContexts,例如:

{-# LANGUAGE OverloadedStrings, FlexibleContexts #-}
module Lib () where
import Data.Conduit
import qualified Data.ByteString as BS
import qualified Network.HTTP.Client.Conduit as CC
import Control.Monad.IO.Class
getStream url = do
  req <- CC.parseUrl url
  CC.withResponse req $ res -> do
   CC.responseBody res $= (awaitForever $ x -> liftIO $ putStrLn "Got a chunk")

然后:t getStream报告:

getStream
  :: (monad-control-1.0.0.4:Control.Monad.Trans.Control.MonadBaseControl
        IO (ConduitM a c m),
      mtl-2.2.1:Control.Monad.Reader.Class.MonadReader env m, MonadIO m,
      CC.HasHttpManager env,
      exceptions-0.8.0.2:Control.Monad.Catch.MonadThrow m) =>
     String -> ConduitM a c m ()

这表明返回类型的形式为 ConduitM ... ,而不是 IO ...

这也显示了MonadReader是如何进入画面的......monad m必须能够通过读取器环境访问 HTTP 管理器,如以下约束所示:

CC.HasHttpManager env
MonadReader env m

所有这些都是说m具有某种类型的读取器环境env它本身具有访问HTTP管理器的方法。

特别是,m不能只是普通的IO monad,这就是错误消息所抱怨的。

评论中问题的回答

下面是如何从 HTTP 响应创建Producer的示例:

{-# LANGUAGE OverloadedStrings #-}
module Lib3 () where
import qualified Data.ByteString as BS
import qualified Network.HTTP.Client.Conduit as CC
import           Network.HTTP.Conduit (http, tlsManagerSettings, newManager)
import qualified Network.HTTP.Client          as Client (httpLbs, responseOpen, responseClose)
import           Data.Conduit (Producer, addCleanup)
import           Data.Conduit (awaitForever, await, ($$))
import qualified Network.HTTP.Client.Conduit  as HCC
import Control.Monad.IO.Class (liftIO, MonadIO)
getStream url = do
  request <- CC.parseUrl url
  manager <- newManager tlsManagerSettings
  response <- Client.responseOpen request manager
  let producer :: Producer IO BS.ByteString
      producer = HCC.bodyReaderSource $ CC.responseBody response
      cleanup _ = do liftIO $ putStrLn "(cleaning up)"; Client.responseClose response
      producerWithCleanup = addCleanup cleanup producer
  return $ response { CC.responseBody = producerWithCleanup }
test = do
  res <- getStream "http://google.com"
  let producer = CC.responseBody res
      consumer = awaitForever $ _ -> liftIO $ putStrLn "Got a chunk"
  producer $$ consumer

相关内容

  • 没有找到相关文章

最新更新