system-canonicalpath- Abstract data type for canonical paths with some utilities

Copyright(c) Boris Buliga, 2014
Safe HaskellNone




FilePath is very deceptive, because it's just a synonym for String, so actually it can be anything - your mothers name or path to file you want to edit. Just look at the type signature of function readFile:

readFile :: FilePath -> IO String

You can translate it as follows:

readFile :: String -> IO String

Well, it is known that IO actions are dangerous by themselves. And here comes another problem - you need to be sure that the path you pass to function is at least well constructed. For this purpose you can use well known FilePath data type. It solves a lot of problems and comes beefed with multiple cool utilities. And also it is build around Text instead of String. Awesome!

So why do we need yet another path library? The answer is simple - we want to use paths like $HOME/.app.cfg, ~/.zshrc or /full/path/to/existing/file/or/dir in our code without any additional overhead. CanonicalPath is named so because it tries to canonicalize given path (FilePath or Text) using canonicalizePath function. It also will extract any variables it finds in path (like $VARNAME, %VARNAME% and special ~/). But these steps both may fail. Thats why this library provides functions that return Maybe CanonicalPath or Either Text CanonicalPath.

CanonicalPath also comes with additional useful property. When it is created, it points to real file or directory. Honestly, it can't guarantee that this path will refer to existing file or directory always (someone can remove or move it to another path - and it's almost impossible to be aware of such cruelty), but you can always reconstruct CanonicalPath.

One more thing about path canonicalization. As I mentioned before, under the hood it uses canonicalizePath function. So here are two warnings. Firstly, it behaves differently on different platforms. Sometime too damn differently. So you better watch your steps. Secondly, it's impossible to guarantee that the implication same file/dir <=> same canonicalizedPath holds in either direction: this function can make only a best-effort attempt.

Happy Haskell Hacking!


Abstract Type


canonicalPath :: MonadIO m => FilePath -> m CanonicalPath Source

Unsafe constructor of CanonicalPath. In case of any problems it will error.


>>> canonicalPath "$HOME"
CanonicalPath "/Users/your-user-name"
>>> canonicalPath "unknown"
*** Exception: Path does not exist (no such file or directory): unknown


canonicalPath' :: MonadIO m => Text -> m CanonicalPath Source

Version of canonicalPath that takes Text instead of FilePath.


canonicalPathM :: MonadIO m => FilePath -> m (Maybe CanonicalPath) Source

Constructs Maybe CanonicalPath.

>>> canonicalPathM "~"
Just CanonicalPath "Users/your-user-name"
>>> canonicalPathM "unknown"


canonicalPathM' :: MonadIO m => Text -> m (Maybe CanonicalPath) Source

Version of canonicalPathM that takes Text instead of FilePath.


canonicalPathE :: MonadIO m => FilePath -> m (Either Text CanonicalPath) Source

Constructs Either Text CanonicalPath.

>>> canonicalPathE "~/"
Right CanonicalPath "/Users/your-user-name"
>>> canonicalPathE "$HOME/this-folder-does-not-exist"
Left "Path does not exist (no such file or directory): /Users/your-user-name/this-folder-does-not-exist"


canonicalPathE' :: MonadIO m => Text -> m (Either Text CanonicalPath) Source

Version of canonicalPathE that takes Text instead of FilePath.


unsafePath :: CanonicalPath -> FilePath Source

Convert CanonicalPath to Filesystem.FilePath.


Some IO functions

readFile :: MonadIO m => CanonicalPath -> m Text Source

readFile file function reads a file and returns the contents of the file as a Text. The file is read lazily, on demand, as with getContents.


writeFile :: MonadIO m => CanonicalPath -> Text -> m () Source

writeFile file txt writes txt to the file.


writeFile' :: MonadIO m => CanonicalPath -> FilePath -> Text -> m () Source

writeFile' dir file txt writes txt to the dir/file. Useful, when the file isn't created yet or you don't sure if it exists.


appendFile :: MonadIO m => CanonicalPath -> Text -> m () Source

appendFile file txt appends txt to the file.


Conversion functions

fromText :: Text -> FilePath

Convert human‐readable text into a FilePath.

This function ignores the user’s locale, and assumes all file paths are encoded in UTF8. If you need to create file paths with an unusual or obscure encoding, encode them manually and then use decode.

Since: 0.2

toText :: FilePath -> Either Text Text

Attempt to convert a FilePath to human‐readable text.

If the path is decoded successfully, the result is a Right containing the decoded text. Successfully decoded text can be converted back to the original path using fromText.

If the path cannot be decoded, the result is a Left containing an approximation of the original path. If displayed to the user, this value should be accompanied by some warning that the path has an invalid encoding. Approximated text cannot be converted back to the original path.

This function ignores the user’s locale, and assumes all file paths are encoded in UTF8. If you need to display file paths with an unusual or obscure encoding, use encode and then decode them manually.

Since: 0.2

toText' :: CanonicalPath -> Text Source

toText' path converts CanonicalPath to Text.


fromPrelude :: FilePath -> FilePath Source

fromPrelude fp converts FilePath to toText.


toPrelude :: FilePath -> FilePath Source

toPrelude up converts FilePath to FilePath.