diff --git a/Filesystem/CanonicalPath.hs b/Filesystem/CanonicalPath.hs
--- a/Filesystem/CanonicalPath.hs
+++ b/Filesystem/CanonicalPath.hs
@@ -3,23 +3,33 @@
 Copyright   : (c) Boris Buliga, 2014
 License     : MIT
 Maintainer  : d12frosted@icloud.com
-Stability   : stable
-Portability : portable
+Stability   : experimental
+Portability : POSIX
 
-@Prelude.FilePath@ is just synonym for @String@, so actually it can be anything - your mothers name or path to file you want to edit. Just look at type signature of function @readFile :: FilePath -> IO String@ which can be converted to @readFile :: String -> IO String@. You can't be sure that it wont break your program in runtime.
+'Prelude.FilePath' is very deceptive, because it's just a synonym for 'Prelude.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 'Prelude.readFile':
 
-OK, you can use @FilePath@ data type from @Filesystem.Path@ module instead of @Prelude.FilePath@. I really like this data type but it has the same problem as @Prelude.FilePath@ - you can't be sure, that it holds actual path to some file or directory on your system.
+@readFile :: FilePath -> IO String@
 
-So here comes abstract type @CanonicalPath@. It solves main problem - @CanonicalPath@ points to existing file or directory. Well, to be honest, 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) - hopefully you can always check if @CanonicalPath@ is not /broken/ (using composition of any constructor and 'unsafePath'). But in most cases @CanonicalPath@ will help you avoid many problems and will make your routine less routine.
+You can translate it as follows:
 
-Oh, and last but not least, @CanonicalPath@ constructor extracts all environment variables. Also it treats @~@ as home directory.
+@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 `Filesystem.Path.FilePath` data type. It solves a lot of problems and comes beefed with multiple cool utilities. And also it is build around 'Data.Text' instead of 'Prelude.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 ('Filesystem.Path.FilePath' or 'Data.Text') using 'System.Directory.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 @'Prelude.Maybe' 'CanonicalPath'@ or @'Prelude.Either' 'Data.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 'System.Directory.canonicalizePath' function. So here are two warnings. Firstly, it behaves differently on different platforms. Sometime too damn differently. So you better watch your steps. Secodly, 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!
 -}
+
+{-# LANGUAGE NoImplicitPrelude #-}
+
 module Filesystem.CanonicalPath
   (-- * Abstract Type
    CanonicalPath
-  ,UnsafePath
 
   -- * Constructors
   ,canonicalPath
@@ -37,9 +47,10 @@
   ,appendFile
 
   -- * Conversion functions
-  ,pathToText
-  ,textToPath
-  ,cpathToText) where
+  ,fromText
+  ,toText
+  ,toText'
+  ,fromPrelude
+  ,toPrelude) where
 
 import Filesystem.CanonicalPath.Internal
-import Prelude ()
diff --git a/Filesystem/CanonicalPath/Directory.hs b/Filesystem/CanonicalPath/Directory.hs
--- a/Filesystem/CanonicalPath/Directory.hs
+++ b/Filesystem/CanonicalPath/Directory.hs
@@ -6,13 +6,14 @@
 Stability   : experimental
 Portability : portable
 
-Redefinition of some functions from @System.Directory@ module. Some of them have different signature, because they need to work with @'CanonicalPath'@. For example, we can't create functions @createDirectory :: 'CanonicalPath' -> IO ()@, because it has no sense. How can we create directory that already exists? Instead we have function @createDirectory :: 'CanonicalPath' -> 'UnsafePath' -> IO 'CanonicalPath'@, that creates new directory in base existing directory with provided name. And also it returns @'CanonicalPath'@ of newly created directory. Isn't it nice?
+Redefinition of some functions from @System.Directory@ module. Some of them have different signature, because they need to work with @'CanonicalPath'@. For example, we can't create functions @createDirectory :: 'CanonicalPath' -> IO ()@, because it has no sense. How can we create directory that already exists? Instead we have function @createDirectory :: 'CanonicalPath' -> 'FilePath' -> IO 'CanonicalPath'@, that creates new directory in base existing directory with provided name. And also it returns @'CanonicalPath'@ of newly created directory. Isn't it nice?
 
 A lot of functions come in two variants: one that returns resulting @'CanonicalPath' and second that ignores result (they end with '_' symbol).
 
 Happy Haskell Hacking!
 -}
 {-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE NoImplicitPrelude #-}
 
 module Filesystem.CanonicalPath.Directory where
 
@@ -21,7 +22,6 @@
 import           Filesystem.CanonicalPath
 import           Filesystem.CanonicalPath.Internal
 import           Filesystem.Path
-import           Prelude ()
 import qualified System.Directory as Directory
 
 {-|
@@ -33,7 +33,7 @@
 -}
 createDirectory :: MonadIO m
                 => CanonicalPath -- ^ base directory
-                -> UnsafePath -- ^ name of new directory
+                -> FilePath -- ^ name of new directory
                 -> m CanonicalPath -- ^ @'CanonicalPath'@ of created directory
 createDirectory cp dir =
   do liftIO . Directory.createDirectory $ toPrelude path
@@ -45,7 +45,7 @@
 
 /Since 0.2.2.0/
 -}
-createDirectory_ :: MonadIO m => CanonicalPath -> UnsafePath -> m ()
+createDirectory_ :: MonadIO m => CanonicalPath -> FilePath -> m ()
 createDirectory_ cp dir = voidM $ createDirectory cp dir
 
 {-|
@@ -58,7 +58,7 @@
 createDirectoryIfMissing :: MonadIO m
                          => Bool -- ^ Create its parents too?
                          -> CanonicalPath -- ^ base directory
-                         -> UnsafePath -- ^ name of new directory
+                         -> FilePath -- ^ name of new directory
                          -> m CanonicalPath -- ^ @'CanonicalPath'@ of created directory
 createDirectoryIfMissing flag cp dir =
   do liftIO . Directory.createDirectoryIfMissing flag $ toPrelude path
@@ -70,7 +70,7 @@
 
 /Since 0.2.2.0/
 -}
-createDirectoryIfMissing_ :: MonadIO m => Bool -> CanonicalPath -> UnsafePath -> m ()
+createDirectoryIfMissing_ :: MonadIO m => Bool -> CanonicalPath -> FilePath -> m ()
 createDirectoryIfMissing_ flag cp dir = voidM $ createDirectoryIfMissing flag cp dir
 
 {-|
@@ -102,19 +102,19 @@
 -}
 renameDirectory :: MonadIO m
                 => CanonicalPath -- ^ old directory
-                -> UnsafePath -- ^ new directory (should be just name of directory)
+                -> FilePath -- ^ new directory (should be just name of directory)
                 -> m CanonicalPath -- ^ @'CanonicalPath'@ of new directory
 renameDirectory cp p =
   do newPath <- canonicalPath $ parent p
      liftIO $ Directory.renameDirectory (toPrelude . unsafePath $ cp) (toPrelude p)
-     return . CanonicalPath $ unsafePath newPath </> dirname (addSlash p)
+     return . CanonicalPath $ unsafePath newPath </> dirname (p </> "")
 
 {-|
 Variant of @'renameDirectory' that ignores resulting @'CanonicalPath'.
 
 /Since 0.2.2.0/
 -}
-renameDirectory_ :: MonadIO m => CanonicalPath -> UnsafePath -> m ()
+renameDirectory_ :: MonadIO m => CanonicalPath -> FilePath -> m ()
 renameDirectory_ cp p = voidM $ renameDirectory cp p
 
 {-|
@@ -124,11 +124,11 @@
 
 /Since 0.1.1.0/
 -}
-getDirectoryContents :: MonadIO m => CanonicalPath -> m [UnsafePath]
+getDirectoryContents :: MonadIO m => CanonicalPath -> m [FilePath]
 getDirectoryContents cp = liftIO . liftM (fromPrelude <$>) $ preludeMap Directory.getDirectoryContents cp
 
 {-|
-The same as @'getDirectoryContents'@, but returns list of @'CanonicalPath'@ instead of @'UnsafePath'@.
+The same as @'getDirectoryContents'@, but returns list of @'CanonicalPath'@ instead of @'FilePath'@.
 
 /Since 0.1.1.0/
 -}
@@ -137,7 +137,7 @@
   where up = unsafePath cp
 
 {-|
-The same as @'getDirectoryContents'@, but returns list of @'Text'@ instead of @'UnsafePath'@.
+The same as @'getDirectoryContents'@, but returns list of @'Text'@ instead of @'FilePath'@.
 
 /Since 0.2.2.0/
 -}
@@ -180,7 +180,7 @@
 
 /Since 0.1.1.0/
 -}
-getAppUserDataDirectory :: MonadIO m => Text -> m UnsafePath
+getAppUserDataDirectory :: MonadIO m => Text -> m FilePath
 getAppUserDataDirectory = liftIO . liftM fromPrelude . Directory.getAppUserDataDirectory . textToString
 
 {-|
@@ -200,7 +200,7 @@
 
 /Since 0.1.1.0/
 -}
-getTemporaryDirectory :: MonadIO m => m UnsafePath
+getTemporaryDirectory :: MonadIO m => m FilePath
 getTemporaryDirectory = liftIO $ liftM fromPrelude Directory.getTemporaryDirectory
 
 {-|
@@ -222,7 +222,7 @@
 -}
 renameFile :: MonadIO m
            => CanonicalPath -- ^ @'CanonicalPath'@ of file you want to rename
-           -> UnsafePath -- ^ new name of file
+           -> FilePath -- ^ new name of file
            -> m CanonicalPath -- ^ @'CanonicalPath'@ of /new/ file
 renameFile cp p =
   do newPath <- canonicalPath $ parent p
@@ -234,7 +234,7 @@
 
 /Since 0.2.2.0/
 -}
-renameFile_ :: MonadIO m => CanonicalPath -> UnsafePath -> m ()
+renameFile_ :: MonadIO m => CanonicalPath -> FilePath -> m ()
 renameFile_ cp p = voidM $ renameFile cp p
 
 {-|
@@ -246,12 +246,12 @@
 -}
 copyFile :: MonadIO m
          => CanonicalPath -- ^ @'CanonicalPath'@ of file you want to copy
-         -> UnsafePath -- ^ name of new file (actually it can be path relative to directory of /old/
+         -> FilePath -- ^ name of new file (actually it can be path relative to directory of /old/
          -> m CanonicalPath -- ^ @'CanonicalPath'@ of /new/ file
 copyFile oldFile newFile =
   do newPath <- canonicalPath $ parent newFile
      liftIO $ Directory.copyFile (toPrelude . unsafePath $ oldFile) (toPrelude newFile)
      return . CanonicalPath $ unsafePath newPath </> filename newFile
 
-copyFile_ :: MonadIO m => CanonicalPath -> UnsafePath -> m ()
+copyFile_ :: MonadIO m => CanonicalPath -> FilePath -> m ()
 copyFile_ oldFile newFile = voidM $ copyFile oldFile newFile
diff --git a/Filesystem/CanonicalPath/Internal.hs b/Filesystem/CanonicalPath/Internal.hs
--- a/Filesystem/CanonicalPath/Internal.hs
+++ b/Filesystem/CanonicalPath/Internal.hs
@@ -8,45 +8,39 @@
                                          ,canonicalPathE
                                          ,canonicalPathE'
                                          ,unsafePath
-                                         ,UnsafePath
-                                         ,SafePath
                                          ,Filesystem.CanonicalPath.Internal.readFile
                                          ,Filesystem.CanonicalPath.Internal.writeFile
                                          ,writeFile'
                                          ,Filesystem.CanonicalPath.Internal.appendFile
                                          ,preludeMap
-                                         ,pathToText
-                                         ,textToPath
-                                         ,cpathToText
+                                         ,fromText
+                                         ,toText
+                                         ,toText'
                                          ,toPrelude
                                          ,fromPrelude
-                                         ,addSlash
                                          ,voidM) where
 
 import           BasicPrelude
 import           Control.Applicative as Applicative
-import           Control.Arrow (left)
+import           Control.Arrow (left, right)
 import           Data.Text ()
 import qualified Data.Text as Text
-import qualified Filesystem.Path.CurrentOS as FilePath
+import           Filesystem.Path.CurrentOS
 import qualified Prelude
 import           System.Directory (getHomeDirectory
-                                  ,canonicalizePath
-                                  ,doesDirectoryExist
-                                  ,doesFileExist)
+                                  ,canonicalizePath)
 import qualified System.Environment as SE (getEnv)
 
-newtype CanonicalPath = CanonicalPath UnsafePath
+newtype CanonicalPath = CanonicalPath FilePath deriving Eq
 
 instance Show CanonicalPath where
   showsPrec d path =
     showParen (d > 15)
               (showString "CanonicalPath " .
-               shows (toText path))
-    where toText (CanonicalPath p) = pathToText p
+               shows (toText' path))
 
 {-|
-Unsafe constructor of @CanonicalPath@. In case of any problem it will @error@.
+Unsafe constructor of 'CanonicalPath'. In case of any problems it will 'error'.
 
 Example:
 
@@ -58,19 +52,19 @@
 
 /Since 0.1.0.0/
 -}
-canonicalPath :: MonadIO m => UnsafePath -> m CanonicalPath
+canonicalPath :: MonadIO m => FilePath -> m CanonicalPath
 canonicalPath path = canonicalize path >>= either (error . textToString) (return . CanonicalPath)
 
 {-|
-Version of @canonicalPath@ that takes @Text@ instead of @UnsafePath@.
+Version of 'canonicalPath' that takes 'Data.Text' instead of 'Filesystem.Path.FilePath'.
 
 /Since 0.2.1.0/
 -}
 canonicalPath' :: MonadIO m => Text -> m CanonicalPath
-canonicalPath' = canonicalPath . textToPath
+canonicalPath' = canonicalPath . fromText
 
 {-|
-Constructs @Maybe CanonicalPath@.
+Constructs @'Maybe' 'CanonicalPath'@.
 
 >>> canonicalPathM "~"
 Just CanonicalPath "Users/your-user-name"
@@ -80,19 +74,19 @@
 
 /Since 0.1.0.0/
 -}
-canonicalPathM :: MonadIO m => UnsafePath -> m (Maybe CanonicalPath)
+canonicalPathM :: MonadIO m => FilePath -> m (Maybe CanonicalPath)
 canonicalPathM path = canonicalize path >>= either (\_ -> return Nothing) (return . Just . CanonicalPath)
 
 {-|
-Version of @canonicalPathM@ that takes @Text@ instead of @UnsafePath@.
+Version of 'canonicalPathM' that takes 'Data.Text' instead of 'Filesystem.Path.FilePath'.
 
 /Since 0.2.1.0/
 -}
 canonicalPathM' :: MonadIO m => Text -> m (Maybe CanonicalPath)
-canonicalPathM' = canonicalPathM . textToPath
+canonicalPathM' = canonicalPathM . fromText
 
 {-|
-Constructs @Either Text CanonicalPath@.
+Constructs 'Prelude.Either' 'Data.Text' 'CanonicalPath'.
 
 >>> canonicalPathE "~/"
 Right CanonicalPath "/Users/your-user-name"
@@ -102,90 +96,84 @@
 
 /Since 0.1.0.0/
 -}
-canonicalPathE :: MonadIO m => UnsafePath -> m (Either Text CanonicalPath)
+canonicalPathE :: MonadIO m => FilePath -> m (Either Text CanonicalPath)
 canonicalPathE path = canonicalize path >>= either (return . Left) (return . Right . CanonicalPath)
 
 {-|
-Version of @canonicalPathE@ that takes @Text@ instead of @UnsafePath@.
+Version of 'canonicalPathE' that takes 'Data.Text' instead of 'Filesystem.Path.FilePath'.
 
 /Since 0.2.1.0/
 -}
 canonicalPathE' :: MonadIO m => Text -> m (Either Text CanonicalPath)
-canonicalPathE' = canonicalPathE . textToPath
+canonicalPathE' = canonicalPathE . fromText
 
--- | Convert @CanonicalPath@ to @Filesystem.FilePath@.
+-- | Convert 'CanonicalPath' to @Filesystem.FilePath@.
 --
 -- /Since 0.1.0.0/
-unsafePath :: CanonicalPath -> UnsafePath
+unsafePath :: CanonicalPath -> FilePath
 unsafePath (CanonicalPath up) = up
 
--- | Synonym of @FilePath@ from @Filesystem.Path@ module.
---
--- /Since 0.1.0.0/
-type UnsafePath = FilePath.FilePath
-type SafePath = Either Text UnsafePath
-
 -- * Functions used for canonicalization
 
-canonicalize :: MonadIO m => UnsafePath -> m SafePath
+canonicalize :: MonadIO m => FilePath -> m (Either Text FilePath)
 canonicalize fp = extractPath fp >>= either (return . Left) canonicalize'
 
-canonicalize' :: MonadIO m => UnsafePath -> m SafePath
-canonicalize' fp =
-  do exists <- liftIO $ liftM2 (||) (doesFileExist . toPrelude $ fp) (doesDirectoryExist . toPrelude $ fp)
-     if exists
-        then liftIO $ liftM Right (pathMap canonicalizePath fp)
-        else return . Left $ "Path does not exist (no such file or directory): " ++ pathToText fp
+-- we do want exceptions from canonicalizePath
+-- also canonicalizePath will throw exception
+-- when @path does not exist
+canonicalize' :: MonadIO m => Text -> m (Either Text FilePath)
+canonicalize' path =
+  liftIO $ liftM (right fromPrelude) (tryIO . canonicalizePath . textToString $ path)
 
-extractPath :: MonadIO m => UnsafePath -> m SafePath
-extractPath = liftM concatPath . mapM extractAtom . FilePath.splitDirectories
+extractPath :: MonadIO m => FilePath -> m (Either Text Text)
+extractPath = liftM (right concatPath . sequence) . mapM extractAtom . splitPath . toTextUnsafe . collapse
 
-extractAtom :: MonadIO m => UnsafePath -> m SafePath
-extractAtom atom = tryEnvPosix <||> tryEnvWindows <||> tryHome <%> atom
+extractAtom :: MonadIO m => Text -> m (Either Text Text)
+extractAtom atom = tryEnvPosix <||> tryHome <||> tryEnvWindows <%> atom
 
 -- * Parsers and parser combinators
 
-type Parser m = UnsafePath -> Maybe (m SafePath)
+type Parser m = Text -> Maybe (m (Either Text Text))
 
 tryEnvPosix :: MonadIO m => Parser m
-tryEnvPosix x = when' (hasPrefix "$" x) (Just . getEnv . pathTail $ x)
+tryEnvPosix x = when' (Text.isPrefixOf "$" x) (Just . getEnv . Text.tail $ x)
 
 tryEnvWindows :: MonadIO m => Parser m
 tryEnvWindows x =
-  when' (hasPrefix "%" x &&
-         hasSuffix "%" x)
-        (Just . getEnv . pathTail . pathInit $ x)
+  when' (Text.isPrefixOf "%" x &&
+         Text.isSuffixOf "%" x)
+        (Just . getEnv . Text.tail . Text.init $ x)
 
 tryHome :: MonadIO m => Parser m
-tryHome x = when' (textToPath "~" == x) (Just $ liftM Right homeDirectory)
+tryHome x = when' ("~" == x) (Just $ liftM Right homeDirectory)
 
 (<||>) :: MonadIO m => Parser m -> Parser m -> Parser m
 p1 <||> p2 = \v -> p1 v <|> p2 v
 
-(<%>) :: MonadIO m => Parser m -> UnsafePath -> m SafePath
+(<%>) :: MonadIO m => Parser m -> Text -> m (Either Text Text)
 p <%> v = fromMaybe (return . Right $ v) (p v)
 
 -- * File operations
 
--- | @'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.
+-- | @readFile file@ function reads a /file/ and returns the contents of the /file/ as a 'Data.Text'. The /file/ is read lazily, on demand, as with getContents.
 --
 -- /Since 0.1.1.0/
 readFile :: MonadIO m => CanonicalPath -> m Text
 readFile = liftIO . BasicPrelude.readFile . unsafePath
 
--- | @'writeFile' file txt@ writes /txt/ to the /file/.
+-- | @writeFile file txt@ writes /txt/ to the /file/.
 --
 -- /Since 0.1.1.0/
 writeFile :: MonadIO m => CanonicalPath -> Text -> m ()
 writeFile p = liftIO . BasicPrelude.writeFile (unsafePath p)
 
--- | @'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.
+-- | @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.
 --
 -- /Since 0.1.2.0/
-writeFile' :: MonadIO m => CanonicalPath -> UnsafePath -> Text -> m ()
+writeFile' :: MonadIO m => CanonicalPath -> FilePath -> Text -> m ()
 writeFile' cp file = liftIO . BasicPrelude.writeFile (unsafePath cp </> file)
 
--- | @'appendFile' file txt@ appends /txt/ to the /file/.
+-- | @appendFile file txt@ appends /txt/ to the /file/.
 --
 -- /Since 0.1.1.0/
 appendFile :: MonadIO m => CanonicalPath -> Text -> m ()
@@ -193,80 +181,58 @@
 
 -- * Utilities
 
-getEnv :: MonadIO m => UnsafePath -> m SafePath
-getEnv var = liftM (left show) tryEnv
-  where env = pathMap SE.getEnv
-        tryEnv :: MonadIO m => m (Either IOException UnsafePath)
-        tryEnv = liftIO . try . env $ var
+tryIO :: MonadIO m => IO a -> m (Either Text a)
+tryIO a = liftM (left show) (try' a)
+  where try' :: MonadIO m => IO a -> m (Either IOException a)
+        try' = liftIO . try
 
-homeDirectory :: MonadIO m => m UnsafePath
-homeDirectory = liftIO $ fromPrelude <$> getHomeDirectory
+getEnv :: MonadIO m => Text -> m (Either Text Text)
+getEnv = liftM (right fromString) . tryIO . SE.getEnv . textToString
 
+homeDirectory :: MonadIO m => m Text
+homeDirectory = liftIO $ fromString <$> getHomeDirectory
+
 when' :: Alternative f => Bool -> f a -> f a
 when' b v = if b then v else Applicative.empty
 
-pathMap :: MonadIO m => (Prelude.FilePath -> m Prelude.FilePath) -> UnsafePath -> m UnsafePath
-pathMap f p = liftM fromPrelude (f . toPrelude $ p)
-
-hasPrefix :: Text -> UnsafePath -> Bool
-hasPrefix prefix path = prefix `Text.isPrefixOf` pathToText path
-
-hasSuffix :: Text -> UnsafePath -> Bool
-hasSuffix suffix path = suffix `Text.isSuffixOf` pathToText path
-
-pathTail :: UnsafePath -> UnsafePath
-pathTail = textToPath . Text.tail . pathToText
-
-pathInit :: UnsafePath -> UnsafePath
-pathInit = textToPath . Text.init . pathToText
-
-addSlash :: UnsafePath -> UnsafePath
-addSlash = textToPath . (++ "/") . pathToText
+splitPath :: Text -> [Text]
+splitPath = Text.splitOn "/"
 
-concatPath :: [SafePath] -> SafePath
-concatPath = foldl' (<//>) (Right "")
+concatPath :: [Text] -> Text
+concatPath = Text.intercalate "/"
 
-(<//>) :: SafePath -> SafePath -> SafePath
-(<//>) l@(Left _) _ = l
-(<//>) _ l@(Left _) = l
-(<//>) (Right a) (Right b) = Right $ a </> b
+-- concatPath :: [Either Text Text] -> Either Text Text
+-- concatPath = right BasicPrelude.concat . sequence
+-- concatPath = right (Text.intercalate "/") . sequence
 
 preludeMap :: (Prelude.FilePath -> a) -> CanonicalPath -> a
 preludeMap f = f . toPrelude . unsafePath
 
--- * Type conversions
-
--- | @'pathToText' path@ converts 'UnsafePath' /path/ to 'Text'. In case of eny problems it will throw error.
+-- | @toText path@ converts 'Filesystem.FilePath' /path/ to 'Data.Text'. In case of any problems it will throw error.
 --
 -- See 'Filesystem.Path.CurrentOS.toText' function for details.
 --
--- /Since 0.1.2.0/
-pathToText :: UnsafePath -> Text
-pathToText = either (error . textToString) id . FilePath.toText
-
--- | @'textToPath' txt@ converts 'Text' to 'UnsafePath'.
---
--- /Since 0.1.2.0/
-textToPath :: Text -> UnsafePath
-textToPath = FilePath.fromText
+-- /Since 0.3.0.0/
+toTextUnsafe :: FilePath -> Text
+toTextUnsafe = either (error . textToString) id . toText
 
--- | @'cpathToText' path@ converts 'CanonicalPath' to 'Text'.
+-- | @toText' path@ converts 'CanonicalPath' to 'Data.Text'.
 --
--- /Since 0.2.3.0/
-cpathToText :: CanonicalPath -> Text
-cpathToText = pathToText . unsafePath
+-- /Since 0.3.0.0/
+toText' :: CanonicalPath -> Text
+toText' = toTextUnsafe . unsafePath
 
--- | @'fromPrelude' fp'@ converts 'Prelude.FilePath' to 'UnsafePath'.
+-- | @fromPrelude fp@ converts 'Prelude.FilePath' to 'Filesystem.Path.CurrentOS.toText'.
 --
 -- /Since 0.1.0.0/
-fromPrelude :: Prelude.FilePath -> UnsafePath
-fromPrelude = textToPath . Text.pack
+fromPrelude :: Prelude.FilePath -> FilePath
+fromPrelude = fromText . Text.pack
 
--- | @'toPrelude' up'@ converts 'UnsafePath' to 'Prelude.FilePath'.
+-- | @toPrelude up@ converts 'Filesystem.Path.FilePath' to 'Prelude.FilePath'.
 --
 -- /Since 0.1.0.0/
-toPrelude :: UnsafePath -> Prelude.FilePath
-toPrelude = Text.unpack . pathToText
+toPrelude :: FilePath -> Prelude.FilePath
+toPrelude = Text.unpack . toTextUnsafe
 
 voidM :: Monad m => m a -> m ()
 voidM a = a >> return ()
diff --git a/changelog.md b/changelog.md
--- a/changelog.md
+++ b/changelog.md
@@ -1,3 +1,15 @@
+0.3.0.0:
+* remove `UnsafePath` data type. It was synonym to `FilePath` and was confusing me sometimes, so I decided to cut it off
+* rename `cpathToText` to `toText'`
+* remove `pathToText` and `textToPath`
+* export `toText` and `fromText` from `Filesystem.Path.CurrentOS`
+* export `fromPrelude` and `toPrelude` functions
+* improve performance (path canonicalization now is performed 1.6x faster than before)
+* improve `canonicalPath` error messages. Most important - now it respects errors from `System.Directory.canonicalizePath`
+* add tests
+* add travis support
+* update documentation
+
 0.2.3.0:
 * add `cpathToText` that converts `CanonicalPath` to `Text`
 * update base version constraints
diff --git a/system-canonicalpath.cabal b/system-canonicalpath.cabal
--- a/system-canonicalpath.cabal
+++ b/system-canonicalpath.cabal
@@ -1,7 +1,7 @@
 name:                system-canonicalpath
-version:             0.2.3.0
-synopsis:            Abstract data type for canonical paths with pretty operations
-description:         This library provides abstract data type named 'CanonicalPath' and some useful functions for working with it. See every module's description to find out more.
+version:             0.3.0.0
+synopsis:            Abstract data type for canonical paths with some utilities
+description:         This library provides abstract data type named 'Filesystem.CanonicalPath.CanonicalPath' and some useful functions for working with it.
 homepage:            https://github.com/d12frosted/CanonicalPath
 license:             MIT
 license-file:        LICENSE
@@ -21,10 +21,27 @@
 
   other-extensions:  OverloadedStrings
 
-  build-depends:     base >=4.7 && < 4.9
+  build-depends:     base >= 4.7 && < 4.9
                    , basic-prelude
-                   , directory
-                   , system-filepath
+                   , directory >= 1.2
+                   , system-filepath >= 0.4
                    , text
 
+  ghc-options:       -Wall -O2
+  default-language:  Haskell2010
+
+test-suite canonicalpath_tests
+  type:              exitcode-stdio-1.0
+  main-is:           CanonicalPathTests.hs
+
+  hs-source-dirs:    tests
+
+  build-depends:     base >= 4.7 && < 4.9
+                   , basic-prelude
+                   , chell >= 0.4 && < 0.5
+                   , system-canonicalpath
+                   , system-filepath
+
+  ghc-options:       -Wall -O2
+  cc-options:        -Wall
   default-language:  Haskell2010
diff --git a/tests/CanonicalPathTests.hs b/tests/CanonicalPathTests.hs
new file mode 100644
--- /dev/null
+++ b/tests/CanonicalPathTests.hs
@@ -0,0 +1,70 @@
+{-# LANGUAGE TemplateHaskell #-}
+{-# LANGUAGE NoImplicitPrelude #-}
+{-# LANGUAGE OverloadedStrings #-}
+
+module Main (main) where
+
+import           BasicPrelude
+import qualified Control.Arrow as Arrow(right)
+import           Filesystem.CanonicalPath
+import           Filesystem.CanonicalPath.Directory
+import           Test.Chell
+
+main :: IO ()
+main = defaultMain [testCanonicalPath]
+
+testCanonicalPath :: Suite
+testCanonicalPath =
+  suite "canonical path constructors" $
+  [testConstructor]
+
+testConstructor :: Test
+testConstructor =
+  assertions "canonicalPathE tests" $
+  do let left' v = v >>= \v' -> return $ left v'
+         equal' v w = do
+           v' <- v
+           w' <- w
+           return $ equal v' w'
+         check p1 p2 = equal' (canonicalize' p1) (liftM Right $ inCurrentDir p2)
+
+     $expect $ left' (canonicalize' "diro/")
+     $expect $ left' (canonicalize' "dir.txt")
+     $expect $ left' (canonicalize' "dir/ab/../../dir/ab/./../file1.tx")
+     $expect $ left' (canonicalize' deepFile)
+
+     $expect $ check "dir/" "dir"
+     $expect $ check "dir" "dir"
+     $expect $ check "file" "file"
+     -- check "file/" "file" will fail on linux
+     -- but will not fail on os x
+     $expect $ check "dir/file1.txt" "dir/file1.txt"
+     $expect $ check "dir/ab/" "dir/ab"
+     $expect $ check "dir/file" "dir/file"
+     $expect $ check "dir/ab/../file" "dir/file"
+     $expect $ check "dir/ab/../ab/" "dir/ab"
+     $expect $ check "dir/ab/./file2.txt" "dir/ab/file2.txt"
+     $expect $ check "dir/ab/../../dir/ab/./../file1.txt" "dir/file1.txt"
+
+     $expect $ equal' (canonicalize "$HOME") (liftM Right getHomeDirectory)
+     $expect $ equal' (canonicalize "$HOME/") (liftM Right getHomeDirectory)
+     $expect $ equal' (canonicalize "~/") (liftM Right getHomeDirectory)
+
+-- Helper functions
+
+canonicalize :: FilePath -> IO (Either Text CanonicalPath)
+canonicalize = canonicalPathE
+
+canonicalize' :: FilePath -> IO (Either Text FilePath)
+canonicalize' p = inCurrentDir p >>= liftM (Arrow.right unsafePath) . canonicalize
+
+currentDir :: IO FilePath
+currentDir = liftM ((</> "tests") . unsafePath) getCurrentDirectory
+
+inCurrentDir :: FilePath -> IO FilePath
+inCurrentDir p =
+  do h <- currentDir
+     return $ h </> p
+
+deepFile :: FilePath
+deepFile = concat $ replicate 500 "deep-hell"
