Excellent. I've made some progress, but the biggest challenge right now is coming up with clean types.
1. AbstractFilePath abstracts over platforms
2. WindowsFilePath / PosixFilePath are exposed newtypes in case you need to deal with platform specific paths (eg. for tar)
3. Some filepath operations however are not just dealing with filepaths, but "strings". E.g.
https://hackage.haskell.org/package/filepath-1.4.2.1/docs/System-FilePath-Posix.html#v:splitSearchPath ... while not being strictly filepaths, these follow the same encoding caveats, but certain functions may or may not make sense on them (like 'isValid').
As such, the original proposal is missing a solution for this distinction. There aren't many options:
1. A type synonym: type AbstractFilePath = OsString (or vice versa). This is similar to 'type FilePath = String'. The disadvantage is that we lose the type level distinction and it's more like a hint.
2. Use another newtype. This may be unergonomic, given that they actually maintain the same invariants. AbstractFilePath does not ensure filepath validity anyway (since there is no definite list of what is allowed, only hints. Even the filesystem may have an impact on what is valid. As such we only maintain the encoding). This approach may be more interesting if AbstractFilePath had more invariants, such as relative vs absolute on type level (compare with Path and hpath libraries).
3. Provide only one type for both use cases and rename to e.g. OsString.
4. Convert non-filepath things to String or Text. This kinda goes against the original proposal of not losing the encoding information and not roundtripping through types.
I feel if the types aren't intuitive, adoption will be harder. So seemingly trivial things like type names are important.
As a reference point, rust has two types: OsString and Path:
https://doc.rust-lang.org/std/path/index.html where Path is basically a thin newtype wrapper, so corresponds to option 2.
Cheers,
Julian