Recursive directory tree printer I wrote this program that recursively prints all the files and directories starting at the current directory:

import Data.Tree (Tree (..))
import System.Directory (doesDirectoryExist, getCurrentDirectory, getDirectoryContents)

main :: IO ()
main = do
  currentDirectory &lt;- getCurrentDirectory
  tree &lt;- tree currentDirectory "."
  printTree 0 tree

printTree :: Int -&gt; Tree FilePath -&gt; IO ()
printTree indent tree = do
  putStrLn $ (replicate indent ' ' ++ rootLabel tree)
  mapM_ (printTree $ indent + 2) (subForest tree)

treeHelper :: FilePath -&gt; IO [Tree FilePath]
treeHelper path =
  let filter' = filter (`notElem` [".", ".."]) in
  do
    contents &lt;- getDirectoryContents path
    trees &lt;- mapM (tree path) (filter' contents)
    return trees

tree :: FilePath -&gt; FilePath -&gt; IO (Tree FilePath)
tree parent path =
  let fullPath = (parent ++ "/" ++ path) in
  do
    isDirectory &lt;- doesDirectoryExist fullPath
    case isDirectory of
      False -&gt; return $ Node path []
      True -&gt; treeHelper fullPath &gt;&gt;= (return . Node (path ++ "/"))


If you run it, it might print something like this:

./
  dist/
    build/
      autogen/
        cabal_macros.h
        Paths_Tree.hs
      tree/
        tree
        tree-tmp/
          Main.hi
          Main.o
    package.conf.inplace
    setup-config
  Setup.lhs
  tree.cabal
  Tree.hs


I think this is pretty nifty. It's a toy program, so I purposefully left out stuff like error checking and symlink handling and etc. How would you improve this program? Am I duplicating functionality available in a library? Are there places where the code could be tighter? Is my Haskell style not up to snuff?
You can also use unfoldTree (or unfoldTreeM here) to build Data.Tree:

import Control.Monad
import System.Directory
import System.FilePath
import Data.Tree


dirTree :: FilePath -&gt; IO (Tree FilePath)
dirTree root = unfoldTreeM step (root,root)
    where step (f,c) = do
            fs &lt;- getDirectoryContents f
            ds &lt;- filterM doesDirectoryExist fs
            return (c, [(f &lt;/&gt; d, d) | d &lt;- ds, d /= "." &amp;&amp; d /= ".."])

main :: IO ()
main = do
  t &lt;- dirTree "."
  putStrLn $ drawTree t


Update: As Björn Lindqvist correctly noted in his edit suggestion, my use of doesDirectoryExist doesn't work here (it uses directory names instead of directory paths).

The correct version of dirTree would be:

dirTree :: FilePath -&gt; IO (Tree FilePath)
dirTree root = unfoldTreeM step (root,root)
    where step (p, c) = do
            isDirectory &lt;- doesDirectoryExist p
            fs &lt;- if isDirectory then getDirectoryContents p else return []
            return (c, [(p &lt;/&gt; f, f) | f &lt;- fs, f `notElem` [".", ".."]])