Идиоматический обход каталога io-streams

Я обсуждал код на Reddit, и мне стало любопытно о том, как это будет реализовано в io-streams. Рассмотрим следующий код, который проходит через структуру каталогов и выводит все имена файлов:

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

Вместо того, чтобы просто печатать файлы, предположим, что мы хотим создать какое-то потоковое представление пути к файлу. Я знаю, как это будет работать в счетчике, канале и каналах. Однако, поскольку промежуточные шаги требуют приобретения дефицитного ресурса (DirStream), я не уверен, какой будет реализация для потоков ввода-вывода. Может ли кто-нибудь привести пример того, как это будет сделано?

Для сравнения вот реализация канала, что стало возможным благодаря bracketP и MonadResource. А вот как можно использовать код канала для реализации той же программы печати файлов, что и выше:

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)

person Michael Snoyman    schedule 07.05.2014    source источник
comment
Побочный комментарий: я собирался пометить это io-streams, но, видимо, SO считает, что это конфликтует с существующим тегом iostreams. Вместо этого я поставил snap-framework, но если есть лучший тег для вопросов io-streams, пожалуйста, дайте мне знать.   -  person Michael Snoyman    schedule 07.05.2014
comment
Я надеюсь, что это небольшая структура каталогов; это выглядит мучительно медленно. В моей системе запуск sourceDirectoryDeep в дереве сборки ghc (без вывода каких-либо результатов) занимает около 1,6 с по сравнению с 95 мс при использовании traverseDirectory из posix-путей.   -  person John L    schedule 07.05.2014
comment
Я не удивлен, что это медленнее; он использует FilePath вместо RawFilePath. Также сейчас отсутствуют некоторые правила перезаписи для sourceDirectoryDeep, что может быть одним из факторов. Мне было бы любопытно, какая разница в скорости между posix-путями и самими потоковыми сообществами.   -  person Michael Snoyman    schedule 07.05.2014
comment
posix-paths ничего не статит, что, пожалуй, самая существенная экономия (после RawFilePath)   -  person John L    schedule 07.05.2014
comment
Ааа, это бы сработало. Жаль, что unix не предоставляет больше информации с readDirStream, возможно, мне следует изменить streaming-commons, чтобы предоставить больше информации.   -  person Michael Snoyman    schedule 07.05.2014


Ответы (1)


Типичным стилем было бы сделать что-то вроде этого:

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

то есть стандартная функция «с-» с очевидной реализацией.

Изменить: добавлен рабочий пример реализации: https://gist.github.com/gregorycollins/00c51e7e33cf1f9c8cc0

Это не совсем сложно, но и не так тривиально, как я сначала предложил.

person gregorycollins    schedule 09.05.2014
comment
Можете ли вы на самом деле предоставить реализацию с точки зрения потокового сообщества (или любой другой библиотеки, которая вам нравится)? Я не понимаю, как получить правильную семантику распределения ресурсов, учитывая рекурсивный характер обхода. - person Michael Snoyman; 09.05.2014
comment
Благодарю за разъяснение. Я добавил комментарий к Gist с некоторыми опасениями по поводу безопасности исключений в коде освобождения. - person Michael Snoyman; 12.05.2014