diff --git a/Lastik.cabal b/Lastik.cabal
--- a/Lastik.cabal
+++ b/Lastik.cabal
@@ -1,5 +1,5 @@
 Name:               Lastik
-Version:            0.2
+Version:            0.3
 License:            BSD3
 License-File:       LICENSE
 Author:             Tony Morris <code@tmorris.net>
@@ -16,7 +16,7 @@
 
 Library
   if flag(small_base)
-    Build-Depends: base < 4 && >= 3, bytestring, directory, filepath, process, FileManip, zip-archive, pureMD5, SHA
+    Build-Depends: base < 4 && >= 3, bytestring, directory, filepath, process, zip-archive, pureMD5, SHA
   else
     Build-Depends: base < 3, filepath, process, FileManip, zip-archive, pureMD5, SHA
 
@@ -25,6 +25,7 @@
           Lastik.Compile,
           Lastik.Directory,
           Lastik.Extension,
+          Lastik.Find,
           Lastik.Output,
           Lastik.Runner,
           Lastik.Util,
diff --git a/Lastik/Compile.hs b/Lastik/Compile.hs
--- a/Lastik/Compile.hs
+++ b/Lastik/Compile.hs
@@ -3,14 +3,18 @@
 -- | A module for representing data types that can be compiled by taking a list of files.
 module Lastik.Compile (
                        Compile,
-                       compile
+                       compile,
+                       environmentCommand
                       ) where
 
 import Control.Monad
 import Data.List
+import Data.Maybe
 import System.Directory
 import System.Cmd
 import System.Exit
+import System.Environment
+import System.FilePath
 
 -- | A class of compilable data types.
 class Compile c where
@@ -21,3 +25,7 @@
 
 instance Compile ([FilePath] -> String) where
   compile = id
+
+environmentCommand :: String -> String -> IO String
+environmentCommand v c = (\e -> (case lookup v e of Nothing -> c
+                                                    Just k -> k </> c) ++ " ") `fmap` getEnvironment
diff --git a/Lastik/Directory.hs b/Lastik/Directory.hs
--- a/Lastik/Directory.hs
+++ b/Lastik/Directory.hs
@@ -13,7 +13,7 @@
 
 import System.Directory
 import System.FilePath
-import System.FilePath.Find
+import Lastik.Find
 import Codec.Archive.Zip
 import qualified Data.ByteString.Lazy as B
 import Control.Monad
@@ -29,7 +29,7 @@
 
 -- | Create a zip archive by changing into directories and archiving the contents.
 archiveDirectories :: [(FilePath, FilePath)] -- ^ A list of base directories to change to and contents of that directory to archive.
-                      -> RecursionPredicate  -- ^ The recursion predicate to search for files to archive.
+                      -> RecursePredicate  -- ^ The recursion predicate to search for files to archive.
                       -> FilterPredicate     -- ^ The filter predicate to search for files to archive.
                       -> [ZipOption]         -- ^ The options during the creation of the archive.
                       -> IO Archive          -- ^ The constructed archive.
@@ -38,7 +38,7 @@
 
 -- | Writes a zip archive to a file.
 writeArchive :: [(FilePath, FilePath)] -- ^ A list of base directories to change to and contents of that directory to archive.
-                -> RecursionPredicate  -- ^ The recursion predicate to search for files to archive.
+                -> RecursePredicate  -- ^ The recursion predicate to search for files to archive.
                 -> FilterPredicate     -- ^ The filter predicate to search for files to archive.
                 -> [ZipOption]         -- ^ The options during the creation of the archive.
                 -> FilePath            -- ^ The file to write the archive to.
@@ -46,9 +46,9 @@
 writeArchive dirs rp fp opts f = do a <- archiveDirectories dirs rp fp opts
                                     B.writeFile f (fromArchive a)
 
--- | Writes a zip archive to a file then computes a MD5 and SHA1 hash and writes them to files with @".MD5"@ and @".SHA"@ extensions.
+-- | Writes a zip archive to a file then computes a MD5 and SHA1 hash and writes them to files with @".md5"@ and @".sha1"@ extensions.
 writeHashArchive :: [(FilePath, FilePath)] -- ^ A list of base directories to change to and contents of that directory to archive.
-                    -> RecursionPredicate  -- ^ The recursion predicate to search for files to archive.
+                    -> RecursePredicate  -- ^ The recursion predicate to search for files to archive.
                     -> FilterPredicate     -- ^ The filter predicate to search for files to archive.
                     -> [ZipOption]         -- ^ The options during the creation of the archive.
                     -> FilePath            -- ^ The file to write the archive to and the prefix name of the files containing the hashes.
@@ -56,11 +56,10 @@
 writeHashArchive dirs rp fp opts f = do a <- archiveDirectories dirs rp fp opts
                                         let s = fromArchive a
                                         B.writeFile f s
-                                        writeFile (f <.> "MD5") (show (md5 s))
-                                        writeFile (f <.> "SHA") (show (sha1 s))
+                                        forM_ [(show . md5, "md5"), (show . sha1, "sha1")] (\(k, a) -> writeFile (f <.> a) (k s))
 
 -- | Copy the contents of a directory to another, perhaps trimming parent directories.
-copyDir :: RecursionPredicate -- ^ The recursion predicate to search for files in the source directory.
+copyDir :: RecursePredicate -- ^ The recursion predicate to search for files in the source directory.
            -> FilterPredicate -- ^ The filter predicate to search for files in the source directory.
            -> Int             -- ^ The number of parent directories to drop before copying to the target directory.
            -> FilePath        -- ^ The source directory.
diff --git a/Lastik/Find.hs b/Lastik/Find.hs
new file mode 100644
--- /dev/null
+++ b/Lastik/Find.hs
@@ -0,0 +1,192 @@
+module Lastik.Find where
+
+import Control.Applicative
+import Control.Monad
+import Control.Monad.Instances
+import Data.Monoid
+import System.FilePath
+import System.Directory
+import qualified System.FilePath as P
+import Lastik.Util
+
+newtype FilePather a = FilePather {
+  (<?>) :: FilePath -> a
+}
+
+instance Functor FilePather where
+  fmap f (FilePather k) = FilePather (f . k)
+
+instance Applicative FilePather where
+  FilePather f <*> FilePather a = FilePather (f <*> a)
+  pure = FilePather . const
+
+instance Monad FilePather where
+  FilePather f >>= k = FilePather (f >>= (<?>) . k)
+  return = pure
+
+instance (Monoid a) => Monoid (FilePather a) where
+  mempty = return mempty
+  FilePather x `mappend` FilePather y = FilePather (x `mappend` y)
+
+filePather :: (FilePath -> a) -> FilePather a
+filePather = FilePather
+
+filePath :: FilePather FilePath
+filePath = filePather id
+
+always :: FilePather Bool
+always = filePather (\_ -> True)
+
+always' :: FilePather (a -> Bool)
+always' = constant always
+
+never :: FilePather Bool
+never = filePather (\_ -> False)
+
+never' :: FilePather (a -> Bool)
+never' = constant never
+
+extension :: FilePather FilePath
+extension = filePather takeExtension
+
+extension' :: FilePather (a -> FilePath)
+extension' = constant extension
+
+directory :: FilePather FilePath
+directory = filePather takeDirectory
+
+directory' :: FilePather (a -> FilePath)
+directory' = constant directory
+
+hasExtension :: FilePather Bool
+hasExtension = filePather P.hasExtension
+
+hasExtension' :: FilePather (a -> Bool)
+hasExtension' = constant Lastik.Find.hasExtension
+
+splitExtension :: FilePather (String, String)
+splitExtension = filePather P.splitExtension
+
+splitExtension' :: FilePather (a -> (String, String))
+splitExtension' = constant Lastik.Find.splitExtension
+
+splitDirectories :: FilePather [FilePath]
+splitDirectories = filePather P.splitDirectories
+
+splitDirectories' :: FilePather (a -> [FilePath])
+splitDirectories' = constant Lastik.Find.splitDirectories
+
+hasTrailingPathSeparator :: FilePather Bool
+hasTrailingPathSeparator = filePather P.hasTrailingPathSeparator
+
+hasTrailingPathSeparator' :: FilePather (a -> Bool)
+hasTrailingPathSeparator' = constant Lastik.Find.hasTrailingPathSeparator
+
+fileName :: FilePather FilePath
+fileName = filePather takeFileName
+
+fileName' :: FilePather (a -> FilePath)
+fileName' = constant fileName
+
+baseName :: FilePather FilePath
+baseName = filePather takeBaseName
+
+baseName' :: FilePather (a -> FilePath)
+baseName' = constant baseName
+
+normalise :: FilePather FilePath
+normalise = filePather P.normalise
+
+normalise' :: FilePather (a -> FilePath)
+normalise' = constant Lastik.Find.normalise
+
+makeValid :: FilePather FilePath
+makeValid = filePather P.makeValid
+
+makeValid' :: FilePather (a -> FilePath)
+makeValid' = constant Lastik.Find.makeValid
+
+isRelative :: FilePather Bool
+isRelative = filePather P.isRelative
+
+isRelative' :: FilePather (a -> Bool)
+isRelative' = constant Lastik.Find.isRelative
+
+isAbsolute :: FilePather Bool
+isAbsolute = filePather P.isAbsolute
+
+isAbsolute' :: FilePather (a -> Bool)
+isAbsolute' = constant Lastik.Find.isAbsolute
+
+isValid :: FilePather Bool
+isValid = filePather P.isValid
+
+isValid' :: FilePather (a -> Bool)
+isValid' = constant Lastik.Find.isValid
+
+not' :: (Functor f) => f Bool -> f Bool
+not' = fmap not
+
+constant :: (Functor f) => f a -> f (t -> a)
+constant p = fmap const p
+
+(==?) :: (Eq a, Functor f) => f a -> a -> f Bool
+p ==? a = fmap (a ==) p
+
+(/=?) :: (Eq a, Functor f) => f a -> a -> f Bool
+p /=? a = fmap (a /=) p
+
+(==>) :: (Applicative f) => f Bool -> f Bool -> f Bool
+(==>) = liftA2 (\p q -> not p || q)
+
+(===>) :: (Applicative f1, Applicative f2) => f1 (f2 Bool) -> f1 (f2 Bool) -> f1 (f2 Bool)
+(===>) = liftA2 (==>)
+
+(/=>) :: (Applicative f) => f Bool -> f Bool -> f Bool
+(/=>) = liftA2 (\p q -> not q && p)
+
+(/==>) :: (Applicative f1, Applicative f2) => f1 (f2 Bool) -> f1 (f2 Bool) -> f1 (f2 Bool)
+(/==>) = liftA2 (/=>)
+
+(&&?) :: (Applicative f) => f Bool -> f Bool -> f Bool
+(&&?) = liftA2 (&&)
+
+(?&&?) :: (Applicative f1, Applicative f2) => f1 (f2 Bool) -> f1 (f2 Bool) -> f1 (f2 Bool)
+(?&&?) = liftA2 (&&?)
+
+(||?) :: (Applicative f) => f Bool -> f Bool -> f Bool
+(||?) = liftA2 (||)
+
+(?||?) :: (Applicative f1, Applicative f2) => f1 (f2 Bool) -> f1 (f2 Bool) -> f1 (f2 Bool)
+(?||?) = liftA2 (||?)
+
+data FileType = File | Directory | Unknown deriving (Eq, Show)
+
+type RecursePredicate = FilePather Bool
+type FilterPredicate = FilePather (FileType -> Bool)
+
+isFile :: (Applicative f) => f (FileType -> Bool)
+isFile = pure (== File)
+
+isDirectory :: (Applicative f) => f (FileType -> Bool)
+isDirectory = pure (== Directory)
+
+isUnknown :: (Applicative f) => f (FileType -> Bool)
+isUnknown = pure (== Unknown)
+
+find :: RecursePredicate -> FilterPredicate -> FilePath -> IO [FilePath]
+find = find' []
+  where
+  find' :: FilePath -> RecursePredicate -> FilterPredicate -> FilePath -> IO [FilePath]
+  find' k r f p = let z = if null k then p else k </> p
+                      z' t = if f <?> z $ t then [z] else []
+                  in ifM (doesFileExist z)
+                       (return (z' File)) $
+                       do e <- doesDirectoryExist z
+                          if e
+                            then if r <?> z
+                                   then do c <- getDirectoryContents z
+                                           t <- fmap join $ forM (filter (`notElem` [".", ".."]) c) (find' k r f . (z </>))
+                                           return (z' Directory ++ t)
+                                   else return (z' Directory)
+                            else return (z' Unknown)
diff --git a/Lastik/Output.hs b/Lastik/Output.hs
--- a/Lastik/Output.hs
+++ b/Lastik/Output.hs
@@ -17,7 +17,7 @@
 import Control.Monad.Instances
 import Data.Maybe
 import System.Directory
-import System.FilePath.Find
+import Lastik.Find
 
 -- | A class of data types that have a potential output target.
 class Output o where
@@ -86,5 +86,5 @@
 d <===> s = case output d of Nothing -> return s
                              Just k -> do e <- doesDirectoryExist k
                                           if e
-                                            then find always always k >>= (\t -> t <==> s)
+                                            then find always always' k >>= (\t -> t <==> s)
                                             else return s
diff --git a/Lastik/Runner.hs b/Lastik/Runner.hs
--- a/Lastik/Runner.hs
+++ b/Lastik/Runner.hs
@@ -22,7 +22,7 @@
 import System.Cmd
 import System.Directory
 import System.Exit
-import System.FilePath.Find
+import Lastik.Find
 import Lastik.Compile
 import Lastik.Extension
 import Lastik.Output
@@ -86,7 +86,7 @@
 pathTransform' :: (Extension e) => e -> Runner r -> Runner r
 pathTransform' = pathTransform . recurse
   where
-  recurse c p = fmap concat $ find always (extension ==? ext' c) `mapM` p
+  recurse c p = fmap concat $ find always (constant $ extension ==? ext' c) `mapM` p
 
 -- | Execute the compile result as a system command.
 (!!!) :: (Compile c) => Runner c
diff --git a/Lastik/Util.hs b/Lastik/Util.hs
--- a/Lastik/Util.hs
+++ b/Lastik/Util.hs
@@ -16,7 +16,8 @@
                    (^^^),
                    space,
                    space',
-                   uncons
+                   uncons,
+                   ifM
                   ) where
 
 import System.Exit
@@ -132,3 +133,9 @@
 uncons x _ [] = x
 uncons _ f (h:t) = f h t
 
+-- | if/then/else with a lifted predicate.
+ifM :: (Monad f) => f Bool -> f a -> f a -> f a
+ifM c t f = do c' <- c
+               t' <- t
+               f' <- f
+               return (if c' then t' else f')
