HiveBrain v1.2.0
Get Started
← Back to all entries
patternMinor

Recursive directory tree printer

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
recursiveprintertreedirectory

Problem

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  Tree FilePath -> IO ()
printTree indent tree = do
  putStrLn $ (replicate indent ' ' ++ rootLabel tree)
  mapM_ (printTree $ indent + 2) (subForest tree)

treeHelper :: FilePath -> IO [Tree FilePath]
treeHelper path =
  let filter' = filter (`notElem` [".", ".."]) in
  do
    contents  FilePath -> IO (Tree FilePath)
tree parent path =
  let fullPath = (parent ++ "/" ++ path) in
  do
    isDirectory  return $ Node path []
      True -> treeHelper fullPath >>= (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?

Solution

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 -> IO (Tree FilePath)
dirTree root = unfoldTreeM step (root,root)
    where step (f,c) = do
            fs  d, d) | d <- ds, d /= "." && d /= ".."])

main :: IO ()
main = do
  t <- 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 -> IO (Tree FilePath)
dirTree root = unfoldTreeM step (root,root)
    where step (p, c) = do
            isDirectory  f, f) | f <- fs, f `notElem` [".", ".."]])

Code Snippets

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


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

main :: IO ()
main = do
  t <- dirTree "."
  putStrLn $ drawTree t
dirTree :: FilePath -> IO (Tree FilePath)
dirTree root = unfoldTreeM step (root,root)
    where step (p, c) = do
            isDirectory <- doesDirectoryExist p
            fs <- if isDirectory then getDirectoryContents p else return []
            return (c, [(p </> f, f) | f <- fs, f `notElem` [".", ".."]])

Context

StackExchange Code Review Q#8431, answer score: 8

Revisions (0)

No revisions yet.