I just started to learn Haskell, and i wrote a function that walks the directory tree recursively and pass the content of each directory to a callback function:
- The content of the directory is a tuple, containing a list of all the sub-directories, and a list of file names.
- If there is an error, the error is passed to the callback function instead of the directory content (it use
Either
).
So here is the type of the callback function:
callback :: FileName -> Either IOError ([FileName], [FileName]) -> IO ()
And here is the type of my walk
function:
walk :: FilePath -> (FilePath -> Either IOError ([FilePath], [FilePath]) -> IO ()) -> IO ()
And here is my complete code:
module Test where
import System.FilePath ((</>))
import Control.Monad (filterM, forM_, return)
import System.IO.Error (tryIOError, IOError)
import System.Directory (getDirectoryContents, doesFileExist, doesDirectoryExist)
myVisitor :: FilePath -> Either IOError ([FilePath], [FilePath]) -> IO ()
myVisitor path result = do
case result of
Left error -> do
putStrLn $ "I've tryed to look in " ++ path ++ "."
putStrLn $ "\tThere was an error: "
putStrLn $ "\t\t" ++ (show error)
Right (dirs, files) -> do
putStrLn $ "I've looked in " ++ path ++ "."
putStrLn $ "\tThere was " ++ (show $ length dirs) ++ " directorie(s) and " ++ (show $ length files) ++ " file(s):"
forM_ (dirs ++ files) (\x -> putStrLn $ "\t\t- " ++ x)
putStrLn ""
walk :: FilePath -> (FilePath -> Either IOError ([FilePath], [FilePath]) -> IO ()) -> IO ()
walk path visitor = do
result <- tryIOError listdir
case result of
Left error -> do
visitor path result
Right (dirs, files) -> do
visitor path result
forM_
(map (\x -> path </> x) dirs)
(\x -> walk x visitor)
where
listdir = do
entries <- (getDirectoryContents path) >>= filterHidden
subdirs <- filterM isDir entries
files <- filterM isFile entries
return (subdirs, files)
where
isFile entry = doesFileExist (path </> entry)
isDir entry = doesDirectoryExist (path </> entry)
filterHidden paths = do
return $ filter (\path -> head path /= '.') paths
main :: IO ()
main = do
walk "/tmp" myVisitor
walk "foo bar" myVisitor
putStrLn "Done :-/"
As you can see, there is a lot of code and i import
a lot of things...
I'm sure there is a better way to do that, and i'm interested in any
{idea,hint,tip}
to improve this piece of code.
Thanks in advance :-)