
Chers amis of functionaλ cooking, aujourd'hui, le maître de cuisine is going to criticize System.FilePath.Find http://darcs.serpentine.com/filemanip/dist/doc/html/ /FileManip/System-FilePath-Find.html Is it seasoned too much with Curry and Monads? Pure or diluée with side effects? Apfelmus will tell you! And like every serious cooking critic, le maître thinks no end of himself so he can judge the haddock without eating the compiled meal. Who did not yet have to scan the file buffet for data of his gusto? This is what System.FilePath.Find helps you to do. It basically offers a function find :: RecursionPredicate -> FilterPredicate -> FilePath -> IO [FilePath] to lazily traverse a directory tree filtering out files that do not match the FilterPredicate. The RecursionPredicate decides whether to recurse into or a sub-directory or not (it could be mentioned explicitly that the predicate is only invoked on directories). A variant of find includes user-defined error handling. How to define predicates? Mon dieu! They are to be obtained from a monad FindClause a type FilterPredicate = FindClause Bool which offers operations like extension :: FindClause FilePath (==?) :: Eq a => FindClause a -> a -> FindClause Bool (||?) = liftM2 (||) One example predicate would be (extension ==? ".hs") ||? (extension ==? ".lhs") Cette soupe is much too monadic! In particular, we have the isomorphism (FileInfo -> a) ≅ FindClause a witnessed by the function pair (f,g) = ((`liftM` fileInfo), evalClause) Of course, the encapsulation was made to allow a formulation like extension ==? ".hs" which does not mention the FileInfo parameter. But alas, this can also be achieved more naturally by appealing to the instance Control.Applicative ((->) a) Abstracting the concrete representation away into FindClause hinders reuse of already existing functionality from System.FilePath, although the name "extension" is arguably more succinct than "takeExtension". The monad could make sense if the predicate might involve not only the file status but also looking inside the file. Returning all files with a certain magic number would be such a use case but System.FilePath.Find currently does not offer zis possibilité. Un autre point is that the library offers a function fold that is almost a foldl' over all files in a directory tree, but not quite: here, the fold goes over FileInfos but the list is a simple list of FilePaths. One has to foldM over the latter and get the file infos again to achieve the same effect. Le maître de cuisine thinks that these quirks stem from the fact that the file system lacks a purely functional design. The observation is that given persistence, reading does not need to be monadic. In other words, directory tree traversal might well return a list of files data File = File { contents :: String, status :: FileStatus } with contents and status. More generally, the traversal could be a side-effect-free traversal of a pure data structure. Of course, a purely functional file system needs further research, also due to performance requirements. Another direction of généralisation is that XML data is in principle similar to a file system. Shouldn't both have a common traversal interface? Last but not least, every modern cooking critic includes a Rating: λλλ Le maître awards three λ since the library uses a monad where none should be, but acknowledges that this was for the ability to formulate queries without plumbing the FileInfo-parameter everywhere. Bon appetit, apfelmus