diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -61,3 +61,8 @@
 + Added `class Container (Contained, (?))`
 + `fmap` is exported as `map`
 
+## v0.10
++ Added `safeHeadAndTail`, `safeHeadAndTail2`, `firstJust`
++ Little documentation improvements
++ Added `findFilesRecursively` and `findFilesRecursivelyWithContext`
+  in `Hydrogen.Prelude.System`
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -138,7 +138,7 @@
 
 The Hydrogen Prelude offers you the functions and datatypes from these modules, all with one import:
 
-+ from `base`
++ from [`base`](http://hackage.haskell.org/package/base)
   + `module Prelude`
   + `module Control.Applicative`
   + `module Control.Arrow`
@@ -168,44 +168,79 @@
   + `module Numeric`
   + `module Text.Printf`
 
-+ from `array`
++ from [`array`](http://hackage.haskell.org/package/array)
   + `module Data.Array`
 
-+ from `cereal`
++ from [`cereal`](http://hackage.haskell.org/package/cereal)
   + `module Data.Serialize`
 
-+ from `containers`
++ from [`containers`](http://hackage.haskell.org/package/containers)
   + `Data.Set`, `Data.Map`, and `Data.Seq`
 
-+ from `hashable`
++ from [`hashable`](http://hackage.haskell.org/package/hashable)
   + `module Data.Hashable`
 
-+ form `hydrogen-multimap`
++ form [`hydrogen-multimap`](http://hackage.haskell.org/package/hydrogen-multimap)
   + `Hydrogen.MultiMap`
 
-+ from `hydrogen-version`
++ from [`hydrogen-version`](http://hackage.haskell.org/package/hydrogen-version)
   + `module Hydrogen.Version`
 
-+ from `regex-tdfa`
++ from [`regex-tdfa`](http://hackage.haskell.org/package/regex-tdfa)
   + `module Text.Regex.TDFA`
 
-+ from `time`
++ from [`time`](http://hackage.haskell.org/package/time)
   + `module Data.Time`
 
-+ from `transformers`
++ from [`transformers`](http://hackage.haskell.org/package/transformers)
   + `module Data.Functor.Identity`
   + `module Data.Functor.Reverse`
 
-+ from `uuid`
++ from [`uuid`](http://hackage.haskell.org/package/uuid)
   + `Data.UUID`
   + `Data.UUID.fromString` as `uuidFromString`
   + `Data.UUID.V4.nextRandom` as `randomUUID`
 
+
 ### Hydrogen.Prelude.IO
 
++ from [`base`](http://hackage.haskell.org/package/base)
+  + `module Data.IORef`
+  + `module Control.Concurret`
+  + `module Control.Exception`
+  + `module System.IO`
+  + `module System.Timeout`
+
++ from [`strict`](http://hackage.haskell.org/package/strict)
+  + strict IO functions `hGetContents'`, `getContents'`, `readFile'`, `interact'`
+
+
 ### Hydrogen.Prelude.System
 
++ from [`base`](http://hackage.haskell.org/package/base)
+  + `module System.CPUTime`
+  + `module System.Environment`
+  + `module System.Exit`
+  + `module System.Info`
+
++ from [`directory`](http://hackage.haskell.org/package/directory)
+  + `module System.Directory`
+
++ from [`filepath`](http://hackage.haskell.org/package/filepath)
+  + `module System.FilePath`
+
++ from [`process`](http://hackage.haskell.org/package/process)
+  + `module System.Process`
+
++ from [`random`](http://hackage.haskell.org/package/random)
+  + `module System.Random`
+
+
 ### Hydrogen.Prelude.Network
+
++ from [`network`](http://hackage.haskell.org/package/network)
+  + `module Network`
+
 
 FAQ
 ===
diff --git a/hydrogen-prelude.cabal b/hydrogen-prelude.cabal
--- a/hydrogen-prelude.cabal
+++ b/hydrogen-prelude.cabal
@@ -1,5 +1,5 @@
 name:                 hydrogen-prelude
-version:              0.9
+version:              0.10
 homepage:             http://scravy.de/hydrogen-prelude/
 synopsis:             Hydrogen Prelude
 license:              MIT
diff --git a/src/Hydrogen/Prelude.hs b/src/Hydrogen/Prelude.hs
--- a/src/Hydrogen/Prelude.hs
+++ b/src/Hydrogen/Prelude.hs
@@ -27,6 +27,7 @@
   , module Data.Serialize
   , module Data.String
   , module Data.Time
+  , module Data.Time.Calendar.OrdinalDate
   , module Data.Traversable
   , module Data.Tuple
   , module Data.Typeable
@@ -43,6 +44,9 @@
   , uuidFromString
   , randomUUID
   , safeHead
+  , safeHeadAndTail
+  , safeHeadAndTail2
+  , firstJust
   , map
   , UUID
   , Generic
@@ -142,6 +146,7 @@
 import "cereal" Data.Serialize
 import "base" Data.String
 import "time" Data.Time
+import "time" Data.Time.Calendar.OrdinalDate
 import "base" Data.Traversable
 import "base" Data.Tuple
 import "base" Data.Typeable
@@ -211,23 +216,51 @@
 f .^ g = \x -> f x /= g x
 
 (|>) :: a -> (a -> b) -> b
+-- ^ @flip ('$')@
 (|>) = flip ($)
 
 __ :: a
+-- ^ A shorthand for 'undefined'.
 __ = error "Hydrogen.Prelude.undefined"
 
 uuidFromString :: String -> Maybe UUID
 uuidFromString = Data.UUID.fromString
 
 randomUUID :: IO UUID
+-- ^ Produces a random V4 UUID (alias for 'Data.UUID.V4.nextRandom').
 randomUUID = Data.UUID.V4.nextRandom
 
-safeHead :: a -> [a] -> a
-safeHead d = \case
+safeHead
+    :: a   -- ^ The default value for the case of the empty list.
+    -> [a] -- ^ The list.
+    -> a
+-- ^ Returns the head of the list or the default value.
+safeHead def = \case
     x : _ -> x
-    _     -> d
+    _     -> def
 
+safeHeadAndTail :: a -> [a] -> (a, [a])
+safeHeadAndTail def = \case
+    x : xs -> (x, xs)
+    _ -> (def, [])
+
+safeHeadAndTail2 :: a -> a -> [a] -> (a, a, [a])
+safeHeadAndTail2 d1 d2 = \case
+    x : y : xs -> (x, y, xs)
+    x : xs -> (x, d2, xs)
+    xs -> (d1, d2, xs)
+
+firstJust :: [a -> Maybe b] -> a -> Maybe b
+-- ^ Applies a bunch of functions on a given value,
+--   returns the first result that is not Nothing
+--   (or 'Nothing' if no 'Just' value was produced).
+firstJust (f : fs) v = case f v of
+    Nothing -> firstJust fs v
+    x -> x
+firstJust [] _ = Nothing
+
 map :: Functor f => (a -> b) -> f a -> f b
+-- ^ map as it should be: 'fmap'.
 map = fmap
 
 class TMap a where
diff --git a/src/Hydrogen/Prelude/System.hs b/src/Hydrogen/Prelude/System.hs
--- a/src/Hydrogen/Prelude/System.hs
+++ b/src/Hydrogen/Prelude/System.hs
@@ -1,3 +1,5 @@
+{-# OPTIONS_GHC -fno-warn-name-shadowing #-}
+
 module Hydrogen.Prelude.System (
     module Hydrogen.Prelude.IO
   , module System.CPUTime
@@ -8,6 +10,10 @@
   , module System.Info
   , module System.Process
   , module System.Random
+  , findFilesRecursively
+  , findFilesRecursivelyWithContext
+  , escapeFileName
+  , unescapeFileName
 ) where
 
 import Hydrogen.Prelude.IO
@@ -20,4 +26,78 @@
 import "base" System.Info
 import "process" System.Process
 import "random" System.Random
+
+import qualified Data.Set as Set
+
+
+findFilesRecursively :: (FilePath -> IO Bool) -> FilePath -> IO [FilePath]
+findFilesRecursively f dir =
+    map fst <$> findFilesRecursivelyWithContext (\c _ _ -> return c) f () dir
+
+
+findFilesRecursivelyWithContext
+    :: forall c.
+       (c -> FilePath -> [FilePath] -> IO c)  -- ^ update function for current context
+    -> (FilePath -> IO Bool)                  -- ^ predicate to filter files
+    -> c                                      -- ^ current context
+    -> FilePath -> IO [(FilePath, c)]
+findFilesRecursivelyWithContext updater predicate context dir = do
+
+    cwd <- getCurrentDirectory
+    snd <$> find Set.empty context (cwd </> dir)
+
+  where
+
+    find :: Set FilePath -> c -> FilePath -> IO (Set FilePath, [(FilePath, c)])
+    find visited context dir = do
+
+      thisDirectory <- canonicalizePath dir
+      if | Set.member thisDirectory visited -> return (Set.empty, [])
+         | otherwise -> do
+
+            allFiles <- map (dir </>) <$> getDirectoryContents dir
+            theFiles <- filterFiles allFiles
+            theDirs  <- filterM isDir allFiles
+            context' <- updater context dir theFiles
+
+            let visited' = Set.insert thisDirectory visited
+                f (visited, files) dir = do
+                    (visited', files') <- find visited context' dir
+                    return (visited', files' : files)
+
+            (visited'', files') <- foldM f (visited', []) theDirs
+            
+            return (visited'', concat (zip theFiles (repeat context') : files'))
+
+    filterFiles = filterM (\x -> liftM2 (&&) (doesFileExist x) (predicate x))
+    isDir x = liftM2 (&&) (doesDirectoryExist x) (return (head (takeFileName x) /= '.'))
+
+
+escapeFileName :: String -> String
+escapeFileName s = case s of
+    ('/' : xs)
+        -> '_' : escapeFileName xs
+    (x : xs) ->
+      if | isSafeChar x -> x : escapeFileName xs
+         | ord x <= 255 -> '$' : printf "%02X" (ord x) ++ escapeFileName xs
+         | otherwise    -> "$$" ++ printf "%04X" (ord x) ++ escapeFileName xs
+    [] -> []
+  where
+    isSafeChar x = isAscii x && isAlphaNum x || x `elem` ".-"
+
+
+unescapeFileName :: String -> Maybe String
+unescapeFileName s = case s of
+    ('$' : '$' : a : b : c : d : xs)
+        -> (chr <$> hexnum (a : b : c : [d])) `cons` unescapeFileName xs
+    ('$' : a : b : xs)
+        -> (chr <$> hexnum (a : [b])) `cons` unescapeFileName xs
+    ('_' : xs)
+        -> pure '/' `cons` unescapeFileName xs
+    (x : xs)
+        -> pure x `cons` unescapeFileName xs
+    [] -> return []
+  where
+    cons = liftA2 (:)
+    hexnum = fmap fst . listToMaybe . readHex
 
