Я изучаю Haskell Lazy IO.
Я ищу элегантный способ скопировать большой файл (8 ГБ) во время печати хода копирования на консоль.
Рассмотрим следующую простую программу, которая копирует файл
module Main where
import System
import qualified Data.ByteString.Lazy as B
main = do [from, to] <- getArgs
body <- B.readFile from
B.writeFile to body
Представьте, что есть функция обратного вызова, которую вы хотите использовать для отчетности:
onReadBytes :: Integer -> IO ()
onReadBytes count = putStrLn $ "Bytes read: " ++ (show count)
ВОПРОС: как встроить функцию onReadBytes в Lazy ByteString, чтобы она вызывалась при успешном чтении? Или, если этот дизайн не очень хорош, то как это сделать в Haskell?
ПРИМЕЧАНИЕ: частота обратного вызова не важна, его можно вызывать каждые 1024 байта или каждый 1 Мбайт - не важно
ОТВЕТ: Большое спасибо camccann за ответ. Я предлагаю прочитать его целиком.
Ниже представлена моя версия кода, основанная на коде Camccann, вы можете найти его полезным.
module Main where
import System
import System.IO
import qualified Data.ByteString.Lazy as B
main = do [from, to] <- getArgs
withFile from ReadMode $ \fromH ->
withFile to WriteMode $ \toH ->
copyH fromH toH $ \x -> putStrLn $ "Bytes copied: " ++ show x
copyH :: Handle -> Handle -> (Integer -> IO()) -> IO ()
copyH fromH toH onProgress =
copy (B.hGet fromH (256 * 1024)) (write toH) B.null onProgress
where write o x = do B.hPut o x
return . fromIntegral $ B.length x
copy :: (Monad m) => m a -> (a -> m Integer) -> (a -> Bool) -> (Integer -> m()) -> m()
copy = copy_ 0
copy_ :: (Monad m) => Integer -> m a -> (a -> m Integer) -> (a -> Bool) -> (Integer -> m()) -> m()
copy_ count inp outp done onProgress = do x <- inp
unless (done x) $
do n <- outp x
onProgress (n + count)
copy_ (n + count) inp outp done onProgress