diff --git a/CHANGES.txt b/CHANGES.txt
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,5 +1,9 @@
 Changelog for filepattern
 
+0.1.2, released 2020-02-26
+    Optimise matchMany for empty lists
+    Remove support for GHC 7.4 to 7.8
+    Make Directory module reexport FilePattern
 0.1.1, released 2019-02-12
     Switch to https://github.com/ndmitchell/filepattern
 0.1, released 2019-02-12
diff --git a/LICENSE b/LICENSE
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright Neil Mitchell 2011-2019.
+Copyright Neil Mitchell 2011-2020.
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -1,9 +1,27 @@
-# FilePattern [![Hackage version](https://img.shields.io/hackage/v/filepattern.svg?label=Hackage)](https://hackage.haskell.org/package/filepattern) [![Stackage version](https://www.stackage.org/package/filepattern/badge/nightly?label=Stackage)](https://www.stackage.org/package/filepattern) [![Linux Build Status](https://img.shields.io/travis/ndmitchell/filepattern/master.svg?label=Linux%20build)](https://travis-ci.org/ndmitchell/filepattern) [![Windows Build Status](https://img.shields.io/appveyor/ci/ndmitchell/filepattern/master.svg?label=Windows%20build)](https://ci.appveyor.com/project/ndmitchell/filepattern)
+# FilePattern [![Hackage version](https://img.shields.io/hackage/v/filepattern.svg?label=Hackage)](https://hackage.haskell.org/package/filepattern) [![Stackage version](https://www.stackage.org/package/filepattern/badge/nightly?label=Stackage)](https://www.stackage.org/package/filepattern) [![Linux build status](https://img.shields.io/travis/ndmitchell/filepattern/master.svg?label=Linux%20build)](https://travis-ci.org/ndmitchell/filepattern) [![Windows build status](https://img.shields.io/appveyor/ci/ndmitchell/filepattern/master.svg?label=Windows%20build)](https://ci.appveyor.com/project/ndmitchell/filepattern)
 
-A file path matching library, loosely modelled on the semantics from:
+A library for matching files using patterns such as `src/**/*.png` for all `.png` files recursively under the `src` directory. There are two special forms:
 
-* [VS Code](https://code.visualstudio.com/docs/editor/codebasics#_advanced-search-options).
-* [Git](https://git-scm.com/docs/gitignore).
-* [Glob](https://www.npmjs.com/package/glob).
+* `*` matches part of a path component, excluding any separators.
+* `**` as a path component matches an arbitrary number of path components.
 
-Originally part of [Shake](http://shakebuild.com/), now split off as a separate project.
+Some examples:
+
+* `test.c` matches `test.c` and nothing else.
+* `*.c` matches all `.c` files in the current directory, so `file.c` matches, but `file.h` and `dir/file.c` don't.
+* `**/*.c` matches all `.c` files anywhere on the filesystem, so `file.c`, `dir/file.c`, `dir1/dir2/file.c` and `/path/to/file.c` all match, but `file.h` and `dir/file.h` don't.
+* `dir/*/*` matches all files one level below `dir`, so `dir/one/file.c` and `dir/two/file.h` match, but `file.c`, `one/dir/file.c`, `dir/file.h` and `dir/one/two/file.c` don't.
+
+More complete semantics are given in the documentation for the matching function [`?==`](https://hackage.haskell.org/package/filepattern/docs/System-FilePattern.html#v:-63--61--61-).
+
+## Features
+
+* All matching is _O(n)_. Most functions precompute some information given only one argument. There are also functions to provide bulk matching of many patterns against many paths simultaneously, see [`step`](https://hackage.haskell.org/package/filepattern/docs/System-FilePattern.html#v:step) and [`matchMany`](https://hackage.haskell.org/package/filepattern/docs/System-FilePattern.html#v:matchMany).
+* You can obtain the parts that matched the `*` and `**` special forms using [`match`](https://hackage.haskell.org/package/filepattern/docs/System-FilePattern.html#v:match), and substitute them into other patterns using [`substitute`](https://hackage.haskell.org/package/filepattern/docs/System-FilePattern.html#v:substitute).
+* You can search for files using a minimal number of IO operations, using the [System.FilePattern.Directory module](https://hackage.haskell.org/package/filepattern-0.1.1/docs/System-FilePattern-Directory.html).
+
+## Related work
+
+* Another Haskell file pattern matching library is [Glob](https://hackage.haskell.org/package/Glob), which aims to be closer to the [POSIX `glob()` function](http://man7.org/linux/man-pages/man7/glob.7.html), with forms such as `*`, `?`, `**/` (somewhat different to the `filepattern` equivalent) and `[:alpha:]`. A complete guide is [in the documentation](https://hackage.haskell.org/package/Glob/docs/System-FilePath-Glob.html#v:compile). Compared to `filepattern`, the `Glob` library is closer to a regular expression library - definitely more powerful, potentially harder to use.
+* The [`shake` library](https://shakebuild.com/) has contained a `FilePattern` type since the beginning. This library evolved from that code, with significant improvements.
+* The semantics are heavily inspired by [VS Code](https://code.visualstudio.com/docs/editor/codebasics#_advanced-search-options), [Git](https://git-scm.com/docs/gitignore) and the [NPM package Glob](https://www.npmjs.com/package/glob).
diff --git a/filepattern.cabal b/filepattern.cabal
--- a/filepattern.cabal
+++ b/filepattern.cabal
@@ -1,19 +1,19 @@
 cabal-version:      >= 1.18
 build-type:         Simple
 name:               filepattern
-version:            0.1.1
+version:            0.1.2
 license:            BSD3
 license-file:       LICENSE
 category:           Development, FilePath
 author:             Neil Mitchell <ndmitchell@gmail.com>, Evan Rutledge Borden <evan@evan-borden.com>
 maintainer:         Neil Mitchell <ndmitchell@gmail.com>
-copyright:          Neil Mitchell 2011-2019
+copyright:          Neil Mitchell 2011-2020
 synopsis:           File path glob-like matching
 description:
     A library for matching files using patterns such as @\"src\/**\/*.png\"@ for all @.png@ files
     recursively under the @src@ directory. Features:
     .
-    * All matching is /O(n)/.
+    * All matching is /O(n)/. Most functions precompute some information given only one argument.
     .
     * See "System.FilePattern" and @?==@ simple matching and semantics.
     .
@@ -28,7 +28,7 @@
     Originally taken from the <https://hackage.haskell.org/package/shake Shake library>.
 homepage:           https://github.com/ndmitchell/filepattern#readme
 bug-reports:        https://github.com/ndmitchell/filepattern/issues
-tested-with:        GHC==8.6.3, GHC==8.4.4, GHC==8.2.2, GHC==8.0.2, GHC==7.10.3, GHC==7.8.4, GHC==7.6.3, GHC==7.4.2
+tested-with:        GHC==8.8.1, GHC==8.6.5, GHC==8.4.4, GHC==8.2.2, GHC==8.0.2, GHC==7.10.3
 extra-doc-files:
     CHANGES.txt
     README.md
diff --git a/src/System/FilePattern.hs b/src/System/FilePattern.hs
--- a/src/System/FilePattern.hs
+++ b/src/System/FilePattern.hs
@@ -4,7 +4,7 @@
 --  recursively under the @src@ directory. See '?==' for the semantics of
 --  'FilePattern' values. Features:
 --
---  * All matching is /O(n)/.
+--  * All matching is /O(n)/. Most functions precompute some information given only one argument.
 --
 --  * Use 'match' and 'substitute' to extract suitable
 --  strings from the @*@ and @**@ matches, and substitute them back into other patterns.
@@ -108,8 +108,11 @@
 --
 -- > matchMany [(a, pat)] [(b, path)] == maybeToList (map (a,b,) (match pat path))
 matchMany :: [(a, FilePattern)] -> [(b, FilePath)] -> [(a, b, [String])]
-matchMany pats = f (step pats) . makeTree . map (second $ (\(Core.Path x) -> x) . parsePath)
+matchMany [] = const []
+matchMany pats = \files -> if null files then [] else f spats $ makeTree $ map (second $ (\(Core.Path x) -> x) . parsePath) files
     where
+        spats = step pats
+
         f Step{..} (Tree bs xs) = concat $
             [(a, b, ps) | (a, ps) <- stepDone, b <- bs] :
             [f (stepApply x) t | (x, t) <- xs, case stepNext of StepOnly xs -> x `elem` xs; _ -> True]
diff --git a/src/System/FilePattern/Directory.hs b/src/System/FilePattern/Directory.hs
--- a/src/System/FilePattern/Directory.hs
+++ b/src/System/FilePattern/Directory.hs
@@ -1,5 +1,6 @@
 
 -- | Optimised directory traversal using 'FilePattern' values.
+--   All results are guaranteed to be sorted.
 --
 --   /Case Sensitivity/: these traversals are optimised to reduce the number of IO operations
 --   performed. In particular, if the relevant subdirectories can be determined in
@@ -12,6 +13,7 @@
 --   If these optimisation differences are absolutely unacceptable use 'getDirectoryFilesIgnoreSlow'.
 --   However, normally these differences are not a problem.
 module System.FilePattern.Directory(
+    FilePattern,
     getDirectoryFiles,
     getDirectoryFilesIgnore,
     getDirectoryFilesIgnoreSlow
@@ -43,8 +45,10 @@
 
 
 -- | Get the files below a certain root matching any of the first set of 'FilePattern' values,
---   but don't return any files which match any ignore pattern. Typically the ignore pattens will
---   end with @\/**@, e.g. @.git\/**@.
+--   but don't return any files which match any ignore pattern (the final argument).
+--   Typically the ignore pattens will end with @\/**@, e.g. @.git\/**@.
+--
+-- > getDirectoryFilesIgnore "myproject/src" ["**/*.h","**/*.c"] [".git/**"]
 --
 --   /Warning/: on case-insensitive file systems certain optimisations can cause surprising results.
 --   See the top of the module for details.
diff --git a/src/System/FilePattern/Step.hs b/src/System/FilePattern/Step.hs
--- a/src/System/FilePattern/Step.hs
+++ b/src/System/FilePattern/Step.hs
@@ -83,7 +83,7 @@
         | [s] <- ss = s
         | otherwise = Step
             {stepDone = concatMap stepDone ss
-            ,stepNext = mconcat $ map stepNext ss
+            ,stepNext = mconcatMap stepNext ss
             ,stepApply = \x -> fastFoldMap (`stepApply` x) ss
             }
 
@@ -93,6 +93,7 @@
     mconcat = maybe mempty sconcat . NE.nonEmpty -- important: use the fast sconcat
 
 fastFoldMap :: Monoid m => (a -> m) -> [a] -> m
+{- HLINT ignore fastFoldMap -}
 fastFoldMap f = mconcat . map f -- important: use the fast mconcat
 
 
diff --git a/test/Test/Util.hs b/test/Test/Util.hs
--- a/test/Test/Util.hs
+++ b/test/Test/Util.hs
@@ -38,7 +38,7 @@
 testData = unsafePerformIO $ newIORef $ TestData 0 [] []
 
 addTestData :: [FilePattern] -> [FilePath] -> IO ()
-addTestData pats paths = atomicModifyIORef' testData $ \t -> (f t, ())
+addTestData pats paths = atomicModifyIORef'_ testData f
     where f TestData{..} = TestData (testDataCases+1) (reverse pats ++ testDataPats) (reverse paths ++ testDataPaths)
 
 unsafeTestData :: IO TestData
