Proposal: System.FilePath: current directory should be ".", not ""

Duncan Coutts duncan.coutts at
Sat Nov 14 09:02:27 EST 2009

On Sat, 2009-11-14 at 12:42 +0000, Ben Moseley wrote:
> >  A lovely abstract type would be wonderful, but no one has yet  
> > released
> > such a library on hackage...
> I'm certainly not going to claim it's 'lovely' ... but there is now on  
> hackage a library which offers an abstract type:
> It's very early days, hasn't been used in anger, doesn't support a lot  
> of Windows things yet, but the basics do work.


> I'd be grateful for any comments (and of course patches!)

So the type distinctions you are making are:

      * whether a path refers to a file or to a directory
      * whether a path is relative or absolute

By relative and absolute it looks like you mean whether it starts with
the root "/" vs not. So "foo/bar.txt" and "./foo/bar.txt" are both
considered as relative.


> Oh, and....
> *System.Path> takeDirectory ("Main.hs"::RelFile)
> .


So in the similar design I was working on, instead of distinguishing
relative and absolute, I distinguish incomplete and complete. I
initially started with the relative/absolute distinction and moved onto
this one. It'd be interesting to see which is most useful in practise.

What I mean by complete is a path referring to an actual file/dir that
you could pass to a system call. This means they are anchored to some
point the system knows about, such a "." the current directory or "/" or

Then an incomplete path is one that is not anchored. Incomplete paths
become complete by </> them to a existing complete one (including the
current dir).

So the difference between this complete/incomplete notion and
relative/absolute is for paths that are relative to the current

You would say "Main.hs" :: RelFile 


readFile :: AbsRelClass ar => FilePath ar -> IO String

and so readFile "Main.hs" is ok, and reads the file "./Main.hs" in the
current directory.

I would probably say, "Main.hs" :: IncompleteFilePath

(though I think I used somewhat shorter type names than that!)


readFile :: CompleteFilePath -> IO String

and thus readFile "Main.hs" is not well typed, instead it would be:

readFile (currentDir </> "Main.hs")

because that gives us a complete path, and if we want that to be rooted
at the processes' current directory, then we do so explicitly.

My intuition with file paths in Cabal, is that this distinction would
catch more bugs. Ideally cabal's building code would be independent of
the current directory, but the fact that relative paths get
automagically completed to being relative to the current directory means
that it's very easy to break this property. The type distinction would
enforce it, and you could see explicitly where paths are deliberately
completed relative to the current directory (or some other root).

On the other hand it doesn't quite align with people's notion of
relative paths so people might hate it :-).

There are various other distinctions one could try to make, the question
becomes which type distinctions are useful and when does it just become
too much. For example one could distinguish file names (with no
directory part), or pure roots with no directory part (eg ".", "/", "C:
\", "//server/share/").


More information about the Libraries mailing list