Lazy file reading

A good first step would be to have a version of withFile which opened several files at the same time and let you work with the list of Handles in a callback (while still offering the cleanup assurances of withFile).

We can build such a function using ContT:

import Control.Monad.Trans.Cont
import System.IO

withManyFiles :: [FilePath] -> IOMode -> ([Handle] -> IO r) -> IO r
withManyFiles files mode =
  runContT $ traverse (`withFileCont` mode) files
  where
    withFileCont :: FilePath -> IOMode -> ContT r IO Handle
    withFileCont filepath mode = ContT (withFile filepath mode)

This function that filters the list of Handles and keeps only those that haven’t reached eof could also be useful:

filterEOF :: [Handle] -> IO [Handle]
filterEOF = filterM $ \h -> do
    eof <- hIsEOF h
    -- hClose will get called again at the end of withManyFiles,
    -- but that isn't a problem
    when eof $ hClose h
    return (not eof)