2016-04-30 11 views
0

Я пытаюсь создать канал, который будет передавать данные из HTTP через источник кабелепровода. Вот то, что я до сих пор:Трубопровод http через канал haskell

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 ожидается? Для меня это не имеет никакого смысла.

ответ

2

Как об этом изменении the example in the Network.HTTP.Conduit docs:

{-# 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 попадает в картину ... Монада m должен иметь доступ к менеджеру HTTP через среду чтения, как выражается следующими ограничениями:

CC.HasHttpManager env 
MonadReader env m 

Все это сказать, что m имеет среду чтения некоторого типа env, которая сама имеет способ доступа к HTTP-менеджеру.

В частности, m не может быть просто равным IO monad, о чем жалуется сообщение об ошибке.

Ответ на вопрос в комментариях

Вот пример того, как создать Producer из ответа HTTP:

{-# 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 
+0

Ого, что работает лучше, чем я ожидал. Только один вопрос: кажется, что единственный способ использовать поток «Source» - предоставить раковину в качестве аргумента. Есть ли способ фактически вернуть поток «Source», например. завернутый в 'IO'. Если не так? Алси, я не совсем понимаю, почему нужна монашеская «Монастырь». (извините, если это кажется очевидным, я все еще пытаюсь обернуть голову вокруг «Conduit») – fakedrake

+0

Ответ обновлен - см. в конце. В 'http' функция' ResourceT' используется для вызова финализатора для ответа. Тем не менее, финализатор также добавляется в канал 'Producer' (например,' производительWithCleanup'), поэтому я не знаю, действительно ли нужно использовать 'ResourceT' - финализатор, как представляется, вызывается, даже если потребитель не потребляет все из кусков. – ErikR

+0

Подробнее об использовании ResourceT здесь: https://github.com/snoyberg/http-client/issues/194 – ErikR

 Смежные вопросы

  • Нет связанных вопросов^_^