簡體   English   中英

為什么打印不強制整個惰性IO值?

[英]Why doesn't print force entire lazy IO value?

我正在使用http-client教程來使用TLS連接獲取響應正文。 因為我可以觀察到, print由被稱為withResponse ,為什么不print強制在下面的代碼片段輸出整個響應?

withResponse request manager $ \response -> do
    putStrLn $ "The status code was: " ++
    body <- (responseBody response)
    print body

我需要這樣寫:

response <- httpLbs request manager

putStrLn $ "The status code was: " ++
           show (statusCode $ responseStatus response)
print $ responseBody response

我要打印的主體是一個懶惰的ByteString。 我仍然不確定我是否應該期望print打印出整個值。

instance Show ByteString where
    showsPrec p ps r = showsPrec p (unpackChars ps) r

這並不一定與懶惰做的,但與之間的差異Response L.ByteString你與單模獲得,以及Response BodyReader您使用TLS模塊獲得。

您注意到, BodyReaderIO ByteString 但特別是,此操作可以重復,每次下一個字節塊都可以重復。 它遵循的協議是,除非它位於文件末尾,否則從不發送空字節串。 BodyReader可能被稱為ChunkGetter )。 下面的bip就像您寫的一樣:從Response提取BodyReader / IO ByteString ,它執行該操作以獲取第一個塊並進行打印。 但是不會重復此操作以獲取更多收益-因此在這種情況下,我們只看到《創世紀》的前幾章。 您需要的是一個循環來耗盡這些塊,如下面的bop所示,這會導致整個《詹姆斯國王聖經》溢出到控制台中。

{-# LANGUAGE OverloadedStrings #-} 
import Network.HTTP.Client
import Network.HTTP.Client.TLS
import qualified Data.ByteString.Char8 as B

main = bip
-- main = bop

bip = do 
  manager <- newManager tlsManagerSettings
  request <- parseRequest "https://raw.githubusercontent.com/michaelt/kjv/master/kjv.txt"
  withResponse request manager $ \response -> do
      putStrLn "The status code was: "  
      print (responseStatus response)
      chunk  <- responseBody response
      B.putStrLn chunk

bop = do 
  manager <- newManager tlsManagerSettings
  request <- parseRequest "https://raw.githubusercontent.com/michaelt/kjv/master/kjv.txt"
  withResponse request manager $ \response -> do
      putStrLn "The status code was: " 
      print (responseStatus response)
      let loop = do 
            chunk <- responseBody response
            if B.null chunk 
              then return () 
              else B.putStr chunk  >> loop 
      loop

循環不斷返回以獲取更多的塊,直到獲得代表eof的空字符串為止,因此在終端中它將一直打印到末日末尾。

這是很簡單的行為,但是有點技術性。 您只能通過手寫遞歸使用BodyReader 但是http-client庫的目的是使諸如http-conduit類的事情成為可能。 那里withResponse的結果的類型為Response (ConduitM i ByteString m ()) ConduitM i ByteString m ()是字節流的管道類型如何; 該字節流將包含整個文件。

http-client / http-conduit資料的原始形式中, Response包含這樣的管道; BodyReader部分后來被分解到http-client因此它可以被pipes等不同的流媒體庫使用。

因此,舉一個簡單的例子,在與streamingstreaming-bytestring庫相對應的http資料中, withHTTP為您提供了響應類型Response (ByteString IO ()) 顧名思義, ByteString IO ()是IO中產生的字節流的類型; ByteString Identity ()等效於惰性字節串(實際上是純塊列表)。在這種情況下, ByteString IO ()將代表整個字節流,直到天啟。 所以跟進口

 import qualified Data.ByteString.Streaming.HTTP as Bytes -- streaming-utils
 import qualified Data.ByteString.Streaming.Char8 as Bytes -- streaming-bytestring

該程序與惰性字節串程序相同:

bap = do 
    manager <- newManager tlsManagerSettings
    request <- parseRequest "https://raw.githubusercontent.com/michaelt/kjv/master/kjv.txt"
    Bytes.withHTTP request manager $ \response -> do 
        putStrLn "The status code was: "
        print (responseStatus response)
        Bytes.putStrLn $ responseBody response

實際上,它稍微簡單一些,因為您沒有“從IO中提取字節:

        lazy_bytes <- responseStatus response
        Lazy.putStrLn lazy_bytes

但只要寫

        Bytes.putStrLn $ responseBody response

您只需直接“打印”它們即可。 如果您只想從KJV的中間查看,則可以使用懶惰的字節串來做,然后以:

        Bytes.putStrLn $ Bytes.take 1000 $ Bytes.drop 50000 $ responseBody response

然后,您將看到有關亞伯拉罕的信息。

用於streaming-bytestringwithHTTP只是隱藏了我們直接從http-client使用BodyReader素材所需的遞歸循環。 例如,它與在pipes-http找到的withHTTP相同,后者代表Producer ByteString IO ()表示字節串塊流,而與http-conduit相同。 在所有這些情況下,一旦掌握了字節流,就可以采用流IO框架的典型方式來處理它,而無需手寫遞歸。 他們所有人都使用來自http-clientBodyReader進行此操作,這是該庫的主要目的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM