
b) Making (FindClause a) a mere type synonym of (FileInfo -> a) has the benefit that the user can choose whether he wants to use monads or applicative functors via the respective instances or whether he does not.
That's where I had started out, as a matter of fact.
or for general applicative functors as
(||) <$> ((== ".hs") <$> extension) <*> ((== ".lhs") <$> extension)
I don't find that very readable, I must confess.
Maybe unsafePerformIO is the best solution, because you may safely close the file after having evaluated
predicate (unsafePerformIO $ hGetContents handle) (fileinfo)
to weak head normal form, i.e. True or False. I think it fits perfectly.
In principle unsafeInterleaveIO $ readFile fileName ought to be better, because it will not try to open the file unless the predicate actually inspects it, and opening files is expensive. But it will also not close the file until a finalizer kicks in. A tidier approach might be: maybeH <- newIORef Nothing contents <- unsafeInterleaveIO $ do h <- openFile fileName ReadMode writeIORef maybeH (Just h) hGetContents h let result = predicate contents result `seq` readIORef maybeH >>= maybe (return ()) hClose That's a little cumbersome, but very appealing from the perspective of a user of the library. Unfortunately, it looks to me like it's not very safe; see below.
Using System.FilePath.Find.fold gives you both file status and file path but the ought-to-be equivalent approach of using foldl' on the list returned by find only gives the file path but no file status. So, the suggestion is to make find return a list of FileInfo
Let me pass an idea by you. There's a problem with augmenting FileInfo to potentially cause IO to occur as above (both with your original suggestion and my changes), no? We lose the ability to control when a file might be opened, and when it ought to be closed. If that were the case, the fold interface would have the same problem, if it decided for some reason to sock away FileInfo values in an accumulator and work on them after the fold had completed.
Of course, this leads to the question whether find should be factored even more into generate & prune
find r p dir = map filepath . filter p . directoryWalk r dir
with the intention that System.FilePath.Find only exports directoryWalk.
That's a nice idea, but subject to the same problems as the fold interface would be if we added file contents to the interface. Your other observations regarding making a directory tree abstract, and instances of some of our favourite typeclasses, are very appealing. Now if only I could figure out a clean way to avoid bad things happening in the presence of that user-friendly IO code...