简体   繁体   中英

Idiomatic io-streams directory traversal

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)

Typical style would be to do something like this:

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

ie 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM