Question

I was discussing some code on Reddit, and it made me curious about how this would be implemented in io-streams. Consider the following code which traverses a directory structure and prints out all of the filenames:

import           Control.Exception         (bracket)
import qualified Data.Foldable             as F
import           Data.Streaming.Filesystem (closeDirStream, openDirStream,
                                            readDirStream)
import           System.Environment        (getArgs)
import           System.FilePath           ((</>))

printFiles :: FilePath -> IO ()
printFiles dir = bracket
    (openDirStream dir)
    closeDirStream
    loop
  where
    loop ds = do
        mfp <- readDirStream ds
        F.forM_ mfp $ \fp' -> do
            let fp = dir </> fp'
            ftype <- getFileType fp
            case ftype of
                FTFile -> putStrLn fp
                FTFileSym -> putStrLn fp
                FTDirectory -> printFiles fp
                _ -> return ()
            loop ds

main :: IO ()
main = getArgs >>= mapM_ printFiles

Instead of simply printing the files, suppose we wanted to create some kind of streaming filepath representation. I know how this would work in enumerator, conduit, and pipes. However, since the intermediate steps require acquisition of a scarce resource (the DirStream), I'm not sure what the implementation would be for io-streams. Can someone provide an example of how that would be done?

For comparison, here's the conduit implementation, which is made possible via bracketP and MonadResource. And here's how the conduit code would be used to implemented the same file printing program as above:

import           Control.Monad.IO.Class       (liftIO)
import           Control.Monad.Trans.Resource (runResourceT)
import           Data.Conduit                 (($$))
import           Data.Conduit.Filesystem      (sourceDirectoryDeep)
import qualified Data.Conduit.List            as CL
import           System.Environment           (getArgs)

main :: IO ()
main =
    getArgs >>= runResourceT . mapM_ eachRoot
  where
    -- False means don't traverse dir symlinks
    eachRoot root = sourceDirectoryDeep False root
                 $$ CL.mapM_ (liftIO . putStrLn)
Was it helpful?

Solution

Typical style would be to do something like this:

traverseDirectory :: RawFilePath -> (InputStream RawFilePath -> IO a) -> IO a

i.e. a standard "with-" function, with the obvious implementation.

Edit: added a working example implementation: https://gist.github.com/gregorycollins/00c51e7e33cf1f9c8cc0

It's not exactly complicated but it's also not as trivial as I had first suggested.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top