繁体   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