Почему печать не заставляет всю ленивую ценность IO?
Я использую учебник http-клиента, чтобы получить тело ответа, используя соединение 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
1 ответ
Это не связано с ленью, но с разницей между Response L.ByteString
вы получаете с простым модулем, а Response BodyReader
Вы получаете с модулем TLS.
Вы заметили, что BodyReader
является IO ByteString
, Но, в частности, это действие, которое может повторяться каждый раз со следующим фрагментом байтов. Из протокола следует, что он никогда не отправляет пустую строку, кроме случаев, когда она находится в конце файла. (BodyReader
можно было бы назвать ChunkGetter
). bip
ниже, как то, что вы написали: после извлечения BodyReader
/ IO ByteString
от Response
, он выполняет его, чтобы получить первый фрагмент, и печатает его. Но не повторяйте действие, чтобы получить больше - поэтому в этом случае мы просто видим первые пару глав Бытия. То, что вам нужно, это петля для выпуска кусков, как в 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
,
Итак, чтобы взять простой пример, в соответствующем http материале для streaming
а также streaming-bytestring
библиотеки, 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
Тогда вы увидите что-то об Аврааме.
withHTTP
за streaming-bytestring
просто скрывает рекурсивный цикл, который нам нужно было использовать BodyReader
материал из http-client
непосредственно. То же самое, например, с withHTTP
вы найдете в pipes-http
, который представляет поток кусков байтовой строки как Producer ByteString IO ()
и то же самое с http-conduit
, Во всех этих случаях, когда вы получаете доступ к потоку байтов, вы обрабатываете его способами, типичными для структуры потокового ввода-вывода без рукописной рекурсии. Все они используют BodyReader
от http-client
сделать это, и это было основной целью библиотеки.