再帰的にディレクトリをたどる - その2

遅延評価をどうこうしようと思ったけど挫折。どっかを seq すりゃいいと思うんだけどなー。FilePath のリストを返さず、find 内で出力までやっちゃうようにすればすぐできそうなんだけど、それじゃ関数的じゃないよいなぁということで。

普通の find と比較していたら微妙に違うところがあったので、それを適当に修正した。

import System.Directory (doesFileExist, doesDirectoryExist,
                         getDirectoryContents)
import System.FilePath ((</>))
import Control.Monad (forM)
import Control.Exception (catch, evaluate, SomeException)
import System.Environment (getArgs)

import Prelude hiding (catch)

find :: FilePath -> IO [FilePath]
find p = do isDir <- doesDirectoryExist p
            if isDir
              then find' p
              else return [p]
  where find' dir = do names <- getDirectoryContents dir
                       let names' = filter (`notElem` [".", ".."]) names
                       paths <- forM names'
                                     (\name -> find (dir </> name))
                       return (dir : concat paths)

main = mapM_ putStrLn =<< find' =<< valid =<< head' =<< getArgs
  where head' xs = evaluate (head xs)
                   `catch`
                   (\e -> return (e::SomeException) >> return ".")
        valid p = do dir <- doesDirectoryExist p
                     file <- doesFileExist p
                     return (p, or [dir, file])
        find' (_, False) = return []
        find' (p, _)     = find p

アクションを途中で終了する関数ってないのかなぁ。Bool が False のときには終了、Maybe が Nothing がときには終了的なもの。汎用的な guard というかなんというか。