apfelmus' interface degustation - System.FilePath.Find

Bryan O'Sullivan bos at serpentine.com
Fri Jun 29 20:26:35 EDT 2007

>  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...


More information about the Libraries mailing list