
On Fri, 29 Oct 2004, Andrew Pimlott wrote:
Well, it's messy, but I don't believe it's that hard. Of course, you'll never be correct outside the IO monad, but if you enumerate your assumptions about what a path means outside of IO, I think you can come up with a fairly coherent set of operations.
Okay, lemme try... -- Given a directory path "dir" and a file/directory path "rel", -- returns a merged path "full" with the property that -- (cd dir; do_something_with rel) is equivalent to -- (do_something_with full). (XXX is a Nothing return ever needed?) mergePath :: FilePath -> FilePath -> FilePath -- Given a file/directory path "full", return a path "dir" -- referring to a directory which contains that file/dir as an -- entry, and a path "rel" such that "mergePath dir rel" refers -- to the same file/dir as "full". Returns NeedIO if the answer -- cannot be determined without IO operations, and NoParent if -- there cannot be an answer. (XXX hideous return type) splitPath :: FilePath -> SplitPathResult data SplitPathResult = NeedIO | NoParent | Split FilePath FilePath -- Given path referring to a file or directory, returns a -- canonicalized path, with the intent that two paths referring -- to the same file/directory will map to the same canonicalized -- path. Note that it is impossible to guarantee that the -- implication (same file/dir <=> same canonicalizedPath) holds -- in either direction: this function can make only a best-effort -- attempt. If the path does not refer to an existing file or -- directory, returns Nothing. (XXX not ideal, but what else can -- you do?) canonicalizePath :: FilePath -> IO (Maybe FilePath) -- Does as much as possible to simplify and regularize a path -- in a meaning-preserving manner, without IO monad operations. -- This function is idempotent, and is the identity on all -- FilePaths returned by FilePath library functions. -- (XXX there's not much this can do -- it's unsafe to convert -- "a/b/../c" to "a/c", for example, though I think "a/b/./c" -- to "a/b/c" is safe.) normalizePath :: FilePath -> FilePath -- Returns True if this path's meaning is independent of any OS -- "working directory", False if it isn't. (XXX would probably be -- better to make FilePath an abstract type and make it always -- independent of working directories.) isAbsolutePath :: FilePath -> Bool -- Returns True if the path always denotes a directory -- regardless of the current filesystem state. (XXX is this -- the right thing for eliminating . and .. from directory -- listings, or is another function needed?) isSpecialDirectory :: FilePath -> Bool -- Returns True if the path denotes a root. This implies -- isSpecialDirectory and isAbsolutePath, and a NoParent return -- from splitPath. isRoot :: FilePath -> Bool -- getRoots returns a list of paths denoting directories which exist -- and satisfy isRoot. Can't guarantee to return all of them, since -- they may include e.g. "\\\\foo\\bar" on Win32. getRoots :: IO [FilePath] Omitted: * functions which operate on file names rather than paths * functions which can be implemented portably in terms of the above primitives * isRootedPath -- I can't see a use for this * various other stuff -- Ben