On 14 Nov 2009, at 14:02, Duncan Coutts wrote:
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
Yep, that's the idea.
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.
Yes.
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
"C:\".
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
directory.
You would say "Main.hs" :: RelFile
and
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!)
and
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.
Ah, ok, I see. I can see the attraction to that.
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.
Interesting.
One possible approach with pathtype as it stands would be a wrapper
module which hides "readFile :: AbsRelClass ar => FilePath ar -> IO String"
and exposes it only at the restricted type:
"readAbsFile :: AbsFile -> IO String; readAbsFile = readFile".
This blocks accidental reading of relative files and clients are forced to use
something like 'makeAbsoluteFromCwd'.
I don't think this is exactly the same as what you're suggesting (it doesn't
permit the "completion" process to be separated from the reading), but I think it
would be a way to catch some of the same bugs?
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.
I think this is a very important point. It's definitely a balancing act. I have
ondered whether even capturing Abs/Rel was overkill, but my current feeling
(not yet based on much real-world experience) is that it is worth it.
Cheers,
--Ben