[Haskell-cafe] Filesystem questions

Yitzchak Gale gale at sefer.org
Sun Oct 14 05:33:38 EDT 2007

I wrote:
>> ...a tool for recursing through directories...
>> How about a built-in function that represents a directory tree
>> as a lazy Data.Tree?

Bryan O'Sullivan wrote:
> See System.FilePath.Find in
> http://hackage.haskell.org/cgi-bin/hackage-scripts/package/FileManip-0.2

> Not a very good idea.  Representing a directory structure as a tree
> makes people think they can manipulate it as if it were a tree, which
> leads to all kinds of nasty bugs when the real world bleeds through the
> holes in the abstraction.

You are right. Well, I didn't say what I meant by a
"lazy Data.Tree" :).

Your library is very nice. But - it suffers from the
same problem. You use unsafe IO operations to build
a "lazy" IO list, and we all know what grief that can
lead to.

Especially in this application, using that type of "laziness"
is really unsafe. Any user must be explicitly aware of
potential side effect complications, such as directory
structure shifting under your feet as you work, possibly
induced by your own traversal if you are not careful.

(In my opinion, the documention for Python's os.walk
gives a nice, clear description of what must be
avoided during such a traversal.)

So I think that what is needed here is something like
the following. Based on my experience, this would be
a really convenient API:

data TraversalDirection = TopDown | BottomUp
data Directory = Directory {
  dirPath :: FilePath,
  fileNames :: [FilePath],
  subdirNames :: [FilePath]}

-- List all directories in the tree rooted at the given path
traverseDirectories :: MonadIO m =>
  TraversalDirection -> FilePath -> ListT m Directory

-- List all non-directory files in the tree rooted at the given path
traverseFiles :: MonadIO m =>
  TraversalDirection -> FilePath -> ListT m FilePath
traverseFiles = join . fmap (liftList . fileNames) . traverseDirectories

-- List the paths to all subdirectories in the tree rooted at the given path
traverseSubdirs :: MonadIO m =>
  TraversalDirection -> FilePath -> ListT m FilePath
traverseSubdirs = join . fmap (liftList . subdirNames) . traverseDirectories

Here it is critical that ListT be taken from


and not the broken implementation that comes with mtl.
(Finally fixed in 6.8? Please?)

You could plug the above into your machinery for
"recursion predicates" and all the other nice stuff.


More information about the Haskell-Cafe mailing list