何时在流字节字符串上调用runResourceT?

时间:2019-05-12 00:45:42

标签: haskell haskell-streaming

我是Haskell的初学者,仍然在学习monad变压器。

我正在尝试使用streaming-bytestring库读取二进制文件,处理字节块并在处理每个块时打印结果。我相信这是流行的streaming库,它提供了惰性字节串的替代方案。看来作者复制粘贴了lazy bytestring文档,并添加了一些任意示例。

这些示例提到runResourceT,而没有讨论它是什么或如何使用。似乎应该在执行操作的任何流字节字符串函数上使用runResourceT。很好,但是如果我正在读取处理块并打印它们的无限流怎么办?每次我想处理该块时都应该调用runResourceT吗?

我的代码是这样的:

import qualified Data.ByteString.Streaming as BSS
import System.TimeIt

main = timeIt $ processByteChunks $ BSS.drop 100 $ BSS.readFile "filename"

,但我不确定如何将processByteChunks组织为遍历二进制文件的递归函数。

如果仅调用runResourceT,它将在打印前读取无限文件,对吗?好像不好。

main = timeIt $ runResourceT $ processByteChunks $ BSS.drop 100 $ BSS.readFile "filename"

1 个答案:

答案 0 :(得分:3)

ResourceT monad会在您使用完资源后及时清理它们。在这种情况下,它将确保在使用流时关闭由BSS.readFile打开的文件句柄。 (除非流确实是无限的,否则我猜不会。)

在您的应用程序中,您只想调用一次,因为您不希望在读取所有块之前关闭文件。不用担心-它与输出的时间或诸如此类无关。

这是一个应该起作用的递归processByteChunks的示例。它将延迟读取并在延迟读取块时生成输出:

import Control.Monad.IO.Class
import Control.Monad.Trans.Resource
import qualified Data.ByteString.Streaming as BSS
import qualified Data.ByteString as BS
import System.TimeIt

main :: IO ()
main = timeIt $ runResourceT $
  processByteChunks $ BSS.drop 100 $ BSS.readFile "filename"

processByteChunks :: MonadIO m => BSS.ByteString m () -> m ()
processByteChunks = go 0 0
  where go len nulls stream = do
          m <- BSS.unconsChunk stream
          case m of
            Just (bs, stream') -> do
              let len' = len + BS.length bs
                  nulls' = nulls + BS.length (BS.filter (==0) bs)
              liftIO $ print $ "cumulative length=" ++ show len'
                                      ++ ", nulls=" ++ show nulls'
              go len' nulls' stream'
            Nothing -> return ()