[英]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.