
Hi Neil, I'm having some problems using filepath in GHC. I think some of these we might have discussed before, but if so I've forgotten what the conclusion was, if any, and I can't see the answers in the docs. The first is this: Prelude System.FilePath> "." > "foo" "./foo" which means we get things like [2 of 2] Compiling GHC.Foo ( ./GHC/Foo.hs, ./GHC/Foo.o ) rather than [2 of 2] Compiling GHC.Foo ( GHC/Foo.hs, GHC/Foo.o ) Is there a reason the result shouldn't be "foo"? Possibly relatedly, the current directory seems to be "" rather than ".". This turns up in at least a couple of areas: Prelude System.FilePath> normalise "." "" Prelude System.FilePath> splitFileName "foo" ("","foo") I expected "." and "./" respectively, rather than the ""s. This causes problems when giving arguments to library functions or programs, which generally want "." or "./" rather than "", and if you are displaying a directory to the user then you again probably don't want to print "". As an example, I had a problem where GHC was running "gcc -I foo" rather than "gcc -I. foo". This looks like a straightforward bug to me: Prelude System.FilePath> normalise "./" "/" I'm not convinced by Prelude System.FilePath> isValid "" True I don't think I could "create a file like it". Finally, could splitSearchPath please take an extra argument, a list of FilePaths, to be appended to the result if it ends with a : or ; (as appropriate)? Thanks Ian

Ian Lynagh wrote:
I'm having some problems using filepath in GHC. [...]
I'm sure this has been said before: using the type string to avoid an ADT is generally a bad idea. (it's the "primitive obsession" code smell.) There are well-established "file path" ADT designs, e.g. why don't we rather literally copy http://java.sun.com/javase/6/docs/api/java/io/File.html ? Best regards, Johannes.

On Sun, 9 Dec 2007, Johannes Waldmann wrote:
Ian Lynagh wrote:
I'm having some problems using filepath in GHC. [...]
I'm sure this has been said before: using the type string to avoid an ADT is generally a bad idea. (it's the "primitive obsession" code smell.)
I want to second this, since e.g. the current directory is not in all OSs denoted by '.'. E.g. in good old AmigaOS (where sadly I don't know a Haskell implementation for, yet) the current directory is represented by the empty string. It seems to me that denoting the current directory by "." is rather Windows/Unix-centric.

On 2007-12-09, Johannes Waldmann
Ian Lynagh wrote:
I'm having some problems using filepath in GHC. [...]
I'm sure this has been said before: using the type string to avoid an ADT is generally a bad idea. (it's the "primitive obsession" code smell.)
I hate the term "code smell". It's a quick inelegant phrase designed to shutdown discussion, rather than actually pointing out what's wrong with a design or a given section of code. For an OS interface, it makes sense to actually give something that corresponds directly to what that OS interface is. In this case there are actually several different interfaces. Unix uses byte sequences with 47 (i.e. '/') being treated specially, and a 0 terminator. Windows has several different interfaces.
There are well-established "file path" ADT designs, e.g. why don't we rather literally copy http://java.sun.com/javase/6/docs/api/java/io/File.html ?
Because they don't provide full compatibility for all platforms, and the path handling can end up being very surprising to experienced users of these platforms. -- Aaron Denney -><-

Hi
I'm having some problems using filepath in GHC. [...]
I'm sure this has been said before: using the type string to avoid an ADT is generally a bad idea.
It's been said loads of times before. However, at this point we've got a FilePath library based on type FilePath = String in the base libraries.
There are well-established "file path" ADT designs, e.g. why don't we rather literally copy http://java.sun.com/javase/6/docs/api/java/io/File.html ?
I recommend someone actually implements one of these API's in Haskell and uploads it to Hackage. For bonus points, take an existing Haskell program and port its FilePath usage to your new library then try and convince the author to accept a patch - if the result is a cleaner/shorter code base you should have a chance. Thanks Neil

"Neil Mitchell"
e.g. why don't we rather literally copy http://java.sun.com/javase/6/docs/api/java/io/File.html ?
I recommend someone actually implements one of these API's in Haskell and uploads it to Hackage. For bonus points, take an existing Haskell program and port its FilePath [...]
And please hurry! :-) I tend to use FilePath as just a newtype, i.e. I hardly use any other functions from the library, but use it in type signatures to make it clear what this particular string is for. I won't quite say the current library encourages this, but it clearly doesn't discourage it either... -k -- If I haven't seen further, it is by standing in the footprints of giants

Neil Mitchell wrote:
I'm having some problems using filepath in GHC. [...] I'm sure this has been said before: using the type string to avoid an ADT is generally a bad idea.
It's been said loads of times before. However, at this point we've got a FilePath library based on type FilePath = String in the base libraries.
which is unfortunate. I guess life would be much easier with an ADT. Slightly OT, an ADT is probably the only way to deal with files on the classic MacOS. While it had some crude form of file paths (with ":" as separator), the "real" file operations used abstract references to directories and files (implemented as integer IDs). When moving a file, it would keep its ID, which means that aliases (aka "symbolic links") wouldn't break when moving their destination around. This behavior is still available on MacOS X, there are two forms of symbolic links: POSIX style symlinks and those aliases which are generated by the Finder. Also, moving a file while editing it is possible, too. Regards, apfelmus

On Sun, 2007-12-09 at 13:34 +0000, Ian Lynagh wrote:
Hi Neil,
I'm having some problems using filepath in GHC. I think some of these we might have discussed before, but if so I've forgotten what the conclusion was, if any, and I can't see the answers in the docs.
The first is this: Prelude System.FilePath> "." > "foo" "./foo" which means we get things like [2 of 2] Compiling GHC.Foo ( ./GHC/Foo.hs, ./GHC/Foo.o ) rather than [2 of 2] Compiling GHC.Foo ( GHC/Foo.hs, GHC/Foo.o ) Is there a reason the result shouldn't be "foo"?
The function that does that is normalise. It strips leading . and colapses things like foo/../
Possibly relatedly, the current directory seems to be "" rather than ".". This turns up in at least a couple of areas: Prelude System.FilePath> normalise "." ""
That's just a bug. I've reported that before. I though it was fixed but seems not. We had that problem in Cabal where people specified . as one of the search dirs. Duncan

On Sun, Dec 09, 2007 at 03:57:17PM +0000, Duncan Coutts wrote:
On Sun, 2007-12-09 at 13:34 +0000, Ian Lynagh wrote:
The first is this: Prelude System.FilePath> "." > "foo" "./foo" which means we get things like [2 of 2] Compiling GHC.Foo ( ./GHC/Foo.hs, ./GHC/Foo.o ) rather than [2 of 2] Compiling GHC.Foo ( GHC/Foo.hs, GHC/Foo.o ) Is there a reason the result shouldn't be "foo"?
The function that does that is normalise. It strips leading . and colapses things like foo/../
If I start off with filepaths in normal form then I don't think I should need to normalise the result after using a function like >, though.
Possibly relatedly, the current directory seems to be "" rather than ".". This turns up in at least a couple of areas: Prelude System.FilePath> normalise "." ""
That's just a bug.
The "Possibly relatedly" is because currently filepath doesn't think that "." is in normal form (which breaks my argument above), but IMO it should be. Thanks Ian

The first is this: Prelude System.FilePath> "." > "foo" "./foo" which means we get things like [2 of 2] Compiling GHC.Foo ( ./GHC/Foo.hs, ./GHC/Foo.o ) rather than [2 of 2] Compiling GHC.Foo ( GHC/Foo.hs, GHC/Foo.o ) Is there a reason the result shouldn't be "foo"?
./foo and foo are not interchangeable: $ cat >s #!/bin/sh echo "hi" $ s bash: s: command not found $ ./s bash: ./s: Permission denied $ chmod +x s $ s bash: s: command not found $ ./s hi claus

On Sun, Dec 09, 2007 at 06:11:13PM -0000, Claus Reinke wrote:
The first is this: Prelude System.FilePath> "." > "foo" "./foo" which means we get things like [2 of 2] Compiling GHC.Foo ( ./GHC/Foo.hs, ./GHC/Foo.o ) rather than [2 of 2] Compiling GHC.Foo ( GHC/Foo.hs, GHC/Foo.o ) Is there a reason the result shouldn't be "foo"?
./foo and foo are not interchangeable:
If we want to take that position then we need to fix normalise: Prelude System.FilePath> normalise "./foo" "foo" I'm not convinced that we do, though. Thanks Ian

Hello Claus, Sunday, December 9, 2007, 9:11:13 PM, you wrote:
./foo and foo are not interchangeable:
... in bash. i think it's mainly important that these filenames denote the same files for the OS file API (open,rename,delete...) -- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com

Claus Reinke wrote:
./foo and foo are not interchangeable:
Yes, they are. What you have described is the behaviour of the Bourne shell when it parses a line and searches for a command. If it sees a path component in a command name, it searches from either the filesystem root or the current directory. Otherwise, it uses $PATH, which does not usually contain ".", for security reasons. As you can now imagine, this has no relation to how a Haskell program should be manipulating paths.

On Sun, Dec 09, 2007 at 11:47:43AM -0800, Bryan O'Sullivan wrote:
As you can now imagine, this has no relation to how a Haskell program should be manipulating paths.
It also affects rawSystem: $ ghc -e 'System.Cmd.rawSystem "s" []' ExitFailure 127 $ ghc -e 'System.Cmd.rawSystem "./s" []' hi ExitSuccess but I'm not convinced that isn't a bug too. Thanks Ian

On Sun, 2007-12-09 at 20:00 +0000, Ian Lynagh wrote:
On Sun, Dec 09, 2007 at 11:47:43AM -0800, Bryan O'Sullivan wrote:
As you can now imagine, this has no relation to how a Haskell program should be manipulating paths.
It also affects rawSystem:
$ ghc -e 'System.Cmd.rawSystem "s" []' ExitFailure 127 $ ghc -e 'System.Cmd.rawSystem "./s" []' hi ExitSuccess
but I'm not convinced that isn't a bug too.
That is because it also uses the system path. See execvp vs execve. Duncan

On Sun, Dec 09, 2007 at 08:20:15PM +0000, Duncan Coutts wrote:
On Sun, 2007-12-09 at 20:00 +0000, Ian Lynagh wrote:
On Sun, Dec 09, 2007 at 11:47:43AM -0800, Bryan O'Sullivan wrote:
As you can now imagine, this has no relation to how a Haskell program should be manipulating paths.
It also affects rawSystem:
$ ghc -e 'System.Cmd.rawSystem "s" []' ExitFailure 127 $ ghc -e 'System.Cmd.rawSystem "./s" []' hi ExitSuccess
but I'm not convinced that isn't a bug too.
That is because it also uses the system path. See execvp vs execve.
Right, but is that what it ought to do? Thanks Ian

On Sun, 2007-12-09 at 22:08 +0000, Ian Lynagh wrote:
On Sun, Dec 09, 2007 at 08:20:15PM +0000, Duncan Coutts wrote:
On Sun, 2007-12-09 at 20:00 +0000, Ian Lynagh wrote:
On Sun, Dec 09, 2007 at 11:47:43AM -0800, Bryan O'Sullivan wrote:
As you can now imagine, this has no relation to how a Haskell program should be manipulating paths.
It also affects rawSystem:
$ ghc -e 'System.Cmd.rawSystem "s" []' ExitFailure 127 $ ghc -e 'System.Cmd.rawSystem "./s" []' hi ExitSuccess
but I'm not convinced that isn't a bug too.
That is because it also uses the system path. See execvp vs execve.
Right, but is that what it ought to do?
If not there's no way to execute a program that lives in the current directory if there is also a program by that name earlier on the search path, like in /bin. If exec "./true" and "true" have to run the same program. So the point is when it comes to search paths, a ./ path is not really relative at all. Duncan

On Sun, Dec 09, 2007 at 10:49:02PM +0000, Duncan Coutts wrote:
On Sun, 2007-12-09 at 22:08 +0000, Ian Lynagh wrote:
On Sun, Dec 09, 2007 at 08:20:15PM +0000, Duncan Coutts wrote:
On Sun, 2007-12-09 at 20:00 +0000, Ian Lynagh wrote:
On Sun, Dec 09, 2007 at 11:47:43AM -0800, Bryan O'Sullivan wrote:
As you can now imagine, this has no relation to how a Haskell program should be manipulating paths.
It also affects rawSystem:
$ ghc -e 'System.Cmd.rawSystem "s" []' ExitFailure 127 $ ghc -e 'System.Cmd.rawSystem "./s" []' hi ExitSuccess
but I'm not convinced that isn't a bug too.
That is because it also uses the system path. See execvp vs execve.
Right, but is that what it ought to do?
If not there's no way to execute a program that lives in the current directory if there is also a program by that name earlier on the search path, like in /bin.
I'm not sure we're on the same wavelength. What I mean is, should rawSystem use execvp rather than execv? Or should both options be available?
If exec "./true" and "true" have to run the same program.
So the point is when it comes to search paths, a ./ path is not really relative at all.
I don't understand that. Thanks Ian

On Sun, 2007-12-09 at 23:35 +0000, Ian Lynagh wrote:
but I'm not convinced that isn't a bug too.
That is because it also uses the system path. See execvp vs execve.
Right, but is that what it ought to do?
If not there's no way to execute a program that lives in the current directory if there is also a program by that name earlier on the search path, like in /bin.
I'm not sure we're on the same wavelength. What I mean is, should rawSystem use execvp rather than execv? Or should both options be available?
We certainly need the standard rawSystem to use the path (ie execvp) or everything will break (you'd not be able to run "sh" it'd have to be "/bin/sh"). It's certainly reasonable to provide the alternative that does no patch searching though I doubt it's terribly useful since people can already specify absolute paths, or paths relative to the current directory so the only difference is in this corner case. I don't think it affects much else.
If exec "./true" and "true" have to run the same program.
So the point is when it comes to search paths, a ./ path is not really relative at all.
I don't understand that.
Ok, so for the purposes of path searching there are two kinds of paths: absolute: /bin/sh ./sh They are relative to the root and current directory respectively. There is no ambiguity there. We know the fs root and the current directory, so that refers unambiguously to a specific file. relative: sh This is relative to each dir in the search path. So could refer to any of: /bin/sh /usr/bin/sh ./sh or whatever the system's search path is.
From man 3 execvp
Special semantics for execlp() and execvp() The functions execlp() and execvp() will duplicate the actions of the shell in searching for an executable file if the specified filename does not contain a slash (/) character. The search path is the path specified in the environment by the PATH variable. If this variable isn't specified, the default path ``:/bin:/usr/bin'' is used. NOTES On some other systems the default path (used when the environment does not contain the variable PATH) has the current working directory listed after /bin and /usr/bin, as an anti-Trojan-horse measure. Linux uses here the traditional "current directory first" default path. So it's very clear that posix treats "./blah" differently from "blah" for this function at least. It's true that the kernel does not and we do not need to either. We can have a rawSystem version that does searching and a version that does no searching. Then we could say that the version that does searching will look for "./blah" the search path, which may not contain ".". I don't know if that wouldn't be more confusing. Duncan

./foo and foo are not interchangeable: Yes, they are.
..
What you have described is the behaviour of the Bourne shell when it parses a line and searches for a command. If it sees a path component in a command name, it searches from either the filesystem root or the current directory. Otherwise, it uses $PATH, which does not usually contain ".", for security reasons.
As you can now imagine, this has no relation to how a Haskell program should be manipulating paths.
alas, my imagination is lacking: system "bash -c ./s" my point is that > is a path constructor - it should isolate me from /vs\, but nothing else; actually, since i often work in mixed systems, > should not even try to guess the direction of slashes for me, it should just allow me to abstract over that decision, so that i can make it in a single place, consistently for all my paths (and if the next run or another part of the same program wants the slashes the other way round, that should also be possible. if i want anything more intelligent, i can use normalizeXXX, where XXX specifies the norm of the day. claus

On 2007-12-09, Bryan O'Sullivan
Claus Reinke wrote:
./foo and foo are not interchangeable:
Yes, they are.
What you have described is the behaviour of the Bourne shell when it parses a line and searches for a command. If it sees a path component in a command name, it searches from either the filesystem root or the current directory. Otherwise, it uses $PATH, which does not usually contain ".", for security reasons.
Actually, this is done by certain variants of the exec*() family of system/library calls.
As you can now imagine, this has no relation to how a Haskell program should be manipulating paths.
What, Haskell programs won't ever be manipulating paths of executables it will be calling? -- Aaron Denney -><-

Hi
The first is this: Prelude System.FilePath> "." > "foo" "./foo" which means we get things like [2 of 2] Compiling GHC.Foo ( ./GHC/Foo.hs, ./GHC/Foo.o ) rather than [2 of 2] Compiling GHC.Foo ( GHC/Foo.hs, GHC/Foo.o ) Is there a reason the result shouldn't be "foo"?
My best guess is that "." > "foo" should equal "./foo". However, I'm not overly wedded to this behaviour, so if the general consensus is the other way, I'll happily implement that.
Possibly relatedly, the current directory seems to be "" rather than ".". This turns up in at least a couple of areas: Prelude System.FilePath> normalise "." ""
I would say this is a bug, and that normalise "." should be ".".
Prelude System.FilePath> splitFileName "foo" ("","foo")
I'd say this was expected. In a similar way, takeDirectory "foo" gives "", not "./".
This looks like a straightforward bug to me: Prelude System.FilePath> normalise "./" "/"
Woops. I'd have to agree with you there.
I'm not convinced by Prelude System.FilePath> isValid "" True I don't think I could "create a file like it".
Fair enough, I agree with that one too.
Finally, could splitSearchPath please take an extra argument, a list of FilePaths, to be appended to the result if it ends with a : or ; (as appropriate)?
Perhaps call it splitSearchPathIncluding? It seems like splitSearchPath is a sensible enough function already, and the extra argument won't be typically appropriate. Thanks Neil

On 2007-12-09, Neil Mitchell
Hi
The first is this: Prelude System.FilePath> "." > "foo" "./foo" which means we get things like [2 of 2] Compiling GHC.Foo ( ./GHC/Foo.hs, ./GHC/Foo.o ) rather than [2 of 2] Compiling GHC.Foo ( GHC/Foo.hs, GHC/Foo.o ) Is there a reason the result shouldn't be "foo"?
My best guess is that "." > "foo" should equal "./foo". However, I'm not overly wedded to this behaviour, so if the general consensus is the other way, I'll happily implement that.
Concur that "./foo" is correct.
Possibly relatedly, the current directory seems to be "" rather than ".". This turns up in at least a couple of areas: Prelude System.FilePath> normalise "." ""
I would say this is a bug, and that normalise "." should be ".".
Yep.
Prelude System.FilePath> splitFileName "foo" ("","foo")
I'd say this was expected. In a similar way, takeDirectory "foo" gives "", not "./".
If the result type of splitFileName must be (String, String), I think this is the best embedding of no directory into String. However, I think that this might be the wrong API. (Maybe String, String) would be better. (And Maybe String for takeDirectory). -- Aaron Denney -><-

On Sun, Dec 09, 2007 at 09:17:56PM +0000, Neil Mitchell wrote:
The first is this: Prelude System.FilePath> "." > "foo" "./foo" which means we get things like [2 of 2] Compiling GHC.Foo ( ./GHC/Foo.hs, ./GHC/Foo.o ) rather than [2 of 2] Compiling GHC.Foo ( GHC/Foo.hs, GHC/Foo.o ) Is there a reason the result shouldn't be "foo"?
My best guess is that "." > "foo" should equal "./foo". However, I'm not overly wedded to this behaviour, so if the general consensus is the other way, I'll happily implement that.
Possibly relatedly, the current directory seems to be "" rather than ".". This turns up in at least a couple of areas: Prelude System.FilePath> normalise "." ""
I would say this is a bug, and that normalise "." should be ".".
The two answers above together mean that the property \x y -> let x' = normalise x y' = normalise y z = x' > y' in z == normalise z' doesn't hold (for x = ".", y = "foo"), but it is one I would expect to hold.
Prelude System.FilePath> splitFileName "foo" ("","foo")
I'd say this was expected. In a similar way, takeDirectory "foo" gives "", not "./".
I'd expect takeDirectory "foo" to be "." and dropFileName "foo" to be "./" too.
Finally, could splitSearchPath please take an extra argument, a list of FilePaths, to be appended to the result if it ends with a : or ; (as appropriate)?
Perhaps call it splitSearchPathIncluding?
Another name would be fine with me. I don't think "Including" is quite right, as it implies it always includes it. I can't think of a good name right now, though. Thanks Ian

Ian Lynagh wrote:
On Sun, Dec 09, 2007 at 09:17:56PM +0000, Neil Mitchell wrote:
The first is this: Prelude System.FilePath> "." > "foo" "./foo" which means we get things like [2 of 2] Compiling GHC.Foo ( ./GHC/Foo.hs, ./GHC/Foo.o ) rather than [2 of 2] Compiling GHC.Foo ( GHC/Foo.hs, GHC/Foo.o ) Is there a reason the result shouldn't be "foo"? My best guess is that "." > "foo" should equal "./foo". However, I'm not overly wedded to this behaviour, so if the general consensus is the other way, I'll happily implement that.
Possibly relatedly, the current directory seems to be "" rather than ".". This turns up in at least a couple of areas: Prelude System.FilePath> normalise "." "" I would say this is a bug, and that normalise "." should be ".".
The two answers above together mean that the property
\x y -> let x' = normalise x y' = normalise y z = x' > y' in z == normalise z'
doesn't hold (for x = ".", y = "foo"), but it is one I would expect to hold.
I think you have an extra ' on the last line of the code above. But anyway, why do you expect that to hold? There's an implicit context in the definition of normalise. The invariant we want is that I can use "normalise x" wherever I use "x" and get the same result. But it can't be that simple, because we can always tell the difference by looking at the string, so we have to define the allowable context carefully. It looks like the context we're aiming for is "all functions in System.FilePath, System.IO and System.Directory". Which would make "./foo" the same as "foo": as far as the OS is concerned, these always mean the same thing. execvp mucks things up by giving a special meaning to "foo". But all this means is that execvp (and hence rawSystem) is a context that can tell the difference between "normalise x" and "x", and therefore you shouldn't call normalise on the path given to rawSystem.
Prelude System.FilePath> splitFileName "foo" ("","foo")
I'd say this was expected. In a similar way, takeDirectory "foo" gives "", not "./".
I'd expect takeDirectory "foo" to be "." and dropFileName "foo" to be "./" too.
Right, me too. But this is more of a pervasive design choice. It looks like System.FilePath consistently treats "" as a valid FilePath meaning "the current directory", and this would mean changing that policy. As far as System.Directory is concerned, "" is not a valid FilePath (i.e. you can't say getDirectoryContents ""), and the current directory is denoted by ".". So it would seem sensible for System.FilePath to behave in the same way. But then (splitFileName "foo") would have to give (".", "foo"), and therefore uncurry (>) (splitFileName x) /= x which seems odd. But perhaps the right property is normalise (uncurry (>) (splitFileName x)) == normalise x Cheers, Simon

On Tue, Dec 11, 2007 at 02:22:14PM +0000, Simon Marlow wrote:
Ian Lynagh wrote:
The two answers above together mean that the property
\x y -> let x' = normalise x y' = normalise y z = x' > y' in z == normalise z'
doesn't hold (for x = ".", y = "foo"), but it is one I would expect to hold.
I think you have an extra ' on the last line of the code above.
Yes, thanks.
But anyway, why do you expect that to hold?
It just seems obvious to me that operators on a datatype would maintain normalisedness of that type. If it's not obvious to you then I'm not sure I can explain why; it's like explaining to someone why you find something beautiful. The only slightly similar thing I can think of is how the primitive operations of Ratio keep the components normalised, but that's slightly different as it's an abstract datatype, everything must be normalised on the way in, and normalisation prevents internal overflows.
Prelude System.FilePath> splitFileName "foo" ("","foo") I'd say this was expected. In a similar way, takeDirectory "foo" gives "", not "./".
I'd expect takeDirectory "foo" to be "." and dropFileName "foo" to be "./" too.
Right, me too.
But this is more of a pervasive design choice. It looks like System.FilePath consistently treats "" as a valid FilePath meaning "the current directory", and this would mean changing that policy.
As far as System.Directory is concerned, "" is not a valid FilePath (i.e. you can't say getDirectoryContents ""), and the current directory is denoted by ".". So it would seem sensible for System.FilePath to behave in the same way.
But then (splitFileName "foo") would have to give (".", "foo"), and
All agreed.
therefore
uncurry (>) (splitFileName x) /= x
which seems odd.
If it helps, I think that that seeming odd is similar to why I expect my property above to hold.
But perhaps the right property is
normalise (uncurry (>) (splitFileName x)) == normalise x
That should certainly hold, but I think \x -> let x' = normalise x in uncurry (>) (splitFileName x') == x' should too (and it does if "." > p == p) Thanks Ian

Ian Lynagh wrote:
On Tue, Dec 11, 2007 at 02:22:14PM +0000, Simon Marlow wrote:
Ian Lynagh wrote:
The two answers above together mean that the property
\x y -> let x' = normalise x y' = normalise y z = x' > y' in z == normalise z'
doesn't hold (for x = ".", y = "foo"), but it is one I would expect to hold. I think you have an extra ' on the last line of the code above.
Yes, thanks.
But anyway, why do you expect that to hold?
It just seems obvious to me that operators on a datatype would maintain normalisedness of that type. If it's not obvious to you then I'm not sure I can explain why; it's like explaining to someone why you find something beautiful.
The only reason *not* to make "." > p == p is that in some contexts (e.g. rawSystem) "foo" is not the same as "./foo". You could argue that rawSystem is therefore not taking a proper FilePath as an argument, it is taking something else, and we should be more explicit about it, data ExecutableSpec = FindOnPath String | Relative FilePath this certainly has some merit. However, if we don't change the API of rawSystem, then having "." > p == p would make it somewhat harder to construct the argument to rawSystem, you would have to do it manually: "." ++ pathSeparator : "foo". I guess that seems reasonable, given that rawSystem is really the exception. Cheers, Simon

Hello Simon, Wednesday, December 12, 2007, 12:23:27 PM, you wrote:
I guess that seems reasonable, given that rawSystem is really the exception.
it's just not file-manipulation OS call, but something else, with its own filename syntax -- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com

On Wed, Dec 12, 2007 at 09:23:27AM +0000, Simon Marlow wrote:
Ian Lynagh wrote:
On Tue, Dec 11, 2007 at 02:22:14PM +0000, Simon Marlow wrote:
Ian Lynagh wrote:
The two answers above together mean that the property
\x y -> let x' = normalise x y' = normalise y z = x' > y' in z == normalise z'
doesn't hold (for x = ".", y = "foo"), but it is one I would expect to hold. I think you have an extra ' on the last line of the code above.
Yes, thanks.
But anyway, why do you expect that to hold?
It just seems obvious to me that operators on a datatype would maintain normalisedness of that type. If it's not obvious to you then I'm not sure I can explain why; it's like explaining to someone why you find something beautiful.
The only reason *not* to make "." > p == p is that in some contexts (e.g. rawSystem) "foo" is not the same as "./foo".
In that case surely we'd need to change Prelude System.FilePath> normalise "./foo" "foo" too, and the property above would still hold?
You could argue that rawSystem is therefore not taking a proper FilePath as an argument, it is taking something else, and we should be more explicit about it,
data ExecutableSpec = FindOnPath String | Relative FilePath
this certainly has some merit.
However, if we don't change the API of rawSystem, then having "." > p == p would make it somewhat harder to construct the argument to rawSystem, you would have to do it manually: "." ++ pathSeparator : "foo".
I guess that seems reasonable, given that rawSystem is really the exception.
OK, I think we are in agreement then. Thanks Ian

Simon Marlow wrote:
Ian Lynagh wrote:
On Sun, Dec 09, 2007 at 09:17:56PM +0000, Neil Mitchell wrote:
Prelude System.FilePath> splitFileName "foo" ("","foo")
I'd say this was expected. In a similar way, takeDirectory "foo" gives "", not "./".
I'd expect takeDirectory "foo" to be "." and dropFileName "foo" to be "./" too.
Right, me too.
But this is more of a pervasive design choice. It looks like System.FilePath consistently treats "" as a valid FilePath meaning "the current directory", and this would mean changing that policy.
As far as System.Directory is concerned, "" is not a valid FilePath (i.e. you can't say getDirectoryContents ""), and the current directory is denoted by ".". So it would seem sensible for System.FilePath to behave in the same way.
[ reviving an old thread ] I've just come across this again. It's quite inconvenient that takeDirectory "foo" == "" because it means that you can't say doesDirectoryExist (takeDirectory f) in other words, takeDirectory doesn't return a valid directory, at least as far as the OS is concerned. Neil - is it possible to change this? Cheers, Simon

Hi Simon
I've just come across this again. It's quite inconvenient that
takeDirectory "foo" == ""
because it means that you can't say
doesDirectoryExist (takeDirectory f)
Yes, I came across this the other day. And am leaning towards agreeing with you. I think its reasonable if splitFileName keeps the same behaviour, and dropFileName as well, but takeDirectory follows the new behaviour. So: takeDirectory "foo" = "." takeDirectory "foo/bar" = "foo" dropFileName "foo" = "" dropFilename "foo/bar" = "foo/" Does that sound like a plan that suits everyone? Thanks Neil

Neil Mitchell wrote:
Hi Simon
I've just come across this again. It's quite inconvenient that
takeDirectory "foo" == ""
because it means that you can't say
doesDirectoryExist (takeDirectory f)
Yes, I came across this the other day. And am leaning towards agreeing with you.
I think its reasonable if splitFileName keeps the same behaviour, and dropFileName as well, but takeDirectory follows the new behaviour. So:
takeDirectory "foo" = "." takeDirectory "foo/bar" = "foo"
dropFileName "foo" = "" dropFilename "foo/bar" = "foo/"
Does that sound like a plan that suits everyone?
That seems strange, actually. I would expect, perhaps naively, that takeDirectory == fst . splitFileName dropFileName == takeDirectory is there a good reason for these not to be true? Cheers, Simon

Hi
That seems strange, actually. I would expect, perhaps naively, that
takeDirectory == fst . splitFileName dropFileName == takeDirectory
is there a good reason for these not to be true?
The original motivation was that: uncurry (++) . splitFileName === id And hence dropFileName = fst . splitFileName However, in practice I think its more useful to use takeDirectory, since usually the trailing slash gets in the way. Those were the original reasons, but it is possible they were bad reasons from the beginning. Thanks Neil
participants (13)
-
Aaron Denney
-
apfelmus
-
Bryan O'Sullivan
-
Bulat Ziganshin
-
Claus Reinke
-
Duncan Coutts
-
Henning Thielemann
-
Ian Lynagh
-
Johannes Waldmann
-
Ketil Malde
-
Neil Mitchell
-
Simon Marlow
-
Udo Stenzel