[英]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模塊獲得。
您注意到, BodyReader
是IO 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
等不同的流媒體庫使用。
因此,舉一個簡單的例子,在與streaming
和streaming-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-bytestring
的withHTTP
只是隱藏了我們直接從http-client
使用BodyReader
素材所需的遞歸循環。 例如,它與在pipes-http
找到的withHTTP
相同,后者代表Producer ByteString IO ()
表示字節串塊流,而與http-conduit
相同。 在所有這些情況下,一旦掌握了字節流,就可以采用流IO框架的典型方式來處理它,而無需手寫遞歸。 他們所有人都使用來自http-client
的BodyReader
進行此操作,這是該庫的主要目的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.