diff --git a/shikensu.cabal b/shikensu.cabal
--- a/shikensu.cabal
+++ b/shikensu.cabal
@@ -1,98 +1,55 @@
-name: shikensu
-version: 0.3.3
-category: File IO
-
-synopsis:
-  A small toolset for building static websites
-
-description:
-  Please see README.md
-
-homepage:
-  https://github.com/icidasset/shikensu#README
-
-license: MIT
-license-file: LICENSE
-
-author: Steven Vandevelde
-maintainer: icid.asset@gmail.com
-
-build-type: Simple
-cabal-version: >= 1.10
+-- This file has been generated from package.yaml by hpack version 0.17.0.
+--
+-- see: https://github.com/sol/hpack
 
+name:           shikensu
+version:        0.3.4
+synopsis:       Run a sequence of functions on in-memory representations of files
+description:    See README at <https://github.com/icidasset/shikensu#readme>
+category:       Filesystem
+homepage:       https://github.com/icidasset/shikensu#readme
+bug-reports:    https://github.com/icidasset/shikensu/issues
+maintainer:     Steven Vandevelde <icid.asset@gmail.com>
+license:        MIT
+license-file:   LICENSE
+build-type:     Simple
+cabal-version:  >= 1.10
 
 source-repository head
   type: git
   location: https://github.com/icidasset/shikensu
 
-
-library
-  default-language:
-    Haskell2010
-
-  default-extensions:
-    OverloadedStrings
-
+test-suite spec
+  type: exitcode-stdio-1.0
+  main-is: Test.hs
   hs-source-dirs:
-    src
-
-  exposed-modules:
-    Shikensu,
-    Shikensu.Contrib,
-    Shikensu.Contrib.IO,
-    Shikensu.Internal.Utilities,
-    Shikensu.Metadata,
-    Shikensu.Sorting,
-    Shikensu.Types,
-    Shikensu.Utilities
-
+      tests
+      src
+  default-extensions: DisambiguateRecordFields DuplicateRecordFields OverloadedStrings
   build-depends:
-    aeson == 1.0.*,
-    base >= 4.8 && < 5,
-    bytestring == 0.10.*,
-    directory == 1.3.*,
-    filepath == 1.4.*,
-    flow == 1.0.*,
-    Glob == 0.7.*,
-    unordered-containers == 0.2.*,
-    text >= 1.2 && < 2
-
-
-test-suite tests
-  default-language:
-    Haskell2010
-
-  default-extensions:
-    OverloadedStrings
-
-  ghc-options:
-    -Wall -threaded -rtsopts -with-rtsopts=-N
-
-  type:
-    exitcode-stdio-1.0
-
-  hs-source-dirs:
-    tests
-
-  main-is:
-    Test.hs
-
+      aeson == 1.0.*
+    , base >= 4.9 && < 5
+    , bytestring == 0.10.*
+    , directory == 1.3.*
+    , filepath == 1.4.*
+    , flow == 1.0.*
+    , Glob == 0.7.*
+    , unordered-containers == 0.2.*
+    , text == 1.2.*
+    , tasty == 0.11.*
+    , tasty-hunit == 0.9.*
   other-modules:
-    Test.Contrib,
-    Test.Example,
-    Test.Helpers,
-    Test.Shikensu,
-    Test.Utilities
-
-  build-depends:
-    aeson,
-    base,
-    bytestring,
-    directory,
-    filepath,
-    flow,
-    shikensu,
-    tasty == 0.11.*,
-    tasty-hunit == 0.9.*,
-    text,
-    unordered-containers
+      Test.Contrib
+      Test.Example
+      Test.Helpers
+      Test.Shikensu
+      Test.Utilities
+      Shikensu
+      Shikensu.Contrib
+      Shikensu.Contrib.IO
+      Shikensu.Internal.Types
+      Shikensu.Internal.Utilities
+      Shikensu.Metadata
+      Shikensu.Sorting
+      Shikensu.Utilities
+  default-language: Haskell2010
diff --git a/src/Shikensu.hs b/src/Shikensu.hs
--- a/src/Shikensu.hs
+++ b/src/Shikensu.hs
@@ -4,25 +4,26 @@
 
 -}
 module Shikensu
-    ( list
+    ( forkDefinition
+    , list
     , listF
     , listRelative
     , listRelativeF
-
-    , forkDefinition
     , makeDefinition
     , makeDictionary
+    , module Shikensu.Internal.Types
     ) where
 
+import Data.Monoid ((<>))
 import Flow
+import Shikensu.Internal.Types
 import Shikensu.Internal.Utilities
-import Shikensu.Types
 import System.FilePath
 
 import qualified Data.HashMap.Strict as HashMap (empty)
-import qualified Data.List as List (concat, map, zip)
+import qualified Data.List as List (concatMap, map, zip)
 import qualified System.Directory as Dir (canonicalizePath)
-
+import qualified System.FilePath.Glob as Glob (compile, globDir1)
 
 
 -- IO
@@ -37,40 +38,42 @@
 4. We make a Dictionary out of each tuple (this also needs the path).
 5. Merge the dictionaries into one dictionary.
 
+> list ["*.md"] "/root/articles"
+
 -}
-list :: [Pattern] -> FilePath -> IO Dictionary
+list :: [String] -> FilePath -> IO Dictionary
 list patterns rootDir =
     patterns
-        |> compilePatterns
-        |> globDir rootDir
+        |> List.map (Glob.compile)
+        |> List.map (flip Glob.globDir1 $ rootDir)
+        |> sequence
         |> fmap (List.zip patterns)
-        |> fmap (List.map (uncurry . makeDictionary $ rootDir))
-        |> fmap (List.concat)
+        |> fmap (List.concatMap $ makeDictionary rootDir)
 
 
 {-| Flipped version of `list`.
 -}
-listF :: FilePath -> [Pattern] -> IO Dictionary
+listF :: FilePath -> [String] -> IO Dictionary
 listF = flip list
 
 
 {-| Same as `list`, but given a relative directory.
 
 > listRelative ["*.md"] "./articles"
+
 -}
-listRelative :: [Pattern] -> FilePath -> IO Dictionary
+listRelative :: [String] -> FilePath -> IO Dictionary
 listRelative patterns relativePath =
     Dir.canonicalizePath relativePath >>= list patterns
 
 
 {-| Flipped version `listRelative`.
 -}
-listRelativeF :: FilePath -> [Pattern] -> IO Dictionary
+listRelativeF :: FilePath -> [String] -> IO Dictionary
 listRelativeF = flip listRelative
 
 
 
-
 -- PURE
 
 
@@ -101,7 +104,7 @@
 
 - the root path `/Users/icidasset/Projects/shikensu`
 - the pattern `example/**/*.md`
-- the workspace path `example/test/hello.md`
+- the absolute path `/Users/icidasset/Projects/shikensu/example/test/hello.md`
 
 > Definition
 >     { basename = "hello"
@@ -118,31 +121,34 @@
 >     }
 
 -}
-makeDefinition :: FilePath -> Pattern -> FilePath -> Definition
-makeDefinition _rootDirname _pattern _workspacePath =
+makeDefinition :: FilePath -> String -> FilePath -> Definition
+makeDefinition rootDirname pattern absolutePath =
     let
-        workingDir  = cleanPath . (commonDirectory) $ _pattern
-        localPath   = cleanPath . (stripPrefix workingDir) . cleanPath $ _workspacePath
+        workingDirname      = commonDirectory pattern
+        rootWorkingDirname  = combine rootDirname workingDirname
+
+        theAbsolutePath     = normalise absolutePath
+        theLocalPath        = dropDrive (stripPrefix rootWorkingDirname theAbsolutePath)
     in
         Definition
-            { basename        = takeBaseName localPath
-            , dirname         = takeDirName localPath
-            , extname         = takeExtension localPath
-            , pattern         = _pattern
-            , rootDirname     = dropTrailingPathSeparator _rootDirname
-            , workingDirname  = workingDir
+            { basename        = takeBaseName theLocalPath
+            , dirname         = takeDirName theLocalPath
+            , extname         = takeExtension theLocalPath
+            , pattern         = pattern
+            , rootDirname     = rootDirname
+            , workingDirname  = workingDirname
 
             -- Additional properties
             , content         = Nothing
             , metadata        = HashMap.empty
-            , parentPath      = compileParentPath $ takeDirName localPath
-            , pathToRoot      = compilePathToRoot $ takeDirName localPath
+            , parentPath      = compileParentPath $ takeDirName theLocalPath
+            , pathToRoot      = compilePathToRoot $ takeDirName theLocalPath
             }
 
 
 
 {-| Make a Dictionary.
 -}
-makeDictionary :: FilePath -> Pattern -> [FilePath] -> Dictionary
-makeDictionary _rootDirname _pattern =
-    List.map (makeDefinition _rootDirname _pattern)
+makeDictionary :: FilePath -> (String, [FilePath]) -> Dictionary
+makeDictionary rootDirname (pattern, filepaths) =
+    List.map (makeDefinition rootDirname pattern) filepaths
diff --git a/src/Shikensu/Contrib.hs b/src/Shikensu/Contrib.hs
--- a/src/Shikensu/Contrib.hs
+++ b/src/Shikensu/Contrib.hs
@@ -25,11 +25,11 @@
     ) where
 
 import Data.ByteString (ByteString)
-import Flow
+import Data.Monoid ((<>))
 import Shikensu (forkDefinition)
-import Shikensu.Internal.Utilities (cleanPath, compileParentPath, compilePathToRoot)
+import Shikensu.Internal.Types
+import Shikensu.Internal.Utilities (compileParentPath, compilePathToRoot)
 import Shikensu.Metadata (transposeToMetadata)
-import Shikensu.Types
 import System.FilePath (FilePath, combine)
 
 import qualified Data.HashMap.Strict as HashMap (empty, union)
@@ -41,7 +41,7 @@
 -}
 clearMetadata :: Dictionary -> Dictionary
 clearMetadata =
-    fmap (clearMetadataDef)
+    fmap clearMetadataDef
 
 
 clearMetadataDef :: Definition -> Definition
@@ -62,11 +62,11 @@
 clone existingPath newPath dict =
     let
         makeNew = \def acc ->
-            if (localPath def) == existingPath
-                then acc ++ [forkDefinition newPath def]
-                else acc
+            if localPath def == existingPath
+               then acc <> [forkDefinition newPath def]
+               else acc
     in
-        dict ++ (foldr makeNew [] dict)
+        dict <> foldr makeNew [] dict
 
 
 
@@ -77,7 +77,7 @@
 -}
 copyPropsToMetadata :: Dictionary -> Dictionary
 copyPropsToMetadata =
-    fmap (copyPropsToMetadataDef)
+    fmap copyPropsToMetadataDef
 
 
 copyPropsToMetadataDef :: Definition -> Definition
@@ -93,7 +93,7 @@
 -}
 exclude :: FilePath -> Dictionary -> Dictionary
 exclude path =
-    filter (\def -> (localPath def) /= path)
+    filter (\def -> localPath def /= path)
 
 
 
@@ -127,16 +127,16 @@
 
 permalinkDef :: String -> Definition -> Definition
 permalinkDef newBasename def =
-    if (basename def) /= newBasename then
-        let
-            newDirname = cleanPath $ combine (dirname def) (basename def)
-        in
-            def
-                { basename    = newBasename
-                , dirname     = newDirname
-                , parentPath  = compileParentPath $ newDirname
-                , pathToRoot  = compilePathToRoot $ newDirname
-                }
+    if basename def /= newBasename then
+       let
+           newDirname = combine (dirname def) (basename def)
+       in
+           def
+               { basename    = newBasename
+               , dirname     = newDirname
+               , parentPath  = compileParentPath newDirname
+               , pathToRoot  = compilePathToRoot newDirname
+               }
     else
         def
 
@@ -153,12 +153,12 @@
 prefixDirnameDef :: String -> Definition -> Definition
 prefixDirnameDef prefix def =
     let
-        newDirname = prefix ++ (dirname def)
+        newDirname = prefix <> dirname def
     in
         def
             { dirname     = newDirname
-            , parentPath  = compileParentPath $ newDirname
-            , pathToRoot  = compilePathToRoot $ newDirname
+            , parentPath  = compileParentPath newDirname
+            , pathToRoot  = compilePathToRoot newDirname
             }
 
 
@@ -178,9 +178,9 @@
 
 renameDef :: FilePath -> FilePath -> Definition -> Definition
 renameDef oldPath newPath def =
-  if (localPath def) == oldPath
-    then forkDefinition newPath def
-    else def
+  if localPath def == oldPath
+     then forkDefinition newPath def
+     else def
 
 
 
@@ -199,7 +199,7 @@
 
 renameExtDef :: String -> String -> Definition -> Definition
 renameExtDef oldExtname newExtname def =
-    if (extname def) == oldExtname
+    if extname def == oldExtname
         then def { extname = newExtname }
         else def
 
diff --git a/src/Shikensu/Contrib/IO.hs b/src/Shikensu/Contrib/IO.hs
--- a/src/Shikensu/Contrib/IO.hs
+++ b/src/Shikensu/Contrib/IO.hs
@@ -9,8 +9,7 @@
     ) where
 
 import Data.Maybe (fromMaybe)
-import Flow
-import Shikensu.Types
+import Shikensu.Internal.Types
 import System.Directory (createDirectoryIfMissing, doesFileExist)
 import System.FilePath (FilePath, joinPath, takeDirectory)
 
@@ -25,7 +24,7 @@
 -}
 read :: Dictionary -> IO Dictionary
 read =
-    Utilities.mapIO (readDef)
+    Utilities.mapIO readDef
 
 
 readDef :: Definition -> IO Definition
diff --git a/src/Shikensu/Internal/Types.hs b/src/Shikensu/Internal/Types.hs
new file mode 100644
--- /dev/null
+++ b/src/Shikensu/Internal/Types.hs
@@ -0,0 +1,71 @@
+{-| Types and path helpers.
+    This is re-exported in the main `Shikensu` module.
+
+-}
+module Shikensu.Internal.Types where
+
+import Data.Aeson ((.=), toJSON)
+import Data.ByteString (ByteString)
+import Data.Monoid ((<>))
+import System.FilePath (joinPath)
+
+import qualified Data.Aeson as Aeson (Object, ToJSON, object)
+
+
+{-| A file definition, along with some additional properties.
+-}
+data Definition =
+    Definition
+        { basename :: String
+        , dirname :: FilePath
+        , extname :: String
+        , pattern :: String
+        , rootDirname :: FilePath
+        , workingDirname :: FilePath
+
+        -- Additional properties
+        , content :: Maybe ByteString
+        , metadata :: Metadata
+        , parentPath :: Maybe FilePath
+        , pathToRoot :: FilePath
+        } deriving (Eq, Show)
+
+
+instance Aeson.ToJSON Definition where
+    toJSON def =
+        Aeson.object
+            [ "basename"        .= basename def
+            , "dirname"         .= dirname def
+            , "extname"         .= extname def
+            , "pattern"         .= pattern def
+            , "workingDirname"  .= workingDirname def
+            , "parentPath"      .= parentPath def
+            , "pathToRoot"      .= pathToRoot def
+            ]
+
+
+
+-- Type aliases
+
+
+type Dictionary = [Definition]
+type Metadata = Aeson.Object
+
+
+
+-- Path functions
+
+
+absolutePath :: Definition -> String
+absolutePath def =
+    joinPath [rootDirname def, workspacePath def]
+
+
+localPath :: Definition -> String
+localPath def =
+    joinPath [dirname def, basename def <> extname def]
+
+
+workspacePath :: Definition -> String
+workspacePath def =
+    joinPath [workingDirname def, localPath def]
diff --git a/src/Shikensu/Internal/Utilities.hs b/src/Shikensu/Internal/Utilities.hs
--- a/src/Shikensu/Internal/Utilities.hs
+++ b/src/Shikensu/Internal/Utilities.hs
@@ -2,12 +2,9 @@
 
 -}
 module Shikensu.Internal.Utilities
-    ( cleanPath
-    , commonDirectory
-    , compilePatterns
+    ( commonDirectory
     , compileParentPath
     , compilePathToRoot
-    , globDir
     , replaceSingleDot
     , stripPrefix
     , takeDirName
@@ -15,37 +12,23 @@
 
 import Data.Maybe (fromMaybe)
 import Flow
-import Shikensu.Types (Pattern)
 import System.FilePath
 
 import qualified Data.List as List (map, stripPrefix)
+import qualified Data.Tuple as Tuple (fst)
 import qualified System.FilePath.Glob as Glob
 
 
-{-| "Clean up" a path.
-
-> `/directory/./nested/` -> `directory/nested`
-> `./` -> ``
-> `.` -> ``
-
--}
-cleanPath :: FilePath -> FilePath
-cleanPath =
-    normalise .> dropTrailingPathSeparator .> dropDrive .> replaceSingleDot
-
-
-{-| Get the common directory from a Pattern.
--}
-commonDirectory :: Pattern -> FilePath
-commonDirectory pattern =
-    (Glob.compile .> Glob.commonDirectory .> fst) pattern
-
-
-{-| Compile a list of `Pattern`s to `Glob.Pattern`s.
+{-| Get the common directory from a pattern.
 -}
-compilePatterns :: [Pattern] -> [Glob.Pattern]
-compilePatterns =
-    List.map Glob.compile
+commonDirectory :: String -> FilePath
+commonDirectory =
+    Glob.compile
+    .> Glob.commonDirectory
+    .> Tuple.fst
+    .> normalise
+    .> dropTrailingPathSeparator
+    .> replaceSingleDot
 
 
 {-| Path to parent, when there is one.
@@ -76,21 +59,11 @@
     else
         dirname
             |> splitDirectories
-            |> fmap (\_ -> "..")
+            |> fmap (const "..")
             |> joinPath
             |> addTrailingPathSeparator
 
 
-{-| List contents of a directory using a glob pattern.
-Returns a relative path, based on the given rootDirname.
--}
-globDir :: FilePath -> [Glob.Pattern] -> IO [[FilePath]]
-globDir rootDir patterns =
-    Glob.globDir patterns rootDir
-        |> fmap (fst)
-        |> fmap (List.map . List.map . makeRelative $ rootDir)
-
-
 {-| If the path is a single dot, return an empty string.
 Otherwise return the path.
 -}
@@ -110,4 +83,4 @@
 -}
 takeDirName :: FilePath -> FilePath
 takeDirName =
-    replaceSingleDot . takeDirectory
+    takeDirectory .> replaceSingleDot
diff --git a/src/Shikensu/Metadata.hs b/src/Shikensu/Metadata.hs
--- a/src/Shikensu/Metadata.hs
+++ b/src/Shikensu/Metadata.hs
@@ -7,11 +7,10 @@
     ) where
 
 import Flow
-import Shikensu.Types
+import Shikensu.Internal.Types
 
 import qualified Data.Aeson as Aeson (FromJSON, Result(..), ToJSON, fromJSON, toJSON)
 import qualified Data.HashMap.Strict as HashMap (empty)
-
 
 
 {-| Transpose metadata.
diff --git a/src/Shikensu/Sorting.hs b/src/Shikensu/Sorting.hs
--- a/src/Shikensu/Sorting.hs
+++ b/src/Shikensu/Sorting.hs
@@ -5,7 +5,7 @@
     ( sortByAbsolutePath
     ) where
 
-import Shikensu.Types (Definition, absolutePath)
+import Shikensu.Internal.Types (Definition, absolutePath)
 
 
 {-| Sort by absolutePath.
diff --git a/src/Shikensu/Types.hs b/src/Shikensu/Types.hs
deleted file mode 100644
--- a/src/Shikensu/Types.hs
+++ /dev/null
@@ -1,70 +0,0 @@
-{-| Types and path helpers.
-
--}
-module Shikensu.Types where
-
-import Data.Aeson (ToJSON, Object, (.=), object, toJSON)
-import Data.ByteString (ByteString)
-import System.FilePath (joinPath)
-
-
-{-| A file definition, along with some additional properties.
--}
-data Definition =
-    Definition
-        { basename :: String
-        , dirname :: FilePath
-        , extname :: String
-        , pattern :: Pattern
-        , rootDirname :: FilePath
-        , workingDirname :: FilePath
-
-        -- Additional properties
-        , content :: Maybe ByteString
-        , metadata :: Metadata
-        , parentPath :: Maybe FilePath
-        , pathToRoot :: FilePath
-        } deriving (Eq, Show)
-
-
-instance ToJSON Definition where
-    toJSON def =
-        object
-            [ "basename"        .= basename def
-            , "dirname"         .= dirname def
-            , "extname"         .= extname def
-            , "pattern"         .= pattern def
-            , "workingDirname"  .= workingDirname def
-            , "parentPath"      .= parentPath def
-            , "pathToRoot"      .= pathToRoot def
-            ]
-
-
-
-
--- Type aliases
-
-
-type Dictionary = [Definition]
-type Metadata = Object
-type Pattern = String
-
-
-
-
--- Path functions
-
-
-absolutePath :: Definition -> String
-absolutePath def =
-    joinPath [rootDirname def, workspacePath def]
-
-
-localPath :: Definition -> String
-localPath def =
-    joinPath [dirname def, (basename def) ++ (extname def)]
-
-
-workspacePath :: Definition -> String
-workspacePath def =
-    joinPath [workingDirname def, localPath def]
diff --git a/src/Shikensu/Utilities.hs b/src/Shikensu/Utilities.hs
--- a/src/Shikensu/Utilities.hs
+++ b/src/Shikensu/Utilities.hs
@@ -1,24 +1,25 @@
 module Shikensu.Utilities
-    ( io
-    , mapIO
-    , lsequence
-
+    ( (!~>)
     , (~>)
-    , (!~>)
+    , io
+    , lsequence
+    , mapIO
     ) where
 
 import Data.Aeson (FromJSON, ToJSON, fromJSON)
+import Data.Maybe (fromMaybe)
+import Data.Monoid ((<>))
 import Data.Text (Text)
 import Flow
-import Shikensu.Types
+import Shikensu.Internal.Types
 
 import qualified Data.Aeson as Json (Object, Result(..), encode)
 import qualified Data.HashMap.Strict as HashMap (lookup)
 import qualified Data.List as List (unzip, zip)
-import qualified Data.Tuple as Tuple (fst, snd)
-import qualified Data.Text as Text (concat, unpack)
+import qualified Data.Text as Text (unpack)
 import qualified Data.Text.Lazy as Lazy.Text (unpack)
 import qualified Data.Text.Lazy.Encoding as Lazy.Text (decodeUtf8)
+import qualified Data.Tuple as Tuple (fst, snd)
 
 
 -- IO
@@ -45,7 +46,7 @@
 
 From multiple IO monads to a single IO monad.
 -}
-lsequence :: Monad m => [( String, m a )] -> m [( String, a )]
+lsequence :: Monad m => [( id, m a )] -> m [( id, a )]
 lsequence list =
     let
         unzippedList =
@@ -84,8 +85,8 @@
     case (obj ~> key) of
         Just x  -> x
         Nothing -> error <|
-            "Could not find the key `" ++ (Text.unpack key)         ++ "` " ++
-            "on the metadata object `" ++ (aesonObjectToString obj) ++ "` using (!~>)"
+            "Could not find the key `" <> Text.unpack key         <> "` " <>
+            "on the metadata object `" <> aesonObjectToString obj <> "` using (!~>)"
 
 
 
diff --git a/tests/Test.hs b/tests/Test.hs
--- a/tests/Test.hs
+++ b/tests/Test.hs
@@ -1,23 +1,28 @@
-import Control.Exception (catch)
-import System.Directory (removeDirectoryRecursive)
+module Main where
+
+import System.Directory (canonicalizePath, removePathForcibly)
 import System.FilePath (combine)
 import Test.Contrib
 import Test.Example
-import Test.Helpers (ioErrorHandler, rootPath)
 import Test.Shikensu
 import Test.Tasty
 import Test.Utilities
 
 
 main :: IO ()
-main =
-    rootPath
-        >>= \r -> catch
-            (removeDirectoryRecursive (combine r "tests/build"))
-            (ioErrorHandler)
-        >> defaultMain tests
+main = do
+    root        <- canonicalizePath "./"
+    _           <- removePathForcibly (combine root "tests/build")
 
+    defaultMain tests
 
+
 tests :: TestTree
 tests =
-    testGroup "Tests" [shikensuTests, contribTests, exampleTests, utilityTests]
+    testGroup
+        "Tests"
+        [ shikensuTests
+        , contribTests
+        , exampleTests
+        , utilityTests
+        ]
diff --git a/tests/Test/Contrib.hs b/tests/Test/Contrib.hs
--- a/tests/Test/Contrib.hs
+++ b/tests/Test/Contrib.hs
@@ -1,4 +1,6 @@
-module Test.Contrib (contribTests) where
+module Test.Contrib
+    ( contribTests
+    ) where
 
 import Data.ByteString (ByteString)
 import Flow
@@ -13,12 +15,11 @@
 import qualified Data.HashMap.Strict as HashMap (fromList, lookup)
 import qualified Data.List as List (head, reverse)
 import qualified Data.Text as Text (pack, unpack)
-import qualified Data.Text.IO as Text (readFile)
 import qualified Data.Text.Encoding as Text (decodeUtf8)
+import qualified Data.Text.IO as Text (readFile)
 import qualified Shikensu
 import qualified Shikensu.Contrib as Contrib
 import qualified Shikensu.Contrib.IO as Contrib.IO
-import qualified Shikensu.Types as Shikensu
 
 
 contribTests :: TestTree
@@ -38,13 +39,12 @@
 
 
 
-
 -- Test data
 
 
-list :: Shikensu.Pattern -> IO Shikensu.Dictionary
+list :: String -> IO Shikensu.Dictionary
 list pattern =
-    rootPath >>= Shikensu.list [pattern]
+    Shikensu.listRelative [pattern] "."
 
 
 example_md :: IO Shikensu.Dictionary
@@ -64,7 +64,6 @@
 
 
 
-
 -- Tests
 
 
@@ -72,10 +71,10 @@
 testClone =
     let
         dictionary = fmap (Contrib.clone "example.md" "cloned.md") example_md
-        definition = fmap (List.head . List.reverse) dictionary
+        definition = fmap (List.reverse .> List.head) dictionary
     in
         testCase "Should `clone`"
-        $ definition `rmap` Shikensu.localPath >>= assertEq "cloned.md"
+        $ assertDef definition Shikensu.localPath "cloned.md"
 
 
 
@@ -83,10 +82,10 @@
 testExclude =
     let
         dictionary = fmap (Contrib.exclude "example.md") example_md
-        length_ = fmap length dictionary
+        dictLength = fmap length dictionary
     in
         testCase "Should `exclude`"
-        $ length_ >>= assertEq 0
+        $ assertEqm dictLength 0
 
 
 
@@ -108,33 +107,35 @@
         -- 1. Insert C
         -- 2. Replace with A
         -- 3. Insert B
-        dictionary = example_md
-            <&> ( Contrib.insertMetadata (HashMap.fromList [ (keyC, valueC) ])
-               .> Contrib.replaceMetadata (HashMap.fromList [ (keyA, valueA) ])
-               .> Contrib.copyPropsToMetadata
-               .> Contrib.insertMetadata (HashMap.fromList [ (keyB, valueB) ])
+        dictionary = fmap
+            ( id
+                .> Contrib.insertMetadata   (HashMap.fromList [ (keyC, valueC) ])
+                .> Contrib.replaceMetadata  (HashMap.fromList [ (keyA, valueA) ])
+                .> Contrib.copyPropsToMetadata
+                .> Contrib.insertMetadata   (HashMap.fromList [ (keyB, valueB) ])
             )
+            example_md
 
-        definition = fmap (List.head . List.reverse) dictionary
+        definition      = fmap (List.reverse .> List.head) dictionary
 
-        lookupTitle = \def -> HashMap.lookup keyA (Shikensu.metadata def)
-        lookupHello = \def -> HashMap.lookup keyB (Shikensu.metadata def)
-        lookupRemoved = \def -> HashMap.lookup keyC (Shikensu.metadata def)
-        lookupBasename = \def -> HashMap.lookup keyBase (Shikensu.metadata def)
+        lookupTitle     = \def -> HashMap.lookup keyA (Shikensu.metadata def)
+        lookupHello     = \def -> HashMap.lookup keyB (Shikensu.metadata def)
+        lookupRemoved   = \def -> HashMap.lookup keyC (Shikensu.metadata def)
+        lookupBasename  = \def -> HashMap.lookup keyBase (Shikensu.metadata def)
     in
         testGroup
             "Metadata"
             [ testCase "Should no longer have `removed` key"
-            $ definition `rmap` lookupRemoved >>= assertEq Nothing
+            $ assertDef definition lookupRemoved Nothing
 
             , testCase "Should have `hello` key"
-            $ definition `rmap` lookupHello >>= assertEq (Just valueB)
+            $ assertDef definition lookupHello (Just valueB)
 
             , testCase "Should have `title` key"
-            $ definition `rmap` lookupTitle >>= assertEq (Just valueA)
+            $ assertDef definition lookupTitle (Just valueA)
 
             , testCase "Should have `basename` key"
-            $ definition `rmap` lookupBasename >>= assertEq (Just valueBase)
+            $ assertDef definition lookupBasename (Just valueBase)
             ]
 
 
@@ -143,18 +144,18 @@
 testPermalink =
     let
         dictionary = fmap (Contrib.permalink "index") example_md
-        definition = fmap (List.head) dictionary
+        definition = fmap List.head dictionary
     in
         testGroup
             "Permalink"
             [ testCase "Should have the correct `localPath`"
-            $ definition `rmap` Shikensu.localPath >>= assertEq "example/index.md"
+            $ assertDef definition Shikensu.localPath "example/index.md"
 
             , testCase "Should have the correct `parentPath`"
-            $ definition `rmap` Shikensu.parentPath >>= assertEq (Just "../")
+            $ assertDef definition Shikensu.parentPath (Just "../")
 
             , testCase "Should have the correct `pathToRoot`"
-            $ definition `rmap` Shikensu.pathToRoot >>= assertEq "../"
+            $ assertDef definition Shikensu.pathToRoot "../"
             ]
 
 
@@ -168,13 +169,13 @@
         testGroup
             "PrefixDirname"
             [ testCase "Should have the correct `dirname`"
-            $ definition `rmap` Shikensu.dirname >>= assertEq "prefix/fixtures"
+            $ assertDef definition Shikensu.dirname "prefix/fixtures"
 
             , testCase "Should have the correct `pathToRoot`"
-            $ definition `rmap` Shikensu.pathToRoot >>= assertEq "../../"
+            $ assertDef definition Shikensu.pathToRoot "../../"
 
             , testCase "Should have the correct `parentPath`"
-            $ definition `rmap` Shikensu.parentPath >>= assertEq (Just "../")
+            $ assertDef definition Shikensu.parentPath (Just "../")
             ]
 
 
@@ -182,14 +183,11 @@
 testRead :: TestTree
 testRead =
     let
-        dictionary = example_md >>= Contrib.IO.read
-        definition = fmap List.head dictionary
+        theResult  = Just (Text.pack "# Example\n")
+        definition = fmap List.head (example_md >>= Contrib.IO.read)
     in
         testCase "Should `read`"
-        $ definition
-            <&> Shikensu.content
-            <&> fmap Text.decodeUtf8
-            >>= assertEq (Just (Text.pack "# Example\n"))
+        $ assertDef definition (Shikensu.content .> fmap Text.decodeUtf8) theResult
 
 
 
@@ -197,10 +195,10 @@
 testRename =
     let
         dictionary = fmap (Contrib.rename "example.md" "renamed.md") example_md
-        definition = fmap (List.head) dictionary
+        definition = fmap List.head dictionary
     in
         testCase "Should `rename`"
-        $ definition `rmap` Shikensu.localPath >>= assertEq "renamed.md"
+        $ assertDef definition Shikensu.localPath "renamed.md"
 
 
 
@@ -208,25 +206,22 @@
 testRenameExt =
     let
         dictionary = fmap (Contrib.renameExt ".md" ".html") example_md
-        definition = fmap (List.head) dictionary
+        definition = fmap List.head dictionary
     in
         testCase "Should `renameExt`"
-        $ definition `rmap` Shikensu.extname >>= assertEq ".html"
+        $ assertDef definition Shikensu.extname ".html"
 
 
 
 testRenderContent :: TestTree
 testRenderContent =
     let
+        theResult  = Just (Text.pack "<html># Example\n</html>")
         dictionary = fmap (Contrib.renderContent renderer) (example_md >>= Contrib.IO.read)
-        definition = fmap (List.head) dictionary
-        expectedResult = Just (Text.pack "<html># Example\n</html>")
+        definition = fmap List.head dictionary
     in
         testCase "Should `renderContent`"
-        $ definition
-            <&> Shikensu.content
-            <&> fmap Text.decodeUtf8
-            >>= assertEq expectedResult
+        $ assertDef definition (Shikensu.content .> fmap Text.decodeUtf8) theResult
 
 
 
@@ -234,11 +229,13 @@
 testWrite =
     let
         destination = "tests/build/"
-        dictionary = list "tests/**/example.md" >>= Contrib.IO.read >>= Contrib.IO.write destination
+        dictionary  = list "tests/**/example.md"
+            >>= Contrib.IO.read
+            >>= Contrib.IO.write destination
+
         definition = fmap List.head dictionary
     in
         testCase "Should `write`"
-        $ definition
-            <&> Shikensu.rootDirname
-            >>= \r -> Text.readFile (joinPath [r, destination, "fixtures/example.md"])
-            >>= \c -> assertEq "# Example\n" (Text.unpack c)
+        $ fmap Shikensu.rootDirname definition
+            >>= \r -> readFile (joinPath [r, destination, "fixtures/example.md"])
+            >>= \c -> assertEq "# Example\n" c
diff --git a/tests/Test/Example.hs b/tests/Test/Example.hs
--- a/tests/Test/Example.hs
+++ b/tests/Test/Example.hs
@@ -1,33 +1,40 @@
-module Test.Example (exampleTests) where
+module Test.Example
+    ( exampleTests
+    ) where
 
+import Data.ByteString (ByteString)
+import Data.Monoid ((<>))
 import Data.Text (Text)
+import Flow
+import Prelude hiding (read)
+import Shikensu (Definition(..), Dictionary(..), list, makeDefinition)
+import Shikensu.Contrib (clone, copyPropsToMetadata, permalink, renameExt, renderContent)
+import Shikensu.Contrib.IO (read, write)
 import System.Directory (canonicalizePath)
 import Test.Helpers
 import Test.Tasty
 import Test.Tasty.HUnit
 
 import qualified Data.Text.Encoding as Text (decodeUtf8, encodeUtf8)
-import qualified Shikensu
 
-import Data.ByteString (ByteString)
-import Flow
-import Prelude hiding (read)
-import Shikensu.Types
-import Shikensu.Contrib
-import Shikensu.Contrib.IO (read, write)
 
-
 exampleTests :: TestTree
 exampleTests =
-    let
-        path = canonicalizePath "./tests"
-        dict = path >>= dictionary_io
-        test = path >>= \p ->
-            read [Shikensu.makeDefinition p "fixtures/*.md" "fixtures/example.md"]
-            >>= flow
-    in
-        testCase "Example test"
-        $ dict >>= \d -> test >>= assertEq d
+    testCase "Example test" $ dictionaries >>= uncurry assertEq
+
+
+dictionaries :: IO (Dictionary, Dictionary)
+dictionaries = do
+    root            <- canonicalizePath "./tests"
+    dictA           <- dictionary_io root
+
+    let absolute    = root <> "/fixtures/example.md"
+    let dictB       = [Shikensu.makeDefinition root "fixtures/*.md" absolute]
+
+    dictB_Read      <- read dictB
+    dictB_Final     <- flow dictB_Read
+
+    return (dictA, dictB_Final)
 
 
 
diff --git a/tests/Test/Helpers.hs b/tests/Test/Helpers.hs
--- a/tests/Test/Helpers.hs
+++ b/tests/Test/Helpers.hs
@@ -1,30 +1,24 @@
 module Test.Helpers
-    ( (<&>)
-    , rmap
+    ( assertDef
     , assertEq
-    , ioErrorHandler
-    , rootPath
-    , sort
-    , testsPath
+    , assertEqm
+    , define
     ) where
 
+import Flow
+import Shikensu
 import Shikensu.Sorting (sortByAbsolutePath)
-import Shikensu.Types (Dictionary)
-import System.Directory (canonicalizePath)
-import System.FilePath (combine)
 import Test.Tasty.HUnit (Assertion, assertEqual)
 
-import qualified Data.List as List
+import qualified Data.List as List (head, sortBy)
 
 
-(<&>) :: Functor f => f a -> (a -> b) -> f b
-(<&>) =
-    flip fmap
+-- Assertions
 
 
-rmap :: Functor f => f a -> (a -> b) -> f b
-rmap =
-    (<&>)
+assertDef :: (Eq a, Show a) => IO Definition -> (Definition -> a) -> a -> Assertion
+assertDef definition recordFn value =
+    fmap recordFn definition >>= assertEq value
 
 
 assertEq :: (Eq a, Show a) => a -> a -> Assertion
@@ -32,21 +26,18 @@
     assertEqual ""
 
 
-ioErrorHandler :: IOError -> IO ()
-ioErrorHandler _ =
-    putStrLn ""
+assertEqm :: (Eq a, Show a) => IO a -> a -> Assertion
+assertEqm a b =
+    a >>= assertEqual "" b
 
 
-rootPath :: IO FilePath
-rootPath =
-    canonicalizePath "./"
 
-
-sort :: Dictionary -> Dictionary
-sort =
-    List.sortBy sortByAbsolutePath
+-- Shortcuts
 
 
-testsPath :: IO FilePath
-testsPath =
-    fmap ((flip combine) "tests") rootPath
+define :: String -> String -> IO Definition
+define pattern root =
+    root
+        |> Shikensu.listRelative [pattern]
+        |> fmap (List.sortBy sortByAbsolutePath)
+        |> fmap List.head
diff --git a/tests/Test/Shikensu.hs b/tests/Test/Shikensu.hs
--- a/tests/Test/Shikensu.hs
+++ b/tests/Test/Shikensu.hs
@@ -1,4 +1,6 @@
-module Test.Shikensu (shikensuTests) where
+module Test.Shikensu
+    ( shikensuTests
+    ) where
 
 import Test.Helpers
 import Test.Tasty
@@ -6,14 +8,16 @@
 
 import qualified Data.List as List (head)
 import qualified Shikensu
-import qualified Shikensu.Types as Shikensu
 
 
 shikensuTests :: TestTree
 shikensuTests = testGroup
     "Shikensu tests"
-    [testRegular, testDot, testWithoutWd, testRootFile, testRelative]
-
+    [ testRegular
+    , testDot
+    , testWithoutWd
+    , testRootFile
+    ]
 
 
 
@@ -23,29 +27,23 @@
 testRegular :: TestTree
 testRegular =
     let
-        pattern = "tests/**/*.md"
-        dictionary = fmap sort $ rootPath >>= Shikensu.list [pattern]
-        definition = fmap List.head dictionary
-        localPath = "fixtures/example.md"
+        definition = define "tests/**/*.md" "."
     in
         testGroup "Test regular"
             [ testCase "Should have the correct basename"
-            $ definition `rmap` Shikensu.basename >>= assertEq "example"
+            $ assertDef definition Shikensu.basename "example"
 
             , testCase "Should have the correct dirname"
-            $ definition `rmap` Shikensu.dirname >>= assertEq "fixtures"
+            $ assertDef definition Shikensu.dirname "fixtures"
 
             , testCase "Should have the correct extname"
-            $ definition `rmap` Shikensu.extname >>= assertEq ".md"
-
-            , testCase "Should have the correct localPath"
-            $ definition `rmap` Shikensu.localPath >>= assertEq localPath
+            $ assertDef definition Shikensu.extname ".md"
 
             , testCase "Should have the correct pattern"
-            $ definition `rmap` Shikensu.pattern >>= assertEq pattern
+            $ assertDef definition Shikensu.pattern "tests/**/*.md"
 
             , testCase "Should have the correct workingDirname"
-            $ definition `rmap` Shikensu.workingDirname >>= assertEq "tests"
+            $ assertDef definition Shikensu.workingDirname "tests"
 
             ]
 
@@ -53,29 +51,23 @@
 testDot :: TestTree
 testDot =
     let
-        pattern = "./tests/**/*.md"
-        dictionary = fmap sort $ rootPath >>= Shikensu.list [pattern]
-        definition = fmap List.head dictionary
-        localPath = "fixtures/example.md"
+        definition = define "./tests/**/*.md" "."
     in
         testGroup "Test dot"
             [ testCase "Should have the correct basename"
-            $ definition `rmap` Shikensu.basename >>= assertEq "example"
+            $ assertDef definition Shikensu.basename "example"
 
             , testCase "Should have the correct dirname"
-            $ definition `rmap` Shikensu.dirname >>= assertEq "fixtures"
+            $ assertDef definition Shikensu.dirname "fixtures"
 
             , testCase "Should have the correct extname"
-            $ definition `rmap` Shikensu.extname >>= assertEq ".md"
-
-            , testCase "Should have the correct localPath"
-            $ definition `rmap` Shikensu.localPath >>= assertEq localPath
+            $ assertDef definition Shikensu.extname ".md"
 
             , testCase "Should have the correct pattern"
-            $ definition `rmap` Shikensu.pattern >>= assertEq pattern
+            $ assertDef definition Shikensu.pattern "./tests/**/*.md"
 
             , testCase "Should have the correct workingDirname"
-            $ definition `rmap` Shikensu.workingDirname >>= assertEq "tests"
+            $ assertDef definition Shikensu.workingDirname "tests"
 
             ]
 
@@ -83,29 +75,23 @@
 testWithoutWd :: TestTree
 testWithoutWd =
     let
-        pattern = "**/*.md"
-        dictionary = fmap sort $ testsPath >>= Shikensu.list [pattern]
-        definition = fmap List.head dictionary
-        localPath = "fixtures/example.md"
+        definition = define "**/*.md" "./tests"
     in
         testGroup "Test without workingDirname"
             [ testCase "Should have the correct basename"
-            $ definition `rmap` Shikensu.basename >>= assertEq "example"
+            $ assertDef definition Shikensu.basename "example"
 
             , testCase "Should have the correct dirname"
-            $ definition `rmap` Shikensu.dirname >>= assertEq "fixtures"
+            $ assertDef definition Shikensu.dirname "fixtures"
 
             , testCase "Should have the correct extname"
-            $ definition `rmap` Shikensu.extname >>= assertEq ".md"
-
-            , testCase "Should have the correct localPath"
-            $ definition `rmap` Shikensu.localPath >>= assertEq localPath
+            $ assertDef definition Shikensu.extname ".md"
 
             , testCase "Should have the correct pattern"
-            $ definition `rmap` Shikensu.pattern >>= assertEq pattern
+            $ assertDef definition Shikensu.pattern "**/*.md"
 
             , testCase "Should have the correct workingDirname"
-            $ definition `rmap` Shikensu.workingDirname >>= assertEq ""
+            $ assertDef definition Shikensu.workingDirname ""
 
             ]
 
@@ -113,45 +99,22 @@
 testRootFile :: TestTree
 testRootFile =
     let
-        pattern = "*.md"
-        dictionary = fmap sort $ rootPath >>= Shikensu.list [pattern]
-        definition = fmap List.head dictionary
-        localPath = "CHANGELOG.md"
+        definition = define "*.md" "."
     in
         testGroup "Test file in root path"
             [ testCase "Should have the correct basename"
-            $ definition `rmap` Shikensu.basename >>= assertEq "CHANGELOG"
+            $ assertDef definition Shikensu.basename "CHANGELOG"
 
             , testCase "Should have the correct dirname"
-            $ definition `rmap` Shikensu.dirname >>= assertEq ""
+            $ assertDef definition Shikensu.dirname ""
 
             , testCase "Should have the correct extname"
-            $ definition `rmap` Shikensu.extname >>= assertEq ".md"
-
-            , testCase "Should have the correct localPath"
-            $ definition `rmap` Shikensu.localPath >>= assertEq localPath
+            $ assertDef definition Shikensu.extname ".md"
 
             , testCase "Should have the correct pattern"
-            $ definition `rmap` Shikensu.pattern >>= assertEq pattern
+            $ assertDef definition Shikensu.pattern "*.md"
 
             , testCase "Should have the correct workingDirname"
-            $ definition `rmap` Shikensu.workingDirname >>= assertEq ""
-
-            ]
-
-
-testRelative :: TestTree
-testRelative =
-    let
-        pattern = "*.md"
-        dictionary = fmap sort $ Shikensu.listRelative [pattern] "./"
-        definition = fmap List.head dictionary
-        localPath = "CHANGELOG.md"
-    in
-        testGroup "Test relative"
-            [ testCase "Should have the correct rootDirname"
-            $ definition `rmap` Shikensu.rootDirname >>= (\x -> rootPath >>= assertEq x)
+            $ assertDef definition Shikensu.workingDirname ""
 
-            , testCase "Should have the correct localPath"
-            $ definition `rmap` Shikensu.localPath >>= assertEq localPath
             ]
diff --git a/tests/Test/Utilities.hs b/tests/Test/Utilities.hs
--- a/tests/Test/Utilities.hs
+++ b/tests/Test/Utilities.hs
@@ -1,7 +1,9 @@
-module Test.Utilities (utilityTests) where
+module Test.Utilities
+    ( utilityTests
+    ) where
 
 import Flow
-import Shikensu.Types
+import Shikensu
 import Shikensu.Utilities as Utils
 import Test.Helpers
 import Test.Tasty
@@ -10,15 +12,13 @@
 import qualified Data.HashMap.Strict as HashMap (singleton)
 import qualified Data.List as List (head)
 import qualified Data.Tuple as Tuple (fst)
-import qualified Shikensu
 import qualified Shikensu.Contrib as Contrib
 
 
 utilityTests :: TestTree
 utilityTests = testGroup
     "Utility tests"
-    [testSequencing, testThunder]
-
+    [ testSequencing, testMetadataAccessors ]
 
 
 
@@ -30,40 +30,33 @@
     let
         result =
             Utils.lsequence
-                [ ( "a", rootPath >>= Shikensu.list ["tests/fixtures/example.md"] )
-                , ( "b", rootPath >>= Shikensu.list ["tests/fixtures/example.md"] )
+                [ ( "a", Shikensu.listRelative ["tests/fixtures/example.md"] "." )
+                , ( "b", Shikensu.listRelative ["tests/fixtures/example.md"] "." )
                 ]
     in
         testCase "Test lsequence"
-        $ (List.head .> Tuple.fst) <$> result >>= assertEq "a"
+        $ fmap (List.head .> Tuple.fst) result >>= assertEq "a"
 
 
-testThunder :: TestTree
-testThunder =
+testMetadataAccessors :: TestTree
+testMetadataAccessors =
     let
-        flow =
-            Contrib.insertMetadata (HashMap.singleton "a" "Hi!")
-            .> return
-
-        dictionary =
-            rootPath
-                >>= Shikensu.list ["tests/fixtures/example.md"]
-                >>= flow
-
-        def =
-            fmap List.head dictionary
+        definition =
+            "."
+                |> define "tests/fixtures/example.md"
+                |> fmap (Contrib.insertMetadataDef $ HashMap.singleton "a" "Hi!")
 
         resultExisting =
-            fmap (\d -> metadata d ~> "a" :: Maybe String) def
+            fmap (\d -> metadata d ~> "a" :: Maybe String) definition
 
         resultNonExisting =
-            fmap (\d -> metadata d ~> "b" :: Maybe String) def
+            fmap (\d -> metadata d ~> "b" :: Maybe String) definition
     in
         testGroup
             "Test (~>)"
             [ testCase "Existing"
-            $ resultExisting >>= assertEq (Just "Hi!")
+            $ assertEqm resultExisting (Just "Hi!")
 
             , testCase "Non-existing"
-            $ resultNonExisting >>= assertEq (Nothing)
+            $ assertEqm resultNonExisting Nothing
             ]
