简体   繁体   English

为什么打印不强制整个惰性IO值?

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

I'm using http-client tutorial to get response body using TLS connection. 我正在使用http-client教程来使用TLS连接获取响应正文。 Since I can observe that print is called by withResponse , why doesn't print force entire response to the output in the following fragment? 因为我可以观察到, print由被称为withResponse ,为什么不print强制在下面的代码片段输出整个响应?

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

I need to write this instead: 我需要这样写:

response <- httpLbs request manager

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

Body I want to print is a lazy ByteString. 我要打印的主体是一个懒惰的ByteString。 I'm still not sure whether I should expect print to print the entire value. 我仍然不确定我是否应该期望print打印出整个值。

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

This doesn't have to do with laziness, but with the difference between the Response L.ByteString you get with the Simple module, and the Response BodyReader you get with the TLS module. 这并不一定与懒惰做的,但与之间的差异Response L.ByteString你与单模获得,以及Response BodyReader您使用TLS模块获得。

You noticed that a BodyReader is an IO ByteString . 您注意到, BodyReaderIO ByteString But in particular it is an action that can be repeated, each time with the next chunk of bytes. 但特别是,此操作可以重复,每次下一个字节块都可以重复。 It follows the protocol that it never sends a null bytestring except when it's at the end of file. 它遵循的协议是,除非它位于文件末尾,否则从不发送空字节串。 ( BodyReader might have been called ChunkGetter ). BodyReader可能被称为ChunkGetter )。 bip below is like what you wrote: after extracting the BodyReader / IO ByteString from the Response , it performs it to get the first chunk, and prints it. 下面的bip就像您写的一样:从Response提取BodyReader / IO ByteString ,它执行该操作以获取第一个块并进行打印。 But doesn't repeat the action to get more - so in this case we just see the first couple chapters of Genesis. 但是不会重复此操作以获取更多收益-因此在这种情况下,我们只看到《创世纪》的前几章。 What you need is a loop to exhaust the chunks, as in bop below, which causes the whole King James Bible to spill into the console. 您需要的是一个循环来耗尽这些块,如下面的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

The loop keeps going back to get more chunks until it gets an empty string, which represents eof, so in the terminal it prints through to the end of the Apocalypse. 循环不断返回以获取更多的块,直到获得代表eof的空字符串为止,因此在终端中它将一直打印到末日末尾。

This is behavior is straightforward but slightly technical. 这是很简单的行为,但是有点技术性。 You can only work with a BodyReader by hand-written recursion. 您只能通过手写递归使用BodyReader But the purpose of the http-client library is to make things like http-conduit possible. 但是http-client库的目的是使诸如http-conduit类的事情成为可能。 There the result of withResponse has the type Response (ConduitM i ByteString m ()) . 那里withResponse的结果的类型为Response (ConduitM i ByteString m ()) ConduitM i ByteString m () is how conduit types of a byte stream; ConduitM i ByteString m ()是字节流的管道类型如何; this byte stream would contain the whole file. 该字节流将包含整个文件。

In the original form of the http-client / http-conduit material, the Response contained a conduit like this; http-client / http-conduit资料的原始形式中, Response包含这样的管道; the BodyReader part was later factored out into http-client so it could be used by different streaming libraries like pipes . BodyReader部分后来被分解到http-client因此它可以被pipes等不同的流媒体库使用。

So to take a simple example, in the corresponding http material for the streaming and streaming-bytestring libraries, withHTTP gives you a response of type Response (ByteString IO ()) . 因此,举一个简单的例子,在与streamingstreaming-bytestring库相对应的http资料中, withHTTP为您提供了响应类型Response (ByteString IO ()) ByteString IO () is the type of a stream of bytes arising in IO, as its name suggests; 顾名思义, ByteString IO ()是IO中产生的字节流的类型; ByteString Identity () would be the equivalent of a lazy bytestring (effectively a pure list of chunks.) The ByteString IO () will in this case represent the whole bytestream down to the Apocalypse. ByteString Identity ()等效于惰性字节串(实际上是纯块列表)。在这种情况下, ByteString IO ()将代表整个字节流,直到天启。 So with the imports 所以跟进口

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

the program is identical to a lazy bytestring program: 该程序与惰性字节串程序相同:

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

Indeed it is slightly simpler, since you don't have "extract the bytes from IO`: 实际上,它稍微简单一些,因为您没有“从IO中提取字节:

        lazy_bytes <- responseStatus response
        Lazy.putStrLn lazy_bytes

but just write 但只要写

        Bytes.putStrLn $ responseBody response

you just "print" them directly. 您只需直接“打印”它们即可。 If you want to view just a bit from the middle of the KJV, you can instead do what you would with a lazy bytestring, and end with: 如果您只想从KJV的中间查看,则可以使用懒惰的字节串来做,然后以:

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

Then you will see something about Abraham. 然后,您将看到有关亚伯拉罕的信息。

The withHTTP for streaming-bytestring just hides the recursive looping that we needed to use the BodyReader material from http-client directly. 用于streaming-bytestringwithHTTP只是隐藏了我们直接从http-client使用BodyReader素材所需的递归循环。 It's the same eg with the withHTTP you find in pipes-http , which represents a stream of bytestring chunks as Producer ByteString IO () , and the same with http-conduit . 例如,它与在pipes-http找到的withHTTP相同,后者代表Producer ByteString IO ()表示字节串块流,而与http-conduit相同。 In all of these cases, once you have your hands on the byte stream you handle it in the ways typical of the streaming IO framework without handwritten recursion. 在所有这些情况下,一旦掌握了字节流,就可以采用流IO框架的典型方式来处理它,而无需手写递归。 All of them use the BodyReader from http-client to do this, and this was the main purpose of the library. 他们所有人都使用来自http-clientBodyReader进行此操作,这是该库的主要目的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM