FilePath
The filepath
package provides functionality for manipulating FilePath
values, and is shipped with both GHC and the Haskell Platform. It provides three modules:
System.FilePath.Posix
manipulates POSIX/Linux style FilePath
values (with /
as the path separator).
System.FilePath.Windows
manipulates Windows style FilePath
values (with either \
or /
as the path separator, and deals with drives).
System.FilePath
is an alias for the module appropriate to your platform.
All three modules provide the same API, and the same documentation (calling out differences in the different variants).
Should FilePath
be an abstract data type?
The answer for this library is “no”. While an abstract FilePath
has some advantages (mostly type safety), it also has some disadvantages:
- In Haskell the definition is
type FilePath = String
, and all file-oriented functions operate on this type alias, e.g. readFile
/writeFile
. Any abstract type would require wrappers for these functions or lots of casts between String
and the abstraction.
- It is not immediately obvious what a
FilePath
is, and what is just a pure String
. For example, /path/file.ext
is a FilePath
. Is /
? /path
? path
? file.ext
? .ext
? file
?
- Often it is useful to represent invalid files, e.g.
/foo/*.txt
probably isn’t an actual file, but a glob pattern. Other programs use foo//bar
for globs, which is definitely not a file, but might want to be stored as a FilePath
.
- Some programs use syntactic non-semantic details of the
FilePath
to change their behaviour. For example, foo
, foo/
and foo/.
are all similar, and refer to the same location on disk, but may behave differently when passed to command-line tools.
- A useful step to introducing an abstract
FilePath
is to reduce the amount of manipulating FilePath
values like lists. This library hopes to help in that effort.
Developer notes
Most of the code is in System/FilePath/Internal.hs
which is #include
‘d into both System/FilePath/Posix.hs
and System/FilePath/Windows.hs
with the IS_WINDOWS
CPP define set to either True
or False
. This Internal module is a bit weird in that it isn’t really a Haskell module, but is more an include file.
The library has extensive doc tests. Anything starting with -- >
is transformed into a doc test as a predicate that must evaluate to True
. These tests follow a few rules:
- Tests prefixed with
Windows:
or Posix:
are only tested against that specific implementation - otherwise tests are run against both implementations.
- Any single letter variable, e.g.
x
, is considered universal quantification, and is checked with QuickCheck
.
- If
Valid x =>
appears at the start of a doc test, that means the property will only be tested with x
passing the isValid
predicate.
The tests can be generated by Generate.hs
in the root of the repo, and will be placed in tests/TestGen.hs
. The TestGen.hs
file is checked into the repo, and the CI scripts check that TestGen.hs
is in sync with what would be generated a fresh - if you don’t regenerate TestGen.hs
the CI will fail.
The .ghci
file is set up to allow you to type ghci
to open the library, then :go
will regenerate the tests and run them.