diff --git a/CHANGES.markdown b/CHANGES.markdown
new file mode 100644
--- /dev/null
+++ b/CHANGES.markdown
@@ -0,0 +1,484 @@
+## Changes in 2.4.3
+  - Read command-line options from environment variable `HSPEC_OPTIONS`
+
+## Changes in 2.4.2
+  - Use `--diff` by default
+  - Add `--failure-report` (see #266)
+
+## Changes in 2.4.1
+  - Compatibility with HUnit < 1.3
+
+## Changes in 2.4.0
+  - Read command-line options from config files `~/.hspec` and `.hspec`
+  - Add support for `--diff`
+  - Add `xit`, `xspecify`, `xdescribe` and `xcontext` (see #252)
+  - Add `--rerun-all-on-success`
+  - Report exceptions in `beforeAll` operations only once
+  - Add indentation when actual/expected contain newlines (see #263)
+  - More graceful shutdown on ctrl-c (see #270)
+  - Run around-hook for Bool and Result (see #252)
+  - Include `CHANGES.markdown` in package tarball
+
+Internal changes:
+
+  - Rename `Fail` constructor of `Result`-type to `Failure`
+  - Add `FailureReason` type
+  - Add `Test.Hspec.Core.Spec.safeEvaluateExample`
+
+## Changes in 2.3.2
+  - Compatibility with HUnit 1.5
+
+## Changes in 2.3.1
+  - Fix build for HUnit < 1.4
+
+## Changes in 2.3.0
+  - Proper support for GHC 8 call stacks
+
+## Changes in 2.2.4
+  - Compatibility with QuickCheck-2.9
+
+## Changes in 2.2.3
+  - Make sure that `cabal haddock` works for `hspec-discover`
+  - Forward compatibility for HUnit 1.4
+  - Fix tests for GHC 8.0.1-rc1
+
+## Changes in 2.2.2
+  - Fix compilation for GHC 8.0.1-rc1
+
+## Changes in 2.2.1
+  - Make sure that Vim's default `errorformat` recognizes exact locations
+  - GHCJS compatibility
+
+## Changes in 2.2.0
+  - Add source locations to test `Result`
+
+## Changes in 2.1.10
+  - GHC 7.0.* compatibility
+
+## Changes in 2.1.9
+  - Make use of GHC 7.10.2 source locations
+  - Add `--jobs`
+
+## Changes in 2.1.8
+  - Depend on `hspec-expectations-0.7.0"
+
+## Changes in 2.1.7
+  - Add `beforeAll_`
+
+## Changes in 2.1.6
+  - If there were no previous failures, run all spec items on `--rerun`
+
+## Changes in 2.1.5
+  - Compatibility with QuickCheck-2.8
+
+## Changes in 2.1.4
+  - Make `hspec-discover` ignore modules with invalid module names, this fixes
+    issues with `flycheck`'s temporary files
+
+## Changes in 2.1.3
+  - Format source locations like gcc does
+
+## Changes in 2.1.2
+  - Re-export `before_` from `Test.Hspec`
+
+## Changes in 2.1.1
+  - Add `before_`
+  - Add command-line option `--skip`
+
+## Changes in 2.1.0
+  - Generalize `after_`, `afterAll_` and `around_`
+
+## Changes in 2.0.2
+  - Indent error messages for failed examples (fixes #186)
+  - Export `defaultParams` from `Test.Hspec.Core.Example`
+  - Bring back `Test.Hspec.HUnit` and deprecate it to provide a smother upgrade
+    path
+
+## Changes in 2.0.1
+  - Add missing extra source files to for `hspec-discover`
+
+## Changes in 2.0.0
+  - Split package into `hspec`, `hspec-core` and `hspec-discover`
+  - Allow hooks (`before`, `around`, etc.) to pass arguments to spec items
+  - Do not print `-` in front of spec items with `specdoc` formatter
+  - Move `Test.Hspec.HUnit` to `hspec-contrib`
+
+## Changes in 1.12.4
+  - Add `specGroup` and `specItem` to `Test.Hspec.Core`
+  - Deprecate `Test.Hspec.Core.it` and `Test.Hspec.Core.describe`
+
+## Changes in 1.12.3
+  - Make `hspec-discover` work with `-XNoImplicitPrelude`
+
+## Changes in 1.12.2
+  - Include `IOErrorType` when printing uncaught `IOException`s (see #204)
+
+## Changes in 1.12.1
+  - Add `--module-name` option to `hspec-discover` (see #168)
+
+## Changes in 1.12.0
+  - Add optional source location to spec items
+  - Move item requirement text from `SpecItem` constructor to `Item` data type
+  - Remove `BuildSpecs` constructor from `SpecTree`, having `runIO` this is not
+    really needed
+  - Add `mapSpecTree` to `Test.Hspec.Core`
+  - Add `afterAll` (see #188)
+  - Do not return `Result` from `hspecWith` and accept command-line options
+    (the old behavior is still available as `hspecWithResult`)
+  - Rename `configHandle` to `configOutputFile`
+  - Omit empty `describe` groups from report
+  - Do not pass position to `Formatter.exampleGroupStarted` (we can not support
+    this with the upcomming cleanup actions #188)
+  - Do not print empty lines before/after spec groups
+  - Deprecate `Test.Hspec.Formatters.newParagraph`
+
+## Changes in 1.11.4
+  - Make test suite independent from QuickCheck seed (see #187)
+
+## Changes in 1.11.3
+  - Depend on `hspec-expectations-0.6.1`
+
+## Changes in 1.11.2
+  - Add `beforeAll`
+
+## Changes in 1.11.1
+ - Add `specify` as an alias for `it`
+
+## Changes in 1.11.0
+ - Add `BuildSpecs` constructor to internal `SpecTree` data structure.  This
+   allows you to do `IO` while constructing the spec tree.
+ - Add `runIO`
+
+## Changes in 1.10.0
+ - Do not use exception type to distinguish between synchronous/asynchronous
+   exceptions (thanks to Michael Snoyman)
+ - Remove `Example` instance for `Test.HUnit.Test` (#101), use
+   `Test.Hspec.HUnit.fromHUnitTest` instead.
+ - Do not reexport `property` from `Test.Hspec.QuickCheck`
+ - Move `ProgressCallback` out of `Params`
+ - Add show instance for `Params`
+ - Move requirement text from `Item` to `SpecItem` constructor
+ - Remove deprecated modules and functions
+
+## Changes in 1.9.5
+ - Make sure that ctrl-c works properly with GHC 7.8.* and QuickCheck-2.6
+
+## Changes in 1.9.4
+ - Compatibility with transformers-0.4.0.0
+
+## Changes in 1.9.3
+ - Make internal Tree data structure more strict (#169)
+
+## Changes in 1.9.2
+ - Print type of exceptions that are raised from QuickCheck properties (#94)
+
+## Changes in 1.9.1
+ - Bring back compatibility with older versions of QuickCheck (#166)
+
+## Changes in 1.9.0
+ - Depend on QuickCheck 2.7
+
+## Changes in 1.8.3
+ - Do not use color if `TERM=dumb` (see #158)
+
+## Changes in 1.8.2
+ - `hspec-discover` now accepts `--no-main` which results in a top-level spec
+   being generated instead of a test driver (thanks @DanielG)
+ - Make sure that `after` is run on failing tests (see #159)
+
+## Changes in 1.8.1
+ - Add `shouldMatchList`
+
+## Changes in 1.8.0
+ - Run `before`/`after`/`around` for each single check of a QuickCheck property
+ - Add `Test.Hspec.Core.mapSpecItem`
+ - Add `modifyMaxSuccess`, `modifyMaxDiscardRatio` and `modifyMaxSize` to
+   `Test.Hspec.QuickCheck`
+ - Don't fail if callback is not called in `around`
+ - `hspec-discover`: Remove `--nested` option
+ - `hspec-discover`: Ignore `Spec.hs`/`Spec.lhs`
+
+## Changes in 1.7.2
+ - Add `after` and `around`
+
+## Changes in 1.7.1
+ - Add `shouldContain` (thanks to Alfredo Di Napoli)
+ - When printing progress, skip total if it is 0
+ - Do not colorize the description/requirement in failure list
+
+## Changes in 1.7.0
+ - Add `--depth` for use with `hspec-smallcheck`
+
+## Change in 1.6.2
+ - Add `before`
+ - Add `--qc-max-discard` and `--qc-max-size`
+
+## Changes in 1.6.1
+ - Allow to specify the output file with `--out`
+
+## Changes in 1.6.0
+ - Add support for parallelization
+ - Change license to MIT
+ - Add MonadIO instance for FormatM
+ - Add support for custom formatters to hspec-discover
+ - Add hspecResult
+ - Rename `--re-run` to `--rerun` + add documentation (#95)
+ - Remove `configVerbose`
+ - Use same `--qc-max-success` on `--rerun` (#125)
+ - Add command-line option `--no-color`, `--color` does not accept arguments
+   anymore (#130)
+
+## Changes in 1.5.4
+ - Make sure that QuickCheck is never chatty
+ - Make sure progress for QuickCheck examples is shown
+
+## Changes in 1.5.3
+ - Print "Randomized with seed ..." only once
+
+## Changes in 1.5.2
+ - Add `--seed`, it can be used to specify the seed for QuickCheck properties
+ - Reuse previous seed on `--re-run`
+
+## Changes in 1.5.1
+ - Depend on quickcheck-io
+
+## Changes in 1.5.0
+ - Allow to use expectations as QuickCheck properties (#80)
+ - Do not suppress output to `stdout`
+ - Change type of `pending` to `Expectation`, add `pendingWith` (#121)
+ - Add the `example` function, it fixes the type of an Expectation (#86)
+ - Rename `--fast-fail` to `--fail-fast` (for consistency with RSpec)
+ - Do not clutter Cabal test suite logs with QuickCheck output (#112)
+ - Skip redundant output from QuickCheck failure messages (#102)
+
+## Changes in 1.4.5
+ - hspec-discover now discovers .lhs files, too
+
+## Changes in 1.4.4
+ - Visually distinguish error message from requirements in the summary (#100)
+ - Export `formatException` from `Test.Hspec.Formatters`
+ - Add `--fast-fail` (#82)
+ - Print a summary on UserInterrupt/ctrl-c (#107)
+
+## Changes in 1.4.3
+ - Add `--dry-run` (#111)
+
+## Changes in 1.4.2
+ - Properly handle ctrl-c while running QuickCheck properties (#93)
+ - Default to `--color=always` when `--color` is used without argument (#106)
+ - treat `--qc-max-success` as an alias for `--maximum-generated-tests`
+
+## Changes in 1.4.1
+ - Used CPU time is now only included in the test summary if run with
+   `--print-cpu-time`.
+
+## Changes in 1.4.0
+ - We now have a manual at http://hspec.github.io/.  The sources are in
+   `doc/`.  It's still work in progress.  Contributions are very welcome!
+ - The Haddock documentation now indicates the stability of each exposed
+   module.  `Test.Hspec` is now considered stable.
+ - `hspec` now supports command-line options
+   - `--color` can be used to enable/disable colored output
+   - `--format` can be used to pick a specific formatter
+   - `--html` can be used to produce an HTML report
+   - `--maximum-generated-tests` can be used to specify QuickCheck's
+     `maxSuccess`
+   - `--match` only runs spec items that match a given string
+   - `--re-run` only runs spec items that previously failed.  This is
+     undocumented,
+     experimental and only works within GHCi (use `:reload` / `:main`)!
+ - Runner functions exported from `Test.Hspec.Runner` new expect a monadic
+   spec.
+ - `fromHUnitTest` has been added.  It can be used to run existing HUnit test
+   suites with Hspec.
+ - The `Example` instance for HUnit `Test`s has been deprecated.
+   `fromHUnitTest` can be used instead.  The primary motivation is, that
+   `fromHUnitTest` gives more detailed reporting for nested HUnit test suites.
+ - `Test.Hspec.Monadic` has been deprecated, all functionality is available
+   through one of `Test.Hspec`, `Test.Hspec.Core` or `Test.Hspec.Runner`.
+ - More of Hspec's internals are now exposed from `Test.Hspec.Core`
+ - All runner functions for the core spec type (aka as non-monadic spec) have
+   been deprecated, use e.g. `Test.Hspec.hspec . fromSpecList` instead.  The
+   motivation is to provide an API that does not expose colliding names.
+ - Some other stuff from `Test.Hspec.Core` that collides with other parts of
+   the API has been deprecated.  Compatible alternatives are given in each
+   deprecation message.
+ - The default formatter now produces less whitespace (#73) + other minor
+   improvements
+ - The formatter API has been revamped.
+ - The exception type is now printed if an example fails due to an exception
+   (#50)
+ - The number of pending examples is now printed after each test run (#85)
+ - `--verbose` has been added (#87)
+
+## Changes in 1.3.0
+
+ - `Test.Hspec` now re-exports the monadic API.  If you still use the
+   non-monadic API, you can use `Test.Hspec.Core` as a drop-in replacement.
+
+ - `hspec-expectations`, a set of combinators that allow to express
+   expectations about the outcome of code examples, is now included with Hspec.
+   Have a look at the [README of `hspec-expectations`]
+   (https://github.com/sol/hspec-expectations#readme)
+   for a short introduction.
+
+ - `hspec-discover`, a mechanism for automatic spec discovery, is now an
+   official part of Hspec.
+   Have a look at [`hspec-discover`'s README]
+   (https://github.com/hspec/hspec/tree/master/hspec-discover#readme)
+   for a short introduction.
+
+## Changes in 1.2.0
+
+ - `hspec` has been removed, and `hspecX` has been renamed to `hspec` (see
+   [#71](https://github.com/hspec/hspec/issues/71))
+
+ - `hHspec` now returns a summary of the test run.
+
+ - The time reporting after a test run is not colored anymore.
+
+## Changes in 1.1.3
+
+ - `Test.Hspec` warns now about future changes: It will re-export
+   `Test.Hspec.Monadic` in the future.  If you still use the non-monadic API,
+   you can either use `Test.Hspec.Core` as a drop-in replacement, or migrate your
+   code to the monadic API.
+
+   The monadic API is more stable and easier to use.  Now is a good time to
+   switch!
+
+ - `Test.Hspec.Core` is now a proper superset of `Test.Hspec`
+
+## Changes in 1.1.2
+
+ * All descriptions of nested examples are now included in summary for failing
+   examples
+
+ * `context` is now an alias for `describe`
+
+## Changes in 1.1.1
+
+ * Specs from Test.Hspec.Monadic has been renamed to Spec.  For backward
+   compatibility Specs is still kept as an alias.
+
+## Changes in 1.1.0
+
+### The reason for pending examples is now optional
+
+With this change, both of the following code snippets work.
+
+```haskell
+it "some behavior" $
+  pending  -- no reason given
+```
+
+```haskell
+it "some other behavior" $
+  pending "some reason"
+```
+
+### Hspec does not rely on ExistentialQuantification anymore
+
+### The type used to represent specs is now abstract
+
+This should give more useful error messages when adapting old specs that use
+the non-monadic API for `hspec-1.0`/`hspec-1.1`.
+
+### Several internal types and functions have been deprecated
+
+Those are internal functions, and they will be removed/hidden with the next
+release.  If you use any of those, update your code.  If you really need them,
+[open a ticket](https://github.com/hspec/hspec/issues) and describe your use
+case.
+
+## Changes in 1.0.0
+
+### Hspec now re-uses QuickCheck's property function
+
+`Test.Hspec.QuickCheck.property` is now simply a re-exports of
+`Test.QuickCheck.property`.  This has the advantage that you do not get a name
+collision if you import both, `Test.Hspec.QuickCheck` and `Test.QuickCheck`.
+
+### Better support for nested specs
+
+*NOTE: This is a breaking change to the non-monadic API.  The monadic API is
+not affected.*
+
+In some situations parent descriptions for nested specs were not included in
+the generated report.  Solving this required a change to the data structure
+that is used to represent specs (it was not a proper tree, now it is).
+
+#### Updating specs that use the non-monadic API
+
+The runner functions (`hspec`, `hspecB` and `hspecX`) now take a list of
+descriptions.
+
+The following works with `hspec-0.9`, but not with `hspec-1.0`.
+
+```haskell
+main = hspecX $
+  describe "reverse" [
+    it "reverses a list" $
+      reverse [1, 2, 3] == [3, 2, 1],
+
+    it "gives the original list, if applied twice" $ property $
+      \xs -> reverse (reverse xs) == (xs :: [Int])
+  ]
+
+```
+
+For `hspec-1.0`, you need to wrap it into a list.
+
+```haskell
+main = hspecX [
+    describe "reverse" [
+      it "reverses a list" $
+        reverse [1, 2, 3] == [3, 2, 1],
+
+      it "gives the original list, if applied twice" $ property $
+        \xs -> reverse (reverse xs) == (xs :: [Int])
+    ]
+  ]
+```
+
+Specs consisting of several *desribes*, combined with `descriptions`, continue
+to work unchanged.  But `descriptions` is now a noop, and it will be removed in
+a future release.  So it is a good idea to drop it.
+
+The following works with both `hspec-0.9` and `hspec-1.0`.
+
+```haskell
+main = hspecX $ descriptions [  -- descriptions is redundant
+    describe "Foo" [
+      it "has some behavior" True
+    ]
+  , describe "Bar" [
+      it "has some behavior" True
+    ]
+  ]
+```
+
+But the following is recommended instead.
+
+```haskell
+main = hspecX [
+    describe "Foo" [
+      it "has some behavior" True
+    ]
+  , describe "Bar" [
+      it "has some behavior" True
+    ]
+  ]
+```
+
+### A new monadic API for custom Formatters
+
+For all the details, have a look at the [docs]
+(http://hackage.haskell.org/packages/archive/hspec/latest/doc/html/Test-Hspec-Formatters.html).
+
+
+### The total time required to run a spec is now included in the summary
+
+In addition to the used CPU time, the total time required to run a spec is now
+include in the summary.  This is useful for specs that do non-CPU-intensive
+stuff, or fork subprocesses.
diff --git a/LICENSE b/LICENSE
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2011-2015 Simon Hengel <sol@typeful.net>
+Copyright (c) 2011-2017 Simon Hengel <sol@typeful.net>
 Copyright (c) 2011-2012 Trystan Spangler <trystan.s@comcast.net>
 Copyright (c) 2011-2011 Greg Weber <greg@gregweber.info>
 
diff --git a/changelog b/changelog
deleted file mode 100644
--- a/changelog
+++ /dev/null
@@ -1,1 +0,0 @@
-See https://github.com/hspec/hspec/blob/master/CHANGES.markdown
diff --git a/hspec-core/src/Test/Hspec/Compat.hs b/hspec-core/src/Test/Hspec/Compat.hs
deleted file mode 100644
--- a/hspec-core/src/Test/Hspec/Compat.hs
+++ /dev/null
@@ -1,106 +0,0 @@
-{-# LANGUAGE CPP #-}
-module Test.Hspec.Compat (
-  getDefaultConcurrentJobs
-, showType
-, showFullType
-, readMaybe
-, lookupEnv
-, module Data.IORef
-
-, module Prelude
-, module Control.Applicative
-, module Data.Foldable
-, module Data.Traversable
-, module Data.Monoid
-
-#if !MIN_VERSION_base(4,6,0)
-, modifyIORef'
-#endif
-) where
-
-import           Control.Applicative
-import           Data.Foldable
-import           Data.Traversable
-import           Data.Monoid
-
-import           Prelude hiding (
-    all
-  , and
-  , any
-  , concat
-  , concatMap
-  , elem
-  , foldl
-  , foldl1
-  , foldr
-  , foldr1
-  , mapM
-  , mapM_
-  , maximum
-  , minimum
-  , notElem
-  , or
-  , product
-  , sequence
-  , sequence_
-  , sum
-  )
-
-import           Data.Typeable (Typeable, typeOf, typeRepTyCon)
-import           Text.Read
-import           Data.IORef
-import           System.Environment
-
-import           Data.Typeable.Internal (tyConModule, tyConName)
-import           Control.Concurrent
-
-#if !MIN_VERSION_base(4,6,0)
-import qualified Text.ParserCombinators.ReadP as P
-
--- |Strict version of 'modifyIORef'
-modifyIORef' :: IORef a -> (a -> a) -> IO ()
-modifyIORef' ref f = do
-    x <- readIORef ref
-    let x' = f x
-    x' `seq` writeIORef ref x'
-
--- | Parse a string using the 'Read' instance.
--- Succeeds if there is exactly one valid result.
--- A 'Left' value indicates a parse error.
-readEither :: Read a => String -> Either String a
-readEither s =
-  case [ x | (x,"") <- readPrec_to_S read' minPrec s ] of
-    [x] -> Right x
-    []  -> Left "Prelude.read: no parse"
-    _   -> Left "Prelude.read: ambiguous parse"
- where
-  read' =
-    do x <- readPrec
-       lift P.skipSpaces
-       return x
-
--- | Parse a string using the 'Read' instance.
--- Succeeds if there is exactly one valid result.
-readMaybe :: Read a => String -> Maybe a
-readMaybe s = case readEither s of
-                Left _  -> Nothing
-                Right a -> Just a
-
--- | Return the value of the environment variable @var@, or @Nothing@ if
--- there is no such value.
---
--- For POSIX users, this is equivalent to 'System.Posix.Env.getEnv'.
-lookupEnv :: String -> IO (Maybe String)
-lookupEnv k = lookup k `fmap` getEnvironment
-#endif
-
-showType :: Typeable a => a -> String
-showType a = let t = typeRepTyCon (typeOf a) in
-  show t
-
-showFullType :: Typeable a => a -> String
-showFullType a = let t = typeRepTyCon (typeOf a) in
-  tyConModule t ++ "." ++ tyConName t
-
-getDefaultConcurrentJobs :: IO Int
-getDefaultConcurrentJobs = getNumCapabilities
diff --git a/hspec-core/src/Test/Hspec/Config.hs b/hspec-core/src/Test/Hspec/Config.hs
deleted file mode 100644
--- a/hspec-core/src/Test/Hspec/Config.hs
+++ /dev/null
@@ -1,86 +0,0 @@
-module Test.Hspec.Config (
-  Config (..)
-, ColorMode(..)
-, defaultConfig
-, getConfig
-, configAddFilter
-, configQuickCheckArgs
-) where
-
-import           Control.Applicative
-import           System.IO
-import           System.Exit
-import qualified Test.QuickCheck as QC
-
-import           Test.Hspec.Core.Util
-import           Test.Hspec.Options
-import           Test.Hspec.FailureReport
-import           Test.Hspec.Core.QuickCheckUtil (mkGen)
-import           Test.Hspec.Core.Example (Params(..), defaultParams)
-
--- | Add a filter predicate to config.  If there is already a filter predicate,
--- then combine them with `||`.
-configAddFilter :: (Path -> Bool) -> Config -> Config
-configAddFilter p1 c = c {
-    configFilterPredicate = Just p1 `filterOr` configFilterPredicate c
-  }
-
-mkConfig :: Maybe FailureReport -> Config -> Config
-mkConfig mFailureReport opts = opts {
-    configFilterPredicate = matchFilter `filterOr` rerunFilter
-  , configQuickCheckSeed = mSeed
-  , configQuickCheckMaxSuccess = mMaxSuccess
-  , configQuickCheckMaxDiscardRatio = mMaxDiscardRatio
-  , configQuickCheckMaxSize = mMaxSize
-  }
-  where
-
-    mSeed = configQuickCheckSeed opts <|> (failureReportSeed <$> mFailureReport)
-    mMaxSuccess = configQuickCheckMaxSuccess opts <|> (failureReportMaxSuccess <$> mFailureReport)
-    mMaxSize = configQuickCheckMaxSize opts <|> (failureReportMaxSize <$> mFailureReport)
-    mMaxDiscardRatio = configQuickCheckMaxDiscardRatio opts <|> (failureReportMaxDiscardRatio <$> mFailureReport)
-
-    matchFilter = configFilterPredicate opts
-
-    rerunFilter = case failureReportPaths <$> mFailureReport of
-      Just [] -> Nothing
-      Just xs -> Just (`elem` xs)
-      Nothing -> Nothing
-
-configQuickCheckArgs :: Config -> QC.Args
-configQuickCheckArgs c = qcArgs
-  where
-    qcArgs = (
-        maybe id setSeed (configQuickCheckSeed c)
-      . maybe id setMaxDiscardRatio (configQuickCheckMaxDiscardRatio c)
-      . maybe id setMaxSize (configQuickCheckMaxSize c)
-      . maybe id setMaxSuccess (configQuickCheckMaxSuccess c)) (paramsQuickCheckArgs defaultParams)
-
-    setMaxSuccess :: Int -> QC.Args -> QC.Args
-    setMaxSuccess n args = args {QC.maxSuccess = n}
-
-    setMaxSize :: Int -> QC.Args -> QC.Args
-    setMaxSize n args = args {QC.maxSize = n}
-
-    setMaxDiscardRatio :: Int -> QC.Args -> QC.Args
-    setMaxDiscardRatio n args = args {QC.maxDiscardRatio = n}
-
-    setSeed :: Integer -> QC.Args -> QC.Args
-    setSeed n args = args {QC.replay = Just (mkGen (fromIntegral n), 0)}
-
-getConfig :: Config -> String -> [String] -> IO Config
-getConfig opts_ prog args = do
-  case parseOptions opts_ prog args of
-    Left (err, msg) -> exitWithMessage err msg
-    Right opts -> do
-      r <- if configRerun opts then readFailureReport else return Nothing
-      return (mkConfig r opts)
-
-exitWithMessage :: ExitCode -> String -> IO a
-exitWithMessage err msg = do
-  hPutStr h msg
-  exitWith err
-  where
-    h = case err of
-      ExitSuccess -> stdout
-      _           -> stderr
diff --git a/hspec-core/src/Test/Hspec/Core/Compat.hs b/hspec-core/src/Test/Hspec/Core/Compat.hs
new file mode 100644
--- /dev/null
+++ b/hspec-core/src/Test/Hspec/Core/Compat.hs
@@ -0,0 +1,106 @@
+{-# LANGUAGE CPP #-}
+module Test.Hspec.Core.Compat (
+  getDefaultConcurrentJobs
+, showType
+, showFullType
+, readMaybe
+, lookupEnv
+, module Data.IORef
+
+, module Prelude
+, module Control.Applicative
+, module Data.Foldable
+, module Data.Traversable
+, module Data.Monoid
+
+#if !MIN_VERSION_base(4,6,0)
+, modifyIORef'
+#endif
+) where
+
+import           Control.Applicative
+import           Data.Foldable
+import           Data.Traversable
+import           Data.Monoid
+
+import           Prelude hiding (
+    all
+  , and
+  , any
+  , concat
+  , concatMap
+  , elem
+  , foldl
+  , foldl1
+  , foldr
+  , foldr1
+  , mapM
+  , mapM_
+  , maximum
+  , minimum
+  , notElem
+  , or
+  , product
+  , sequence
+  , sequence_
+  , sum
+  )
+
+import           Data.Typeable (Typeable, typeOf, typeRepTyCon)
+import           Text.Read
+import           Data.IORef
+import           System.Environment
+
+import           Data.Typeable (tyConModule, tyConName)
+import           Control.Concurrent
+
+#if !MIN_VERSION_base(4,6,0)
+import qualified Text.ParserCombinators.ReadP as P
+
+-- |Strict version of 'modifyIORef'
+modifyIORef' :: IORef a -> (a -> a) -> IO ()
+modifyIORef' ref f = do
+    x <- readIORef ref
+    let x' = f x
+    x' `seq` writeIORef ref x'
+
+-- | Parse a string using the 'Read' instance.
+-- Succeeds if there is exactly one valid result.
+-- A 'Left' value indicates a parse error.
+readEither :: Read a => String -> Either String a
+readEither s =
+  case [ x | (x,"") <- readPrec_to_S read' minPrec s ] of
+    [x] -> Right x
+    []  -> Left "Prelude.read: no parse"
+    _   -> Left "Prelude.read: ambiguous parse"
+ where
+  read' =
+    do x <- readPrec
+       lift P.skipSpaces
+       return x
+
+-- | Parse a string using the 'Read' instance.
+-- Succeeds if there is exactly one valid result.
+readMaybe :: Read a => String -> Maybe a
+readMaybe s = case readEither s of
+                Left _  -> Nothing
+                Right a -> Just a
+
+-- | Return the value of the environment variable @var@, or @Nothing@ if
+-- there is no such value.
+--
+-- For POSIX users, this is equivalent to 'System.Posix.Env.getEnv'.
+lookupEnv :: String -> IO (Maybe String)
+lookupEnv k = lookup k `fmap` getEnvironment
+#endif
+
+showType :: Typeable a => a -> String
+showType a = let t = typeRepTyCon (typeOf a) in
+  show t
+
+showFullType :: Typeable a => a -> String
+showFullType a = let t = typeRepTyCon (typeOf a) in
+  tyConModule t ++ "." ++ tyConName t
+
+getDefaultConcurrentJobs :: IO Int
+getDefaultConcurrentJobs = getNumCapabilities
diff --git a/hspec-core/src/Test/Hspec/Core/Config.hs b/hspec-core/src/Test/Hspec/Core/Config.hs
new file mode 100644
--- /dev/null
+++ b/hspec-core/src/Test/Hspec/Core/Config.hs
@@ -0,0 +1,129 @@
+{-# LANGUAGE CPP #-}
+module Test.Hspec.Core.Config (
+  Config (..)
+, ColorMode(..)
+, defaultConfig
+, getConfig
+, configAddFilter
+, configQuickCheckArgs
+#ifdef TEST
+, readConfigFiles
+#endif
+) where
+
+import           Prelude ()
+
+import           Control.Exception
+import           Control.Monad
+import           Data.Maybe
+import           System.IO
+import           System.IO.Error
+import           System.Exit
+import           System.FilePath
+import           System.Directory
+import qualified Test.QuickCheck as QC
+
+import           Test.Hspec.Core.Util
+import           Test.Hspec.Core.Compat
+import           Test.Hspec.Core.Options
+import           Test.Hspec.Core.FailureReport
+import           Test.Hspec.Core.QuickCheckUtil (mkGen)
+import           Test.Hspec.Core.Example (Params(..), defaultParams)
+
+-- | Add a filter predicate to config.  If there is already a filter predicate,
+-- then combine them with `||`.
+configAddFilter :: (Path -> Bool) -> Config -> Config
+configAddFilter p1 c = c {
+    configFilterPredicate = Just p1 `filterOr` configFilterPredicate c
+  }
+
+mkConfig :: Maybe FailureReport -> Config -> Config
+mkConfig mFailureReport opts = opts {
+    configFilterPredicate = matchFilter `filterOr` rerunFilter
+  , configQuickCheckSeed = mSeed
+  , configQuickCheckMaxSuccess = mMaxSuccess
+  , configQuickCheckMaxDiscardRatio = mMaxDiscardRatio
+  , configQuickCheckMaxSize = mMaxSize
+  }
+  where
+
+    mSeed = configQuickCheckSeed opts <|> (failureReportSeed <$> mFailureReport)
+    mMaxSuccess = configQuickCheckMaxSuccess opts <|> (failureReportMaxSuccess <$> mFailureReport)
+    mMaxSize = configQuickCheckMaxSize opts <|> (failureReportMaxSize <$> mFailureReport)
+    mMaxDiscardRatio = configQuickCheckMaxDiscardRatio opts <|> (failureReportMaxDiscardRatio <$> mFailureReport)
+
+    matchFilter = configFilterPredicate opts
+
+    rerunFilter = case failureReportPaths <$> mFailureReport of
+      Just [] -> Nothing
+      Just xs -> Just (`elem` xs)
+      Nothing -> Nothing
+
+configQuickCheckArgs :: Config -> QC.Args
+configQuickCheckArgs c = qcArgs
+  where
+    qcArgs = (
+        maybe id setSeed (configQuickCheckSeed c)
+      . maybe id setMaxDiscardRatio (configQuickCheckMaxDiscardRatio c)
+      . maybe id setMaxSize (configQuickCheckMaxSize c)
+      . maybe id setMaxSuccess (configQuickCheckMaxSuccess c)) (paramsQuickCheckArgs defaultParams)
+
+    setMaxSuccess :: Int -> QC.Args -> QC.Args
+    setMaxSuccess n args = args {QC.maxSuccess = n}
+
+    setMaxSize :: Int -> QC.Args -> QC.Args
+    setMaxSize n args = args {QC.maxSize = n}
+
+    setMaxDiscardRatio :: Int -> QC.Args -> QC.Args
+    setMaxDiscardRatio n args = args {QC.maxDiscardRatio = n}
+
+    setSeed :: Integer -> QC.Args -> QC.Args
+    setSeed n args = args {QC.replay = Just (mkGen (fromIntegral n), 0)}
+
+getConfig :: Config -> String -> [String] -> IO (Maybe FailureReport, Config)
+getConfig opts_ prog args = do
+  configFiles <- do
+    ignore <- ignoreConfigFile opts_ args
+    case ignore of
+      True -> return []
+      False -> readConfigFiles
+  envVar <- fmap words <$> lookupEnv envVarName
+  case parseOptions opts_ prog configFiles envVar args of
+    Left (err, msg) -> exitWithMessage err msg
+    Right opts -> do
+      r <- if configRerun opts then readFailureReport opts else return Nothing
+      return (r, mkConfig r opts)
+
+readConfigFiles :: IO [ConfigFile]
+readConfigFiles = do
+  global <- readGlobalConfigFile
+  local <- readLocalConfigFile
+  return $ catMaybes [global, local]
+
+readGlobalConfigFile :: IO (Maybe ConfigFile)
+readGlobalConfigFile = do
+  mHome <- tryJust (guard . isDoesNotExistError) getHomeDirectory
+  case mHome of
+    Left _ -> return Nothing
+    Right home -> readConfigFile (home </> ".hspec")
+
+readLocalConfigFile :: IO (Maybe ConfigFile)
+readLocalConfigFile = do
+  mName <- tryJust (guard . isDoesNotExistError) (canonicalizePath ".hspec")
+  case mName of
+    Left _ -> return Nothing
+    Right name -> readConfigFile name
+
+readConfigFile :: FilePath -> IO (Maybe ConfigFile)
+readConfigFile name = do
+  exists <- doesFileExist name
+  if exists then Just . (,) name . words <$> readFile name else return Nothing
+
+exitWithMessage :: ExitCode -> String -> IO a
+exitWithMessage err msg = do
+  hPutStr h msg
+  exitWith err
+  where
+    h = case err of
+      ExitSuccess -> stdout
+      _           -> stderr
diff --git a/hspec-core/src/Test/Hspec/Core/Example.hs b/hspec-core/src/Test/Hspec/Core/Example.hs
--- a/hspec-core/src/Test/Hspec/Core/Example.hs
+++ b/hspec-core/src/Test/Hspec/Core/Example.hs
@@ -9,6 +9,8 @@
 , Result (..)
 , Location (..)
 , LocationAccuracy (..)
+, FailureReason (..)
+, safeEvaluateExample
 ) where
 
 import           Data.Maybe (fromMaybe)
@@ -20,6 +22,7 @@
 #endif
 
 import qualified Control.Exception as E
+import           Control.DeepSeq
 import           Data.Typeable (Typeable)
 import qualified Test.QuickCheck as QC
 import           Test.Hspec.Expectations (Expectation)
@@ -29,14 +32,12 @@
 
 import           Test.Hspec.Core.QuickCheckUtil
 import           Test.Hspec.Core.Util
-import           Test.Hspec.Compat
+import           Test.Hspec.Core.Compat
 
 -- | A type class for examples
 class Example e where
   type Arg e
-#if __GLASGOW_HASKELL__ >= 704
   type Arg e = ()
-#endif
   evaluateExample :: e -> Params -> (ActionWith (Arg e) -> IO ()) -> ProgressCallback -> IO Result
 
 data Params = Params {
@@ -57,9 +58,18 @@
 type ActionWith a = a -> IO ()
 
 -- | The result of running an example
-data Result = Success | Pending (Maybe String) | Fail (Maybe Location) String
+data Result = Success | Pending (Maybe String) | Failure (Maybe Location) FailureReason
   deriving (Eq, Show, Read, Typeable)
 
+data FailureReason = NoReason | Reason String | ExpectedButGot (Maybe String) String String
+    deriving (Eq, Show, Read, Typeable)
+
+instance NFData FailureReason where
+  rnf reason = case reason of
+    NoReason -> ()
+    Reason r -> r `deepseq` ()
+    ExpectedButGot p e a  -> p `deepseq` e `deepseq` a `deepseq` ()
+
 instance E.Exception Result
 
 -- | @Location@ is used to represent source locations.
@@ -79,10 +89,46 @@
   BestEffort
   deriving (Eq, Show, Read)
 
+safeEvaluateExample :: Example e => e -> Params -> (ActionWith (Arg e) -> IO ()) -> ProgressCallback -> IO (Either E.SomeException Result)
+safeEvaluateExample example params around progress = do
+  r <- safeTry $ forceResult <$> evaluateExample example params around progress
+  return $ case r of
+    Left e | Just result <- E.fromException e -> Right result
+    Left e | Just hunit <- E.fromException e -> Right (hunitFailureToResult hunit)
+    _ -> r
+  where
+    forceResult :: Result -> Result
+    forceResult r = case r of
+      Success -> r
+      Pending m -> m `deepseq` r
+      Failure _ m -> m `deepseq` r
+
+instance Example Result where
+  type Arg Result = ()
+  evaluateExample e = evaluateExample (\() -> e)
+
+instance Example (a -> Result) where
+  type Arg (a -> Result) = a
+  evaluateExample example _params action _callback = do
+    ref <- newIORef Success
+    action (writeIORef ref . example)
+    readIORef ref
+
 instance Example Bool where
   type Arg Bool = ()
-  evaluateExample b _ _ _ = if b then return Success else return (Fail Nothing "")
+  evaluateExample e = evaluateExample (\() -> e)
 
+instance Example (a -> Bool) where
+  type Arg (a -> Bool) = a
+  evaluateExample p _params action _callback = do
+    ref <- newIORef Success
+    action $ \a -> example a >>= writeIORef ref
+    readIORef ref
+    where
+      example a
+        | p a = return Success
+        | otherwise = return (Failure Nothing NoReason)
+
 instance Example Expectation where
   type Arg Expectation = ()
   evaluateExample e = evaluateExample (\() -> e)
@@ -92,9 +138,11 @@
 #if MIN_VERSION_HUnit(1,3,0)
   HUnit.HUnitFailure mLoc err ->
 #if MIN_VERSION_HUnit(1,5,0)
-      Fail location (HUnit.formatFailureReason err)
+      case err of
+        HUnit.Reason reason -> Failure location (Reason reason)
+        HUnit.ExpectedButGot preface expected actual -> Failure location (ExpectedButGot preface expected actual)
 #else
-      Fail location err
+      Failure location (Reason err)
 #endif
     where
       location = case mLoc of
@@ -105,19 +153,12 @@
         Just loc -> Just $ Location (HUnit.locationFile loc) (HUnit.locationLine loc) (HUnit.locationColumn loc) ExactLocation
 #endif
 #else
-  HUnit.HUnitFailure err -> Fail Nothing err
+  HUnit.HUnitFailure err -> Failure Nothing (Reason err)
 #endif
 
 instance Example (a -> Expectation) where
   type Arg (a -> Expectation) = a
-  evaluateExample e _ action _ = (action e >> return Success) `E.catches` [
-      E.Handler (return . hunitFailureToResult)
-    , E.Handler (return :: Result -> IO Result)
-    ]
-
-instance Example Result where
-  type Arg Result = ()
-  evaluateExample r _ _ _ = return r
+  evaluateExample e _ action _ = action e >> return Success
 
 instance Example QC.Property where
   type Arg QC.Property = ()
@@ -130,11 +171,11 @@
     return $
       case r of
         QC.Success {}               -> Success
-        QC.Failure {QC.output = m}  -> fromMaybe (Fail Nothing $ sanitizeFailureMessage r) (parsePending m)
-        QC.GaveUp {QC.numTests = n} -> Fail Nothing ("Gave up after " ++ pluralize n "test" )
-        QC.NoExpectedFailure {}     -> Fail Nothing ("No expected failure")
+        QC.Failure {QC.output = m}  -> fromMaybe (Failure Nothing . Reason $ sanitizeFailureMessage r) (parsePending m)
+        QC.GaveUp {QC.numTests = n} -> Failure Nothing (Reason $ "Gave up after " ++ pluralize n "test" )
+        QC.NoExpectedFailure {}     -> Failure Nothing (Reason $ "No expected failure")
 #if MIN_VERSION_QuickCheck(2,8,0)
-        QC.InsufficientCoverage {}  -> Fail Nothing ("Insufficient coverage")
+        QC.InsufficientCoverage {}  -> Failure Nothing (Reason $ "Insufficient coverage")
 #endif
     where
       qcProgressCallback = QCP.PostTest QCP.NotCounterexample $
@@ -144,10 +185,12 @@
       sanitizeFailureMessage r = let m = QC.output r in strip $
 #if MIN_VERSION_QuickCheck(2,7,0)
         case QC.theException r of
-          Just e -> let numbers = formatNumbers r in
-            "uncaught exception: " ++ formatException e ++ " " ++ numbers ++ "\n" ++ case lines m of
-              x:xs | x == (exceptionPrefix ++ show e ++ "' " ++ numbers ++ ": ") -> unlines xs
-              _ -> m
+          Just e -> case E.fromException e :: Maybe (HUnit.HUnitFailure) of
+            Just _ -> (addFalsifiable . stripFailed) m
+            Nothing -> let numbers = formatNumbers r in
+              "uncaught exception: " ++ formatException e ++ " " ++ numbers ++ "\n" ++ case lines m of
+                x:xs | x == (exceptionPrefix ++ show e ++ "' " ++ numbers ++ ": ") -> unlines xs
+                _ -> m
           Nothing ->
 #endif
             (addFalsifiable . stripFailed) m
diff --git a/hspec-core/src/Test/Hspec/Core/FailureReport.hs b/hspec-core/src/Test/Hspec/Core/FailureReport.hs
new file mode 100644
--- /dev/null
+++ b/hspec-core/src/Test/Hspec/Core/FailureReport.hs
@@ -0,0 +1,65 @@
+{-# LANGUAGE CPP #-}
+module Test.Hspec.Core.FailureReport (
+  FailureReport (..)
+, writeFailureReport
+, readFailureReport
+) where
+
+#ifndef __GHCJS__
+import           System.SetEnv
+import           Test.Hspec.Core.Util (safeTry)
+#endif
+import           Control.Monad
+import           System.IO
+import           System.Directory
+import           Test.Hspec.Core.Compat
+import           Test.Hspec.Core.Util (Path)
+import           Test.Hspec.Core.Options (Config(..))
+
+data FailureReport = FailureReport {
+  failureReportSeed :: Integer
+, failureReportMaxSuccess :: Int
+, failureReportMaxSize :: Int
+, failureReportMaxDiscardRatio :: Int
+, failureReportPaths :: [Path]
+} deriving (Eq, Show, Read)
+
+writeFailureReport :: Config -> FailureReport -> IO ()
+writeFailureReport config report = case configFailureReport config of
+  Just file -> writeFile file (show report)
+  Nothing -> do
+#ifdef __GHCJS__
+    -- ghcjs currently does not support setting environment variables
+    -- (https://github.com/ghcjs/ghcjs/issues/263). Since writing a failure report
+    -- into the environment is a non-essential feature we just disable this to be
+    -- able to run hspec test-suites with ghcjs at all. Should be reverted once
+    -- the issue is fixed.
+    return ()
+#else
+    -- on Windows this can throw an exception when the input is too large, hence
+    -- we use `safeTry` here
+    safeTry (setEnv "HSPEC_FAILURES" $ show report) >>= either onError return
+    where
+      onError err = do
+        hPutStrLn stderr ("WARNING: Could not write environment variable HSPEC_FAILURES (" ++ show err ++ ")")
+#endif
+
+readFailureReport :: Config -> IO (Maybe FailureReport)
+readFailureReport config = case configFailureReport config of
+  Just file -> do
+    exists <- doesFileExist file
+    if exists
+      then do
+        r <- readFile file
+        let report = readMaybe r
+        when (report == Nothing) $ do
+          hPutStrLn stderr ("WARNING: Could not read failure report from file " ++ show file ++ "!")
+        return report
+      else return Nothing
+  Nothing -> do
+    mx <- lookupEnv "HSPEC_FAILURES"
+    case mx >>= readMaybe of
+      Nothing -> do
+        hPutStrLn stderr "WARNING: Could not read environment variable HSPEC_FAILURES; `--rerun' is ignored!"
+        return Nothing
+      report -> return report
diff --git a/hspec-core/src/Test/Hspec/Core/Formatters.hs b/hspec-core/src/Test/Hspec/Core/Formatters.hs
--- a/hspec-core/src/Test/Hspec/Core/Formatters.hs
+++ b/hspec-core/src/Test/Hspec/Core/Formatters.hs
@@ -19,6 +19,7 @@
 -- Actions live in the `FormatM` monad.  It provides access to the runner state
 -- and primitives for appending to the generated report.
 , Formatter (..)
+, FailureReason (..)
 , FormatM
 
 -- ** Accessing the runner state
@@ -37,7 +38,6 @@
 -- ** Appending to the gerenated report
 , write
 , writeLine
-, newParagraph
 
 -- ** Dealing with colors
 , withInfoColor
@@ -45,12 +45,15 @@
 , withPendingColor
 , withFailColor
 
+, extraChunk
+, missingChunk
+
 -- ** Helpers
 , formatException
 ) where
 
 import           Prelude ()
-import           Test.Hspec.Compat
+import           Test.Hspec.Core.Compat hiding (First)
 
 import           Data.Maybe
 import           Test.Hspec.Core.Util
@@ -64,8 +67,9 @@
 --
 -- Everything imported here has to be re-exported, so that users can implement
 -- their own formatters.
-import Test.Hspec.Core.Formatters.Internal (
+import Test.Hspec.Core.Formatters.Monad (
     Formatter (..)
+  , FailureReason (..)
   , FormatM
 
   , getSuccessCount
@@ -82,14 +86,18 @@
 
   , write
   , writeLine
-  , newParagraph
 
   , withInfoColor
   , withSuccessColor
   , withPendingColor
   , withFailColor
+
+  , extraChunk
+  , missingChunk
   )
 
+import           Test.Hspec.Core.Formatters.Diff
+
 silent :: Formatter
 silent = Formatter {
   headerFormatter     = return ()
@@ -103,7 +111,6 @@
 , footerFormatter     = return ()
 }
 
-
 specdoc :: Formatter
 specdoc = silent {
 
@@ -186,11 +193,37 @@
         withInfoColor $ writeLine (formatLoc loc)
       write ("  " ++ show n ++ ") ")
       writeLine (formatRequirement path)
-      withFailColor $ do
-        forM_ (lines err) $ \x -> do
-          writeLine ("       " ++ x)
+      case reason of
+        Left e -> withFailColor . indent $ (("uncaught exception: " ++) . formatException) e
+        Right NoReason -> return ()
+        Right (Reason err) -> withFailColor $ indent err
+        Right (ExpectedButGot preface expected actual) -> do
+          mapM_ indent preface
+
+          let chunks = diff expected actual
+
+          withFailColor $ write (indentation ++ "expected: ")
+          forM_ chunks $ \chunk -> case chunk of
+            Both a _ -> indented write a
+            First a -> indented extraChunk a
+            Second _ -> return ()
+          writeLine ""
+
+          withFailColor $ write (indentation ++ " but got: ")
+          forM_ chunks $ \chunk -> case chunk of
+            Both a _ -> indented write a
+            First _ -> return ()
+            Second a -> indented missingChunk a
+          writeLine ""
+          where
+            indented output text = case break (== '\n') text of
+              (xs, "") -> output xs
+              (xs, _ : ys) -> output (xs ++ "\n") >> write (indentation ++ "          ") >> indented output ys
       where
-        err = either (("uncaught exception: " ++) . formatException) id reason
+        indentation = "       "
+        indent message = do
+          forM_ (lines message) $ \line -> do
+            writeLine (indentation ++ line)
         formatLoc (Location file line _column accuracy) = "  " ++ file ++ ":" ++ show line ++ ":" ++ message
           where
             message = case accuracy of
@@ -204,19 +237,19 @@
 defaultFooter = do
 
   writeLine =<< (++)
-    <$> (printf "Finished in %1.4f seconds"
-    <$> getRealTime) <*> (maybe "" (printf ", used %1.4f seconds of CPU time") <$> getCPUTime)
+    <$> (printf "Finished in %1.4f seconds" <$> getRealTime)
+    <*> (maybe "" (printf ", used %1.4f seconds of CPU time") <$> getCPUTime)
 
   fails   <- getFailCount
   pending <- getPendingCount
   total   <- getTotalCount
 
-  let c | fails /= 0   = withFailColor
-        | pending /= 0 = withPendingColor
-        | otherwise    = withSuccessColor
-  c $ do
-    write $ pluralize total   "example"
-    write (", " ++ pluralize fails "failure")
-    unless (pending == 0) $
-      write (", " ++ show pending ++ " pending")
-  writeLine ""
+  let
+    output =
+         pluralize total   "example"
+      ++ ", " ++ pluralize fails "failure"
+      ++ if pending == 0 then "" else ", " ++ show pending ++ " pending"
+    c | fails /= 0   = withFailColor
+      | pending /= 0 = withPendingColor
+      | otherwise    = withSuccessColor
+  c $ writeLine output
diff --git a/hspec-core/src/Test/Hspec/Core/Formatters/Diff.hs b/hspec-core/src/Test/Hspec/Core/Formatters/Diff.hs
new file mode 100644
--- /dev/null
+++ b/hspec-core/src/Test/Hspec/Core/Formatters/Diff.hs
@@ -0,0 +1,33 @@
+{-# LANGUAGE CPP #-}
+module Test.Hspec.Core.Formatters.Diff (
+  Diff (..)
+, diff
+#ifdef TEST
+, partition
+, breakList
+#endif
+) where
+
+import           Data.Char
+import           Data.Algorithm.Diff
+
+diff :: String -> String -> [Diff String]
+diff expected actual = map (fmap concat) $ getGroupedDiff (partition expected) (partition actual)
+
+partition :: String -> [String]
+partition = mergeBackslashes . breakList isAlphaNum
+  where
+    mergeBackslashes xs = case xs of
+      ['\\'] : (y : ys) : zs -> ['\\', y] : ys : mergeBackslashes zs
+      z : zs -> z : mergeBackslashes zs
+      [] -> []
+
+breakList :: (a -> Bool) -> [a] -> [[a]]
+breakList _ [] = []
+breakList p xs = case break p xs of
+  (y, ys) -> map return y ++ case span p ys of
+    (z, zs) -> z `cons` breakList p zs
+  where
+    cons x
+      | null x = id
+      | otherwise = (x :)
diff --git a/hspec-core/src/Test/Hspec/Core/Formatters/Free.hs b/hspec-core/src/Test/Hspec/Core/Formatters/Free.hs
new file mode 100644
--- /dev/null
+++ b/hspec-core/src/Test/Hspec/Core/Formatters/Free.hs
@@ -0,0 +1,22 @@
+{-# LANGUAGE DeriveFunctor #-}
+module Test.Hspec.Core.Formatters.Free where
+
+import           Prelude ()
+import           Test.Hspec.Core.Compat
+
+data Free f a = Free (f (Free f a)) | Pure a
+  deriving Functor
+
+instance Functor f => Applicative (Free f) where
+  pure = Pure
+  Pure f <*> Pure a = Pure (f a)
+  Pure f <*> Free m = Free (fmap f <$> m)
+  Free m <*> b = Free (fmap (<*> b) m)
+
+instance Functor f => Monad (Free f) where
+  return = pure
+  Pure a >>= f = f a
+  Free m >>= f = Free (fmap (>>= f) m)
+
+liftF :: Functor f => f a -> Free f a
+liftF command = Free (fmap Pure command)
diff --git a/hspec-core/src/Test/Hspec/Core/Formatters/Internal.hs b/hspec-core/src/Test/Hspec/Core/Formatters/Internal.hs
--- a/hspec-core/src/Test/Hspec/Core/Formatters/Internal.hs
+++ b/hspec-core/src/Test/Hspec/Core/Formatters/Internal.hs
@@ -1,33 +1,8 @@
 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
 module Test.Hspec.Core.Formatters.Internal (
-
--- * Public API
-  Formatter (..)
-, FormatM
-
-, getSuccessCount
-, getPendingCount
-, getFailCount
-, getTotalCount
-
-, FailureRecord (..)
-, getFailMessages
-, usedSeed
-
-, getCPUTime
-, getRealTime
-
-, write
-, writeLine
-, newParagraph
-
-, withInfoColor
-, withSuccessColor
-, withPendingColor
-, withFailColor
-
--- * Functions for internal use
+  FormatM
 , runFormatM
+, interpret
 , increaseSuccessCount
 , increasePendingCount
 , increaseFailCount
@@ -36,7 +11,7 @@
 ) where
 
 import           Prelude ()
-import           Test.Hspec.Compat
+import           Test.Hspec.Core.Compat
 
 import qualified System.IO as IO
 import           System.IO (Handle)
@@ -49,8 +24,31 @@
 import           Data.Time.Clock.POSIX (POSIXTime, getPOSIXTime)
 
 import           Test.Hspec.Core.Util (Path)
-import           Test.Hspec.Core.Spec (Progress, Location)
+import           Test.Hspec.Core.Spec (Location)
+import           Test.Hspec.Core.Example (FailureReason(..))
 
+import qualified Test.Hspec.Core.Formatters.Monad as M
+import           Test.Hspec.Core.Formatters.Monad (Environment(..), interpretWith, FailureRecord(..))
+
+interpret :: M.FormatM a -> FormatM a
+interpret = interpretWith Environment {
+  environmentGetSuccessCount = getSuccessCount
+, environmentGetPendingCount = getPendingCount
+, environmentGetFailCount = getFailCount
+, environmentGetFailMessages = getFailMessages
+, environmentUsedSeed = usedSeed
+, environmentGetCPUTime = getCPUTime
+, environmentGetRealTime = getRealTime
+, environmentWrite = write
+, environmentWithFailColor = withFailColor
+, environmentWithSuccessColor = withSuccessColor
+, environmentWithPendingColor = withPendingColor
+, environmentWithInfoColor = withInfoColor
+, environmentExtraChunk = extraChunk
+, environmentMissingChunk = missingChunk
+, environmentLiftIO = liftIO
+}
+
 -- | A lifted version of `Control.Monad.Trans.State.gets`
 gets :: (FormatterState -> a) -> FormatM a
 gets f = FormatM $ do
@@ -64,6 +62,7 @@
 data FormatterState = FormatterState {
   stateHandle     :: Handle
 , stateUseColor   :: Bool
+, stateUseDiff    :: Bool
 , produceHTML     :: Bool
 , successCount    :: Int
 , pendingCount    :: Int
@@ -78,20 +77,16 @@
 usedSeed :: FormatM Integer
 usedSeed = gets stateUsedSeed
 
--- | The total number of examples encountered so far.
-totalCount :: FormatterState -> Int
-totalCount s = successCount s + pendingCount s + failCount s
-
 -- NOTE: We use an IORef here, so that the state persists when UserInterrupt is
 -- thrown.
 newtype FormatM a = FormatM (StateT (IORef FormatterState) IO a)
   deriving (Functor, Applicative, Monad, MonadIO)
 
-runFormatM :: Bool -> Bool -> Bool -> Integer -> Handle -> FormatM a -> IO a
-runFormatM useColor produceHTML_ printCpuTime seed handle (FormatM action) = do
+runFormatM :: Bool -> Bool -> Bool -> Bool -> Integer -> Handle -> FormatM a -> IO a
+runFormatM useColor useDiff produceHTML_ printCpuTime seed handle (FormatM action) = do
   time <- getPOSIXTime
   cpuTime <- if printCpuTime then Just <$> CPUTime.getCPUTime else pure Nothing
-  st <- newIORef (FormatterState handle useColor produceHTML_ 0 0 0 [] seed cpuTime time)
+  st <- newIORef (FormatterState handle useColor useDiff produceHTML_ 0 0 0 [] seed cpuTime time)
   evalStateT action st
 
 -- | Increase the counter for successful examples
@@ -118,74 +113,20 @@
 getFailCount :: FormatM Int
 getFailCount = gets failCount
 
--- | Get the total number of examples encountered so far.
-getTotalCount :: FormatM Int
-getTotalCount = gets totalCount
-
 -- | Append to the list of accumulated failure messages.
-addFailMessage :: Maybe Location -> Path -> Either SomeException String -> FormatM ()
+addFailMessage :: Maybe Location -> Path -> Either SomeException FailureReason -> FormatM ()
 addFailMessage loc p m = modify $ \s -> s {failMessages = FailureRecord loc p m : failMessages s}
 
 -- | Get the list of accumulated failure messages.
 getFailMessages :: FormatM [FailureRecord]
 getFailMessages = reverse `fmap` gets failMessages
 
-data FailureRecord = FailureRecord {
-  failureRecordLocation :: Maybe Location
-, failureRecordPath     :: Path
-, failureRecordMessage  :: Either SomeException String
-}
-
-data Formatter = Formatter {
-
-  headerFormatter :: FormatM ()
-
--- | evaluated before each test group
---
--- The given number indicates the position within the parent group.
-, exampleGroupStarted :: [String] -> String -> FormatM ()
-
-, exampleGroupDone    :: FormatM ()
-
--- | used to notify the progress of the currently evaluated example
---
--- /Note/: This is only called when interactive/color mode.
-, exampleProgress     :: Handle -> Path -> Progress -> IO ()
-
--- | evaluated after each successful example
-, exampleSucceeded    :: Path -> FormatM ()
-
--- | evaluated after each failed example
-, exampleFailed       :: Path -> Either SomeException String -> FormatM ()
-
--- | evaluated after each pending example
-, examplePending      :: Path -> Maybe String -> FormatM ()
-
--- | evaluated after a test run
-, failedFormatter     :: FormatM ()
-
--- | evaluated after `failuresFormatter`
-, footerFormatter     :: FormatM ()
-}
-
-
--- | Append an empty line to the report.
---
--- Calling this multiple times has the same effect as calling it once.
-newParagraph :: FormatM ()
-newParagraph = writeLine ""
-{-# DEPRECATED newParagraph "use @writeLine \"\"@ instead" #-}
-
 -- | Append some output to the report.
 write :: String -> FormatM ()
 write s = do
   h <- gets stateHandle
   liftIO $ IO.hPutStr h s
 
--- | The same as `write`, but adds a newline character.
-writeLine :: String -> FormatM ()
-writeLine s = write s >> write "\n"
-
 -- | Set output color to red, run given action, and finally restore the default
 -- color.
 withFailColor :: FormatM a -> FormatM a
@@ -231,6 +172,22 @@
 
       -- run action
       (runStateT action st)
+
+-- | Output given chunk in red.
+extraChunk :: String -> FormatM ()
+extraChunk s = do
+  useDiff <- gets stateUseDiff
+  case useDiff of
+    True -> withFailColor $ write s
+    False -> write s
+
+-- | Output given chunk in green.
+missingChunk :: String -> FormatM ()
+missingChunk s = do
+  useDiff <- gets stateUseDiff
+  case useDiff of
+    True -> withSuccessColor $ write s
+    False -> write s
 
 -- |
 -- @finally_ actionA actionB@ runs @actionA@ and then @actionB@.  @actionB@ is
diff --git a/hspec-core/src/Test/Hspec/Core/Formatters/Monad.hs b/hspec-core/src/Test/Hspec/Core/Formatters/Monad.hs
new file mode 100644
--- /dev/null
+++ b/hspec-core/src/Test/Hspec/Core/Formatters/Monad.hs
@@ -0,0 +1,237 @@
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE RankNTypes #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE StandaloneDeriving #-}
+{-# LANGUAGE ExistentialQuantification #-}
+module Test.Hspec.Core.Formatters.Monad (
+  Formatter (..)
+, FailureReason (..)
+, FormatM
+
+, getSuccessCount
+, getPendingCount
+, getFailCount
+, getTotalCount
+
+, FailureRecord (..)
+, getFailMessages
+, usedSeed
+
+, getCPUTime
+, getRealTime
+
+, write
+, writeLine
+
+, withInfoColor
+, withSuccessColor
+, withPendingColor
+, withFailColor
+
+, extraChunk
+, missingChunk
+
+, Environment(..)
+, interpretWith
+) where
+
+import           Prelude ()
+import           Test.Hspec.Core.Compat
+
+import           System.IO (Handle)
+import           Control.Exception
+import           Control.Monad.IO.Class
+
+import           Test.Hspec.Core.Formatters.Free
+
+import           Test.Hspec.Core.Example (FailureReason(..))
+import           Test.Hspec.Core.Util (Path)
+import           Test.Hspec.Core.Spec (Progress, Location)
+
+data Formatter = Formatter {
+
+  headerFormatter :: FormatM ()
+
+-- | evaluated before each test group
+--
+-- The given number indicates the position within the parent group.
+, exampleGroupStarted :: [String] -> String -> FormatM ()
+
+, exampleGroupDone    :: FormatM ()
+
+-- | used to notify the progress of the currently evaluated example
+--
+-- /Note/: This is only called when interactive/color mode.
+, exampleProgress     :: Handle -> Path -> Progress -> IO ()
+
+-- | evaluated after each successful example
+, exampleSucceeded    :: Path -> FormatM ()
+
+-- | evaluated after each failed example
+, exampleFailed       :: Path -> Either SomeException FailureReason -> FormatM ()
+
+-- | evaluated after each pending example
+, examplePending      :: Path -> Maybe String -> FormatM ()
+
+-- | evaluated after a test run
+, failedFormatter     :: FormatM ()
+
+-- | evaluated after `failuresFormatter`
+, footerFormatter     :: FormatM ()
+}
+
+data FailureRecord = FailureRecord {
+  failureRecordLocation :: Maybe Location
+, failureRecordPath     :: Path
+, failureRecordMessage  :: Either SomeException FailureReason
+}
+
+data FormatF next =
+    GetSuccessCount (Int -> next)
+  | GetPendingCount (Int -> next)
+  | GetFailCount (Int -> next)
+  | GetFailMessages ([FailureRecord] -> next)
+  | UsedSeed (Integer -> next)
+  | GetCPUTime (Maybe Double -> next)
+  | GetRealTime (Double -> next)
+  | Write String next
+  | forall a. WithFailColor (FormatM a) (a -> next)
+  | forall a. WithSuccessColor (FormatM a) (a -> next)
+  | forall a. WithPendingColor (FormatM a) (a -> next)
+  | forall a. WithInfoColor (FormatM a) (a -> next)
+  | ExtraChunk String next
+  | MissingChunk String next
+  | forall a. LiftIO (IO a) (a -> next)
+
+instance Functor FormatF where -- deriving this instance would require GHC >= 7.10.1
+  fmap f x = case x of
+    GetSuccessCount next -> GetSuccessCount (fmap f next)
+    GetPendingCount next -> GetPendingCount (fmap f next)
+    GetFailCount next -> GetFailCount (fmap f next)
+    GetFailMessages next -> GetFailMessages (fmap f next)
+    UsedSeed next -> UsedSeed (fmap f next)
+    GetCPUTime next -> GetCPUTime (fmap f next)
+    GetRealTime next -> GetRealTime (fmap f next)
+    Write s next -> Write s (f next)
+    WithFailColor action next -> WithFailColor action (fmap f next)
+    WithSuccessColor action next -> WithSuccessColor action (fmap f next)
+    WithPendingColor action next -> WithPendingColor action (fmap f next)
+    WithInfoColor action next -> WithInfoColor action (fmap f next)
+    ExtraChunk s next -> ExtraChunk s (f next)
+    MissingChunk s next -> MissingChunk s (f next)
+    LiftIO action next -> LiftIO action (fmap f next)
+
+type FormatM = Free FormatF
+
+instance MonadIO FormatM where
+  liftIO s = liftF (LiftIO s id)
+
+data Environment m = Environment {
+  environmentGetSuccessCount :: m Int
+, environmentGetPendingCount :: m Int
+, environmentGetFailCount :: m Int
+, environmentGetFailMessages :: m [FailureRecord]
+, environmentUsedSeed :: m Integer
+, environmentGetCPUTime :: m (Maybe Double)
+, environmentGetRealTime :: m Double
+, environmentWrite :: String -> m ()
+, environmentWithFailColor :: forall a. m a -> m a
+, environmentWithSuccessColor :: forall a. m a -> m a
+, environmentWithPendingColor :: forall a. m a -> m a
+, environmentWithInfoColor :: forall a. m a -> m a
+, environmentExtraChunk :: String -> m ()
+, environmentMissingChunk :: String -> m ()
+, environmentLiftIO :: forall a. IO a -> m a
+}
+
+interpretWith :: forall m a. Monad m => Environment m -> FormatM a -> m a
+interpretWith Environment{..} = go
+  where
+    go :: forall b. FormatM b -> m b
+    go m = case m of
+      Pure value -> return value
+      Free action -> case action of
+        GetSuccessCount next -> environmentGetSuccessCount >>= go . next
+        GetPendingCount next -> environmentGetPendingCount >>= go . next
+        GetFailCount next -> environmentGetFailCount >>= go . next
+        GetFailMessages next -> environmentGetFailMessages >>= go . next
+        UsedSeed next -> environmentUsedSeed >>= go . next
+        GetCPUTime next -> environmentGetCPUTime >>= go . next
+        GetRealTime next -> environmentGetRealTime >>= go . next
+        Write s next -> environmentWrite s >> go next
+        WithFailColor inner next -> environmentWithFailColor (go inner) >>= go . next
+        WithSuccessColor inner next -> environmentWithSuccessColor (go inner) >>= go . next
+        WithPendingColor inner next -> environmentWithPendingColor (go inner) >>= go . next
+        WithInfoColor inner next -> environmentWithInfoColor (go inner) >>= go . next
+        ExtraChunk s next -> environmentExtraChunk s >> go next
+        MissingChunk s next -> environmentMissingChunk s >> go next
+        LiftIO inner next -> environmentLiftIO inner >>= go . next
+
+-- | Get the number of successful examples encountered so far.
+getSuccessCount :: FormatM Int
+getSuccessCount = liftF (GetSuccessCount id)
+
+-- | Get the number of pending examples encountered so far.
+getPendingCount :: FormatM Int
+getPendingCount = liftF (GetPendingCount id)
+
+-- | Get the number of failed examples encountered so far.
+getFailCount :: FormatM Int
+getFailCount = liftF (GetFailCount id)
+
+-- | Get the total number of examples encountered so far.
+getTotalCount :: FormatM Int
+getTotalCount = sum <$> sequence [getSuccessCount, getFailCount, getPendingCount]
+
+-- | Get the list of accumulated failure messages.
+getFailMessages :: FormatM [FailureRecord]
+getFailMessages = liftF (GetFailMessages id)
+
+-- | The random seed that is used for QuickCheck.
+usedSeed :: FormatM Integer
+usedSeed = liftF (UsedSeed id)
+
+-- | Get the used CPU time since the test run has been started.
+getCPUTime :: FormatM (Maybe Double)
+getCPUTime = liftF (GetCPUTime id)
+
+-- | Get the passed real time since the test run has been started.
+getRealTime :: FormatM Double
+getRealTime = liftF (GetRealTime id)
+
+-- | Append some output to the report.
+write :: String -> FormatM ()
+write s = liftF (Write s ())
+
+-- | The same as `write`, but adds a newline character.
+writeLine :: String -> FormatM ()
+writeLine s = write s >> write "\n"
+
+-- | Set output color to red, run given action, and finally restore the default
+-- color.
+withFailColor :: FormatM a -> FormatM a
+withFailColor s = liftF (WithFailColor s id)
+
+-- | Set output color to green, run given action, and finally restore the
+-- default color.
+withSuccessColor :: FormatM a -> FormatM a
+withSuccessColor s = liftF (WithSuccessColor s id)
+
+-- | Set output color to yellow, run given action, and finally restore the
+-- default color.
+withPendingColor :: FormatM a -> FormatM a
+withPendingColor s = liftF (WithPendingColor s id)
+
+-- | Set output color to cyan, run given action, and finally restore the
+-- default color.
+withInfoColor :: FormatM a -> FormatM a
+withInfoColor s = liftF (WithInfoColor s id)
+
+-- | Output given chunk in red.
+extraChunk :: String -> FormatM ()
+extraChunk s = liftF (ExtraChunk s ())
+
+-- | Output given chunk in green.
+missingChunk :: String -> FormatM ()
+missingChunk s = liftF (MissingChunk s ())
diff --git a/hspec-core/src/Test/Hspec/Core/Hooks.hs b/hspec-core/src/Test/Hspec/Core/Hooks.hs
--- a/hspec-core/src/Test/Hspec/Core/Hooks.hs
+++ b/hspec-core/src/Test/Hspec/Core/Hooks.hs
@@ -14,10 +14,12 @@
 , aroundWith
 ) where
 
-import           Control.Exception (finally)
+import           Control.Exception (SomeException, finally, throwIO, try)
 import           Control.Concurrent.MVar
 
-import           Test.Hspec.Core.Spec
+import           Test.Hspec.Core.Example
+import           Test.Hspec.Core.Tree
+import           Test.Hspec.Core.Spec.Monad
 
 -- | Run a custom action before every spec item.
 before :: IO a -> SpecWith a -> Spec
@@ -34,22 +36,30 @@
 -- | Run a custom action before the first spec item.
 beforeAll :: IO a -> SpecWith a -> Spec
 beforeAll action spec = do
-  mvar <- runIO (newMVar Nothing)
+  mvar <- runIO (newMVar Empty)
   before (memoize mvar action) spec
 
 -- | Run a custom action before the first spec item.
 beforeAll_ :: IO () -> SpecWith a -> SpecWith a
 beforeAll_ action spec = do
-  mvar <- runIO (newMVar Nothing)
+  mvar <- runIO (newMVar Empty)
   before_ (memoize mvar action) spec
 
-memoize :: MVar (Maybe a) -> IO a -> IO a
-memoize mvar action = modifyMVar mvar $ \ma -> case ma of
-  Just a -> return (ma, a)
-  Nothing -> do
-    a <- action
-    return (Just a, a)
+data Memoized a =
+    Empty
+  | Memoized a
+  | Failed SomeException
 
+memoize :: MVar (Memoized a) -> IO a -> IO a
+memoize mvar action = do
+  result <- modifyMVar mvar $ \ma -> case ma of
+    Empty -> do
+      a <- try action
+      return (either Failed Memoized a, a)
+    Memoized a -> return (ma, Right a)
+    Failed _ -> throwIO (Pending (Just "exception in beforeAll-hook (see previous failure)"))
+  either throwIO return result
+
 -- | Run a custom action after every spec item.
 after :: ActionWith a -> SpecWith a -> SpecWith a
 after action = aroundWith $ \e x -> e x `finally` action x
@@ -76,10 +86,8 @@
 
 -- | Run a custom action before and/or after every spec item.
 aroundWith :: (ActionWith a -> ActionWith b) -> SpecWith a -> SpecWith b
-aroundWith action = mapAround (. action)
-
-mapAround :: ((ActionWith b -> IO ()) -> ActionWith a -> IO ()) -> SpecWith a -> SpecWith b
-mapAround f = mapSpecItem (untangle f) $ \i@Item{itemExample = e} -> i{itemExample = (. f) . e}
+aroundWith action = mapSpecItem action (modifyAroundAction action)
 
-untangle  :: ((ActionWith b -> IO ()) -> ActionWith a -> IO ()) -> ActionWith a -> ActionWith b
-untangle f g = \b -> f ($ b) g
+modifyAroundAction :: (ActionWith a -> ActionWith b) -> Item a -> Item b
+modifyAroundAction action item@Item{itemExample = e} =
+  item{ itemExample = \params aroundAction -> e params (aroundAction . action) }
diff --git a/hspec-core/src/Test/Hspec/Core/Options.hs b/hspec-core/src/Test/Hspec/Core/Options.hs
new file mode 100644
--- /dev/null
+++ b/hspec-core/src/Test/Hspec/Core/Options.hs
@@ -0,0 +1,283 @@
+module Test.Hspec.Core.Options (
+  Config(..)
+, ColorMode (..)
+, defaultConfig
+, filterOr
+, parseOptions
+, ConfigFile
+, ignoreConfigFile
+, envVarName
+) where
+
+import           Prelude ()
+import           Control.Monad
+import           Test.Hspec.Core.Compat
+
+import           System.IO
+import           System.Exit
+import           System.Console.GetOpt
+
+import           Test.Hspec.Core.Formatters
+import           Test.Hspec.Core.Util
+import           Test.Hspec.Core.Example (Params(..), defaultParams)
+import           Data.Functor.Identity
+import           Data.Maybe
+
+type ConfigFile = (FilePath, [String])
+
+type EnvVar = [String]
+
+envVarName :: String
+envVarName = "HSPEC_OPTIONS"
+
+data Config = Config {
+  configIgnoreConfigFile :: Bool
+, configDryRun :: Bool
+, configPrintCpuTime :: Bool
+, configFastFail :: Bool
+, configFailureReport :: Maybe FilePath
+, configRerun :: Bool
+, configRerunAllOnSuccess :: Bool
+
+-- |
+-- A predicate that is used to filter the spec before it is run.  Only examples
+-- that satisfy the predicate are run.
+, configFilterPredicate :: Maybe (Path -> Bool)
+, configSkipPredicate :: Maybe (Path -> Bool)
+, configQuickCheckSeed :: Maybe Integer
+, configQuickCheckMaxSuccess :: Maybe Int
+, configQuickCheckMaxDiscardRatio :: Maybe Int
+, configQuickCheckMaxSize :: Maybe Int
+, configSmallCheckDepth :: Int
+, configColorMode :: ColorMode
+, configDiff :: Bool
+, configFormatter :: Maybe Formatter
+, configHtmlOutput :: Bool
+, configOutputFile :: Either Handle FilePath
+, configConcurrentJobs :: Maybe Int
+}
+
+defaultConfig :: Config
+defaultConfig = Config {
+  configIgnoreConfigFile = False
+, configDryRun = False
+, configPrintCpuTime = False
+, configFastFail = False
+, configFailureReport = Nothing
+, configRerun = False
+, configRerunAllOnSuccess = False
+, configFilterPredicate = Nothing
+, configSkipPredicate = Nothing
+, configQuickCheckSeed = Nothing
+, configQuickCheckMaxSuccess = Nothing
+, configQuickCheckMaxDiscardRatio = Nothing
+, configQuickCheckMaxSize = Nothing
+, configSmallCheckDepth = paramsSmallCheckDepth defaultParams
+, configColorMode = ColorAuto
+, configDiff = True
+, configFormatter = Nothing
+, configHtmlOutput = False
+, configOutputFile = Left stdout
+, configConcurrentJobs = Nothing
+}
+
+filterOr :: Maybe (Path -> Bool) -> Maybe (Path -> Bool) -> Maybe (Path -> Bool)
+filterOr p1_ p2_ = case (p1_, p2_) of
+  (Just p1, Just p2) -> Just $ \path -> p1 path || p2 path
+  _ -> p1_ <|> p2_
+
+addMatch :: String -> Config -> Config
+addMatch s c = c {configFilterPredicate = Just (filterPredicate s) `filterOr` configFilterPredicate c}
+
+addSkip :: String -> Config -> Config
+addSkip s c = c {configSkipPredicate = Just (filterPredicate s) `filterOr` configSkipPredicate c}
+
+setDepth :: Int -> Config -> Config
+setDepth n c = c {configSmallCheckDepth = n}
+
+setMaxSuccess :: Int -> Config -> Config
+setMaxSuccess n c = c {configQuickCheckMaxSuccess = Just n}
+
+setMaxSize :: Int -> Config -> Config
+setMaxSize n c = c {configQuickCheckMaxSize = Just n}
+
+setMaxDiscardRatio :: Int -> Config -> Config
+setMaxDiscardRatio n c = c {configQuickCheckMaxDiscardRatio = Just n}
+
+setSeed :: Integer -> Config -> Config
+setSeed n c = c {configQuickCheckSeed = Just n}
+
+data ColorMode = ColorAuto | ColorNever | ColorAlways
+  deriving (Eq, Show)
+
+formatters :: [(String, Formatter)]
+formatters = [
+    ("specdoc", specdoc)
+  , ("progress", progress)
+  , ("failed-examples", failed_examples)
+  , ("silent", silent)
+  ]
+
+formatHelp :: String
+formatHelp = unlines (addLineBreaks "use a custom formatter; this can be one of:" ++ map (("   " ++) . fst) formatters)
+
+type Result m = Either InvalidArgument (m Config)
+
+data InvalidArgument = InvalidArgument String String
+
+data Arg a = Arg {
+  _argumentName   :: String
+, _argumentParser :: String -> Maybe a
+, _argumentSetter :: a -> Config -> Config
+}
+
+mkOption :: Monad m => [Char] -> String -> Arg a -> String -> OptDescr (Result m -> Result m)
+mkOption shortcut name (Arg argName parser setter) help = Option shortcut [name] (ReqArg arg argName) help
+  where
+    arg input x = x >>= \c -> case parser input of
+      Just n -> Right (setter n `liftM` c)
+      Nothing -> Left (InvalidArgument name input)
+
+addLineBreaks :: String -> [String]
+addLineBreaks = lineBreaksAt 40
+
+h :: String -> String
+h = unlines . addLineBreaks
+
+commandLineOptions :: [OptDescr (Result Maybe -> Result Maybe)]
+commandLineOptions = [
+    Option   []  ["help"]             (NoArg (const $ Right Nothing))     (h "display this help and exit")
+  , Option   []  ["ignore-dot-hspec"] (NoArg setIgnoreConfigFile)         (h "do not read options from ~/.hspec and .hspec")
+  , mkOption "m"  "match"             (Arg "PATTERN" return addMatch)     (h "only run examples that match given PATTERN")
+  , mkOption []   "skip"              (Arg "PATTERN" return addSkip)      (h "skip examples that match given PATTERN")
+  ]
+  where
+    setIgnoreConfigFile = set $ \config -> config {configIgnoreConfigFile = True}
+
+configFileOptions :: Monad m => [OptDescr (Result m -> Result m)]
+configFileOptions = [
+    Option   []  ["color"]            (NoArg setColor)                    (h "colorize the output")
+  , Option   []  ["no-color"]         (NoArg setNoColor)                  (h "do not colorize the output")
+  , Option   []  ["diff"]             (NoArg setDiff)                     (h "show colorized diffs")
+  , Option   []  ["no-diff"]          (NoArg setNoDiff)                   (h "do not show colorized diffs")
+  , mkOption "f"  "format"            (Arg "FORMATTER" readFormatter setFormatter) formatHelp
+  , mkOption "o"  "out"               (Arg "FILE" return setOutputFile)   (h "write output to a file instead of STDOUT")
+  , mkOption []   "depth"             (Arg "N" readMaybe setDepth)        (h "maximum depth of generated test values for SmallCheck properties")
+  , mkOption "a"  "qc-max-success"    (Arg "N" readMaybe setMaxSuccess)   (h "maximum number of successful tests before a QuickCheck property succeeds")
+  , mkOption ""   "qc-max-size"       (Arg "N" readMaybe setMaxSize)      (h "size to use for the biggest test cases")
+  , mkOption ""   "qc-max-discard"    (Arg "N" readMaybe setMaxDiscardRatio) (h "maximum number of discarded tests per successful test before giving up")
+  , mkOption []   "seed"              (Arg "N" readMaybe setSeed)         (h "used seed for QuickCheck properties")
+  , Option   []  ["print-cpu-time"]   (NoArg setPrintCpuTime)             (h "include used CPU time in summary")
+  , Option   []  ["dry-run"]          (NoArg setDryRun)                   (h "pretend that everything passed; don't verify anything")
+  , Option   []  ["fail-fast"]        (NoArg setFastFail)                 (h "abort on first failure")
+  , Option   "r" ["rerun"]            (NoArg  setRerun)                   (h "rerun all examples that failed in the previous test run (only works in combination with --failure-report or in GHCi)")
+  , mkOption []   "failure-report"    (Arg "FILE" return setFailureReport)(h "read/write a failure report for use with --rerun")
+  , Option   []  ["rerun-all-on-success"] (NoArg setRerunAllOnSuccess)    (h "run the whole test suite after a previously failing rerun succeeds for the first time (only works in combination with --rerun)")
+  , mkOption "j"  "jobs"              (Arg "N" readMaxJobs setMaxJobs)    (h "run at most N parallelizable tests simultaneously (default: number of available processors)")
+  ]
+  where
+    readFormatter :: String -> Maybe Formatter
+    readFormatter = (`lookup` formatters)
+
+    readMaxJobs :: String -> Maybe Int
+    readMaxJobs s = do
+      n <- readMaybe s
+      guard $ n > 0
+      return n
+
+    setFormatter :: Formatter -> Config -> Config
+    setFormatter f c = c {configFormatter = Just f}
+
+    setOutputFile :: String -> Config -> Config
+    setOutputFile file c = c {configOutputFile = Right file}
+
+    setFailureReport :: String -> Config -> Config
+    setFailureReport file c = c {configFailureReport = Just file}
+
+    setMaxJobs :: Int -> Config -> Config
+    setMaxJobs n c = c {configConcurrentJobs = Just n}
+
+    setPrintCpuTime = set $ \config -> config {configPrintCpuTime = True}
+    setDryRun       = set $ \config -> config {configDryRun = True}
+    setFastFail     = set $ \config -> config {configFastFail = True}
+    setRerun        = set $ \config -> config {configRerun = True}
+    setRerunAllOnSuccess = set $ \config -> config {configRerunAllOnSuccess = True}
+    setColor        = set $ \config -> config {configColorMode = ColorAlways}
+    setNoColor      = set $ \config -> config {configColorMode = ColorNever}
+    setDiff         = set $ \config -> config {configDiff = True}
+    setNoDiff       = set $ \config -> config {configDiff = False}
+
+set :: Monad m => (Config -> Config) -> Either a (m Config) -> Either a (m Config)
+set = liftM . liftM
+
+documentedOptions :: [OptDescr (Result Maybe -> Result Maybe)]
+documentedOptions = commandLineOptions ++ configFileOptions
+
+undocumentedOptions :: [OptDescr (Result Maybe -> Result Maybe)]
+undocumentedOptions = [
+    -- for compatibility with test-framework
+    mkOption [] "maximum-generated-tests" (Arg "NUMBER" readMaybe setMaxSuccess) "how many automated tests something like QuickCheck should try, by default"
+
+    -- undocumented for now, as we probably want to change this to produce a
+    -- standalone HTML report in the future
+  , Option []  ["html"]                    (NoArg setHtml)                    "produce HTML output"
+
+    -- now a noop
+  , Option "v" ["verbose"]                 (NoArg id)                         "do not suppress output to stdout when evaluating examples"
+  ]
+  where
+    setHtml :: Result Maybe -> Result Maybe
+    setHtml = set $ \config -> config {configHtmlOutput = True}
+
+recognizedOptions :: [OptDescr (Result Maybe -> Result Maybe)]
+recognizedOptions = documentedOptions ++ undocumentedOptions
+
+parseOptions :: Config -> String -> [ConfigFile] -> Maybe EnvVar -> [String] -> Either (ExitCode, String) Config
+parseOptions config prog configFiles envVar args = do
+      foldM (parseFileOptions prog) config configFiles
+  >>= parseEnvVarOptions prog envVar
+  >>= parseCommandLineOptions prog args
+
+parseCommandLineOptions :: String -> [String] -> Config -> Either (ExitCode, String) Config
+parseCommandLineOptions prog args config = case parse recognizedOptions config args of
+  Right Nothing -> Left (ExitSuccess, usageInfo ("Usage: " ++ prog ++ " [OPTION]...\n\nOPTIONS") documentedOptions)
+  Right (Just c) -> Right c
+  Left err -> failure err
+  where
+    failure err = Left (ExitFailure 1, prog ++ ": " ++ err ++ "\nTry `" ++ prog ++ " --help' for more information.\n")
+
+parseFileOptions :: String -> Config -> ConfigFile -> Either (ExitCode, String) Config
+parseFileOptions prog config (name, args) =
+  parseOtherOptions prog ("in config file " ++ name) args config
+
+parseEnvVarOptions :: String -> (Maybe EnvVar) -> Config -> Either (ExitCode, String) Config
+parseEnvVarOptions prog args =
+  parseOtherOptions prog ("from environment variable " ++ envVarName) (fromMaybe [] args)
+
+parseOtherOptions :: String -> String -> [String] -> Config -> Either (ExitCode, String) Config
+parseOtherOptions prog source args config = case parse configFileOptions config args of
+  Right (Identity c) -> Right c
+  Left err -> failure err
+  where
+    failure err = Left (ExitFailure 1, prog ++ ": " ++ message)
+      where
+        message = unlines $ case lines err of
+          [x] -> [x ++ " " ++ source]
+          xs -> xs ++ [source]
+
+parse :: Monad m => [OptDescr (Result m -> Result m)] -> Config -> [String] -> Either String (m Config)
+parse options config args = case getOpt Permute options args of
+  (opts, [], []) -> case foldl' (flip id) (Right $ return config) opts of
+    Left (InvalidArgument name value) -> Left ("invalid argument `" ++ value ++ "' for `--" ++ name ++ "'")
+    Right x -> Right x
+  (_, _, err:_) -> Left (init err)
+  (_, arg:_, _) -> Left ("unexpected argument `" ++ arg ++ "'")
+
+ignoreConfigFile :: Config -> [String] -> IO Bool
+ignoreConfigFile config args = do
+  ignore <- lookupEnv "IGNORE_DOT_HSPEC"
+  case ignore of
+    Just _ -> return True
+    Nothing -> case parse recognizedOptions config args of
+      Right (Just c) -> return (configIgnoreConfigFile c)
+      _ -> return False
diff --git a/hspec-core/src/Test/Hspec/Core/QuickCheckUtil.hs b/hspec-core/src/Test/Hspec/Core/QuickCheckUtil.hs
--- a/hspec-core/src/Test/Hspec/Core/QuickCheckUtil.hs
+++ b/hspec-core/src/Test/Hspec/Core/QuickCheckUtil.hs
@@ -2,7 +2,7 @@
 module Test.Hspec.Core.QuickCheckUtil where
 
 import           Prelude ()
-import           Test.Hspec.Compat
+import           Test.Hspec.Core.Compat
 
 import           Data.Int
 import           Test.QuickCheck hiding (Result(..))
diff --git a/hspec-core/src/Test/Hspec/Core/Runner.hs b/hspec-core/src/Test/Hspec/Core/Runner.hs
--- a/hspec-core/src/Test/Hspec/Core/Runner.hs
+++ b/hspec-core/src/Test/Hspec/Core/Runner.hs
@@ -21,10 +21,14 @@
 , Path
 , defaultConfig
 , configAddFilter
+
+#ifdef TEST
+, rerunAll
+#endif
 ) where
 
 import           Prelude ()
-import           Test.Hspec.Compat
+import           Test.Hspec.Core.Compat
 
 import           Control.Monad
 import           Data.Maybe
@@ -40,10 +44,11 @@
 
 import           Test.Hspec.Core.Util (Path)
 import           Test.Hspec.Core.Spec
-import           Test.Hspec.Config
+import           Test.Hspec.Core.Config
 import           Test.Hspec.Core.Formatters
 import           Test.Hspec.Core.Formatters.Internal
-import           Test.Hspec.FailureReport
+import qualified Test.Hspec.Core.Formatters.Internal as Formatter
+import           Test.Hspec.Core.FailureReport
 import           Test.Hspec.Core.QuickCheckUtil
 
 import           Test.Hspec.Core.Runner.Eval
@@ -78,7 +83,7 @@
   | otherwise = id
   where
     markSuccess :: Item () -> Item ()
-    markSuccess item = item {itemExample = evaluateExample Success}
+    markSuccess item = item {itemExample = safeEvaluateExample Success}
 
     removeCleanup :: SpecTree () -> SpecTree ()
     removeCleanup spec = case spec of
@@ -105,8 +110,11 @@
 hspecWith :: Config -> Spec -> IO ()
 hspecWith conf spec = do
   r <- hspecWithResult conf spec
-  unless (summaryFailures r == 0) exitFailure
+  unless (isSuccess r) exitFailure
 
+isSuccess :: Summary -> Bool
+isSuccess summary = summaryFailures summary == 0
+
 -- | Run given spec and returns a summary of the test run.
 --
 -- /Note/: `hspecResult` does not exit with `exitFailure` on failing spec
@@ -121,57 +129,93 @@
 -- items.  If you need this, you have to check the `Summary` yourself and act
 -- accordingly.
 hspecWithResult :: Config -> Spec -> IO Summary
-hspecWithResult conf spec = do
+hspecWithResult config spec = do
   prog <- getProgName
   args <- getArgs
-  c <- getConfig conf prog args >>= ensureSeed
-  withArgs [] {- do not leak command-line arguments to examples -} $ withHandle c $ \h -> do
-    let formatter = fromMaybe specdoc (configFormatter c)
-        seed = (fromJust . configQuickCheckSeed) c
-        qcArgs = configQuickCheckArgs c
+  (oldFailureReport, c_) <- getConfig config prog args
+  c <- ensureSeed c_
+  if configRerunAllOnSuccess c
+    -- With --rerun-all we may run the spec twice. For that reason GHC can not
+    -- optimize away the spec tree. That means that the whole spec tree has to
+    -- be constructed in memory and we loose constant space behavior.
+    --
+    -- By separating between rerunAllMode and normalMode here, we retain
+    -- constant space behavior in normalMode.
+    --
+    -- see: https://github.com/hspec/hspec/issues/169
+    then rerunAllMode c oldFailureReport
+    else normalMode c
+  where
+    normalMode c = runSpec c spec
+    rerunAllMode c oldFailureReport = do
+      summary <- runSpec c spec
+      if rerunAll c oldFailureReport summary
+        then hspecWithResult config spec
+        else return summary
 
-    jobsSem <- newQSem =<< case configConcurrentJobs c of
+runSpec :: Config -> Spec -> IO Summary
+runSpec config spec = do
+  doNotLeakCommandLineArgumentsToExamples $ withHandle config $ \h -> do
+    let formatter = fromMaybe specdoc (configFormatter config)
+        seed = (fromJust . configQuickCheckSeed) config
+        qcArgs = configQuickCheckArgs config
+
+    jobsSem <- newQSem =<< case configConcurrentJobs config of
       Nothing -> getDefaultConcurrentJobs
       Just maxJobs -> return maxJobs
 
-    useColor <- doesUseColor h c
+    useColor <- doesUseColor h config
 
-    filteredSpec <- filterSpecs c . applyDryRun c <$> runSpecM spec
+    filteredSpec <- filterSpecs config . applyDryRun config <$> runSpecM spec
 
     withHiddenCursor useColor h $
-      runFormatM useColor (configHtmlOutput c) (configPrintCpuTime c) seed h $ do
-        runFormatter jobsSem useColor h c formatter filteredSpec `finally_` do
-          failedFormatter formatter
+      runFormatM useColor (configDiff config) (configHtmlOutput config) (configPrintCpuTime config) seed h $ do
+        runFormatter jobsSem useColor h config formatter filteredSpec `finally_` do
+          Formatter.interpret $ failedFormatter formatter
 
-        footerFormatter formatter
+        Formatter.interpret $ footerFormatter formatter
 
-        -- dump failure report
-        xs <- map failureRecordPath <$> getFailMessages
-        liftIO $ writeFailureReport FailureReport {
-            failureReportSeed = seed
-          , failureReportMaxSuccess = QC.maxSuccess qcArgs
-          , failureReportMaxSize = QC.maxSize qcArgs
-          , failureReportMaxDiscardRatio = QC.maxDiscardRatio qcArgs
-          , failureReportPaths = xs
-          }
+        xs <- map failureRecordPath <$> Formatter.interpret getFailMessages
+        liftIO $ dumpFailureReport config seed qcArgs xs
 
-        Summary <$> getTotalCount <*> getFailCount
-  where
-    withHiddenCursor :: Bool -> Handle -> IO a -> IO a
-    withHiddenCursor useColor h
-      | useColor  = E.bracket_ (hHideCursor h) (hShowCursor h)
-      | otherwise = id
+        Summary <$> Formatter.interpret getTotalCount <*> Formatter.interpret getFailCount
 
-    doesUseColor :: Handle -> Config -> IO Bool
-    doesUseColor h c = case configColorMode c of
-      ColorAuto  -> (&&) <$> hIsTerminalDevice h <*> (not <$> isDumb)
-      ColorNever -> return False
-      ColorAlways -> return True
+dumpFailureReport :: Config -> Integer -> QC.Args -> [Path] -> IO ()
+dumpFailureReport config seed qcArgs xs = do
+  writeFailureReport config FailureReport {
+      failureReportSeed = seed
+    , failureReportMaxSuccess = QC.maxSuccess qcArgs
+    , failureReportMaxSize = QC.maxSize qcArgs
+    , failureReportMaxDiscardRatio = QC.maxDiscardRatio qcArgs
+    , failureReportPaths = xs
+    }
 
-    withHandle :: Config -> (Handle -> IO a) -> IO a
-    withHandle c action = case configOutputFile c of
-      Left h -> action h
-      Right path -> withFile path WriteMode action
+doNotLeakCommandLineArgumentsToExamples :: IO a -> IO a
+doNotLeakCommandLineArgumentsToExamples = withArgs []
+
+withHiddenCursor :: Bool -> Handle -> IO a -> IO a
+withHiddenCursor useColor h
+  | useColor  = E.bracket_ (hHideCursor h) (hShowCursor h)
+  | otherwise = id
+
+doesUseColor :: Handle -> Config -> IO Bool
+doesUseColor h c = case configColorMode c of
+  ColorAuto  -> (&&) <$> hIsTerminalDevice h <*> (not <$> isDumb)
+  ColorNever -> return False
+  ColorAlways -> return True
+
+withHandle :: Config -> (Handle -> IO a) -> IO a
+withHandle c action = case configOutputFile c of
+  Left h -> action h
+  Right path -> withFile path WriteMode action
+
+rerunAll :: Config -> Maybe FailureReport -> Summary -> Bool
+rerunAll _ Nothing _ = False
+rerunAll config (Just oldFailureReport) summary =
+     configRerunAllOnSuccess config
+  && configRerun config
+  && isSuccess summary
+  && (not . null) (failureReportPaths oldFailureReport)
 
 isDumb :: IO Bool
 isDumb = maybe False (== "dumb") <$> lookupEnv "TERM"
diff --git a/hspec-core/src/Test/Hspec/Core/Runner/Eval.hs b/hspec-core/src/Test/Hspec/Core/Runner/Eval.hs
--- a/hspec-core/src/Test/Hspec/Core/Runner/Eval.hs
+++ b/hspec-core/src/Test/Hspec/Core/Runner/Eval.hs
@@ -8,7 +8,7 @@
 module Test.Hspec.Core.Runner.Eval (runFormatter) where
 
 import           Prelude ()
-import           Test.Hspec.Compat
+import           Test.Hspec.Core.Compat
 
 import           Control.Monad (unless, when)
 import qualified Control.Exception as E
@@ -16,22 +16,22 @@
 import           System.IO (Handle)
 
 import           Control.Monad.IO.Class (liftIO)
-import           Control.DeepSeq (deepseq)
 import           Data.Time.Clock.POSIX
 
 import           Test.Hspec.Core.Util
 import           Test.Hspec.Core.Spec
-import           Test.Hspec.Config
-import           Test.Hspec.Core.Formatters
+import           Test.Hspec.Core.Config
+import           Test.Hspec.Core.Formatters hiding (FormatM)
 import           Test.Hspec.Core.Formatters.Internal
-import           Test.Hspec.Timer
+import qualified Test.Hspec.Core.Formatters.Internal as Formatter
+import           Test.Hspec.Core.Timer
 
 type EvalTree = Tree (ActionWith ()) (String, Maybe Location, ProgressCallback -> FormatResult -> IO (FormatM ()))
 
 -- | Evaluate all examples of a given spec and produce a report.
 runFormatter :: QSem -> Bool -> Handle -> Config -> Formatter -> [SpecTree ()] -> FormatM ()
 runFormatter jobsSem useColor h c formatter specs = do
-  headerFormatter formatter
+  Formatter.interpret $ headerFormatter formatter
   chan <- liftIO newChan
   reportProgress <- liftIO mkReportProgress
   run chan reportProgress c formatter (toEvalTree specs)
@@ -60,24 +60,24 @@
 
 type FormatResult = Either E.SomeException Result -> FormatM ()
 
-parallelize :: QSem -> Bool -> (ProgressCallback -> IO Result) -> ProgressCallback -> FormatResult -> IO (FormatM ())
+parallelize :: QSem -> Bool -> (ProgressCallback -> IO (Either E.SomeException Result)) -> ProgressCallback -> FormatResult -> IO (FormatM ())
 parallelize jobsSem isParallelizable e
   | isParallelizable = runParallel jobsSem e
   | otherwise = runSequentially e
 
-runSequentially :: (ProgressCallback -> IO Result) -> ProgressCallback -> FormatResult -> IO (FormatM ())
+runSequentially :: (ProgressCallback -> IO (Either E.SomeException Result)) -> ProgressCallback -> FormatResult -> IO (FormatM ())
 runSequentially e reportProgress formatResult = return $ do
-  result <- liftIO $ evalExample (e reportProgress)
+  result <- liftIO $ e reportProgress
   formatResult result
 
 data Report = ReportProgress Progress | ReportResult (Either E.SomeException Result)
 
-runParallel :: QSem -> (ProgressCallback -> IO Result) -> ProgressCallback -> FormatResult -> IO (FormatM ())
+runParallel :: QSem -> (ProgressCallback -> IO (Either E.SomeException Result)) -> ProgressCallback -> FormatResult -> IO (FormatM ())
 runParallel jobsSem e reportProgress formatResult = do
   mvar <- newEmptyMVar
   _ <- forkIO $ E.bracket_ (waitQSem jobsSem) (signalQSem jobsSem) $ do
     let progressCallback = replaceMVar mvar . ReportProgress
-    result <- evalExample (e progressCallback)
+    result <- e progressCallback
     replaceMVar mvar (ReportResult result)
   return $ evalReport mvar
   where
@@ -93,15 +93,6 @@
     replaceMVar :: MVar a -> a -> IO ()
     replaceMVar mvar p = tryTakeMVar mvar >> putMVar mvar p
 
-evalExample :: IO Result -> IO (Either E.SomeException Result)
-evalExample e = safeTry $ forceResult <$> e
-  where
-    forceResult :: Result -> Result
-    forceResult r = case r of
-      Success -> r
-      Pending m -> m `deepseq` r
-      Fail _ m -> m `deepseq` r
-
 data Message = Done | Run (FormatM ())
 
 run :: Chan Message -> (Path -> ProgressCallback) -> Config -> Formatter -> [EvalTree] -> FormatM ()
@@ -121,9 +112,9 @@
 
     queueSpec :: [String] -> EvalTree -> IO ()
     queueSpec rGroups (Node group xs) = do
-      defer (exampleGroupStarted formatter (reverse rGroups) group)
+      defer (Formatter.interpret $ exampleGroupStarted formatter (reverse rGroups) group)
       forM_ xs (queueSpec (group : rGroups))
-      defer (exampleGroupDone formatter)
+      defer (Formatter.interpret $ exampleGroupDone formatter)
     queueSpec rGroups (NodeWithCleanup action xs) = do
       forM_ xs (queueSpec rGroups)
       defer (runCleanup (action ()) (reverse rGroups, "afterAll-hook"))
@@ -138,22 +129,22 @@
 
         reportProgress = reportProgress_ path
 
-        formatResult :: Either E.SomeException Result -> FormatM ()
+        formatResult :: FormatResult
         formatResult result = do
           case result of
             Right Success -> do
               increaseSuccessCount
-              exampleSucceeded formatter path
+              Formatter.interpret $ exampleSucceeded formatter path
             Right (Pending reason) -> do
               increasePendingCount
-              examplePending formatter path reason
-            Right (Fail loc_ err) -> failed (loc_ <|> loc) path (Right err)
+              Formatter.interpret $ examplePending formatter path reason
+            Right (Failure loc_ err) -> failed (loc_ <|> loc) path (Right err)
             Left err         -> failed loc path (Left  err)
 
     failed loc path err = do
       increaseFailCount
       addFailMessage loc path err
-      exampleFailed formatter path err
+      Formatter.interpret $ exampleFailed formatter path err
 
 processMessages :: IO Message -> Bool -> FormatM ()
 processMessages getMessage fastFail = go
@@ -161,6 +152,6 @@
     go = liftIO getMessage >>= \m -> case m of
       Run action -> do
         action
-        fails <- getFailCount
+        fails <- Formatter.interpret getFailCount
         unless (fastFail && fails /= 0) go
       Done -> return ()
diff --git a/hspec-core/src/Test/Hspec/Core/Spec.hs b/hspec-core/src/Test/Hspec/Core/Spec.hs
--- a/hspec-core/src/Test/Hspec/Core/Spec.hs
+++ b/hspec-core/src/Test/Hspec/Core/Spec.hs
@@ -8,10 +8,16 @@
 module Test.Hspec.Core.Spec (
 
 -- * Defining a spec
-  describe
-, it
+  it
+, specify
+, describe
+, context
 , pending
 , pendingWith
+, xit
+, xspecify
+, xdescribe
+, xcontext
 , parallel
 
 -- * The @SpecM@ monad
@@ -30,6 +36,7 @@
 import           Test.Hspec.Expectations (Expectation)
 
 import           Test.Hspec.Core.Example
+import           Test.Hspec.Core.Hooks
 import           Test.Hspec.Core.Tree
 import           Test.Hspec.Core.Spec.Monad
 
@@ -37,6 +44,21 @@
 describe :: String -> SpecWith a -> SpecWith a
 describe label spec = runIO (runSpecM spec) >>= fromSpecList . return . specGroup label
 
+-- | @context@ is an alias for `describe`.
+context :: String -> SpecWith a -> SpecWith a
+context = describe
+
+-- |
+-- Changing `describe` to `xdescribe` marks all spec items of the corresponding subtree as pending.
+--
+-- This can be used to temporarily disable spec items.
+xdescribe :: String -> SpecWith a -> SpecWith a
+xdescribe label spec = before_ pending $ describe label spec
+
+-- | @xcontext@ is an alias for `xdescribe`.
+xcontext :: String -> SpecWith a -> SpecWith a
+xcontext = xdescribe
+
 -- | The @it@ function creates a spec item.
 --
 -- A spec item consists of:
@@ -51,12 +73,27 @@
 it :: (HasCallStack, Example a) => String -> a -> SpecWith (Arg a)
 it label action = fromSpecList [specItem label action]
 
+-- | @specify@ is an alias for `it`.
+specify :: (HasCallStack, Example a) => String -> a -> SpecWith (Arg a)
+specify = it
+
+-- |
+-- Changing `it` to `xit` marks the corresponding spec item as pending.
+--
+-- This can be used to temporarily disable a spec item.
+xit :: (HasCallStack, Example a) => String -> a -> SpecWith (Arg a)
+xit label action = before_ pending $ it label action
+
+-- | @xspecify@ is an alias for `xit`.
+xspecify :: (HasCallStack, Example a) => String -> a -> SpecWith (Arg a)
+xspecify = xit
+
 -- | `parallel` marks all spec items of the given spec to be safe for parallel
 -- evaluation.
 parallel :: SpecWith a -> SpecWith a
 parallel = mapSpecItem_ $ \item -> item {itemIsParallelizable = True}
 
--- | `pending` can be used to indicate that an example is /pending/.
+-- | `pending` can be used to mark a spec item as pending.
 --
 -- If you want to textually specify a behavior but do not have an example yet,
 -- use this:
@@ -69,6 +106,6 @@
 
 -- |
 -- `pendingWith` is similar to `pending`, but it takes an additional string
--- argument that can be used to specify the reason for why it's pending.
+-- argument that can be used to specify the reason for why the spec item is pending.
 pendingWith :: String -> Expectation
 pendingWith = E.throwIO . Pending . Just
diff --git a/hspec-core/src/Test/Hspec/Core/Spec/Monad.hs b/hspec-core/src/Test/Hspec/Core/Spec/Monad.hs
--- a/hspec-core/src/Test/Hspec/Core/Spec/Monad.hs
+++ b/hspec-core/src/Test/Hspec/Core/Spec/Monad.hs
@@ -14,7 +14,7 @@
 ) where
 
 import           Prelude ()
-import           Test.Hspec.Compat
+import           Test.Hspec.Core.Compat
 
 import           Control.Monad.Trans.Writer
 import           Control.Monad.IO.Class (liftIO)
diff --git a/hspec-core/src/Test/Hspec/Core/Timer.hs b/hspec-core/src/Test/Hspec/Core/Timer.hs
new file mode 100644
--- /dev/null
+++ b/hspec-core/src/Test/Hspec/Core/Timer.hs
@@ -0,0 +1,14 @@
+module Test.Hspec.Core.Timer where
+
+import           Data.IORef
+import           Data.Time.Clock.POSIX
+
+newTimer :: POSIXTime -> IO (IO Bool)
+newTimer delay = do
+  ref <- getPOSIXTime >>= newIORef
+  return $ do
+    t0 <- readIORef ref
+    t1 <- getPOSIXTime
+    if delay < t1 - t0
+      then writeIORef ref t1 >> return True
+      else return False
diff --git a/hspec-core/src/Test/Hspec/Core/Tree.hs b/hspec-core/src/Test/Hspec/Core/Tree.hs
--- a/hspec-core/src/Test/Hspec/Core/Tree.hs
+++ b/hspec-core/src/Test/Hspec/Core/Tree.hs
@@ -15,9 +15,10 @@
 ) where
 
 import           Data.CallStack
+import           Control.Exception
 
 import           Prelude ()
-import           Test.Hspec.Compat
+import           Test.Hspec.Core.Compat
 
 import           Test.Hspec.Core.Example
 
@@ -51,7 +52,7 @@
   -- parallel with other spec items
 , itemIsParallelizable :: Bool
   -- | Example for behavior
-, itemExample :: Params -> (ActionWith a -> IO ()) -> ProgressCallback -> IO Result
+, itemExample :: Params -> (ActionWith a -> IO ()) -> ProgressCallback -> IO (Either SomeException Result)
 }
 
 -- | The @specGroup@ function combines a list of specs into a larger spec.
@@ -64,7 +65,7 @@
 
 -- | The @specItem@ function creates a spec item.
 specItem :: (HasCallStack, Example a) => String -> a -> SpecTree (Arg a)
-specItem s e = Leaf $ Item requirement location False (evaluateExample e)
+specItem s e = Leaf $ Item requirement location False (safeEvaluateExample e)
   where
     requirement
       | null s = "(unspecified behavior)"
diff --git a/hspec-core/src/Test/Hspec/Core/Util.hs b/hspec-core/src/Test/Hspec/Core/Util.hs
--- a/hspec-core/src/Test/Hspec/Core/Util.hs
+++ b/hspec-core/src/Test/Hspec/Core/Util.hs
@@ -21,7 +21,7 @@
 import           Control.Exception
 import           Control.Concurrent.Async
 
-import           Test.Hspec.Compat (showType)
+import           Test.Hspec.Core.Compat (showType)
 
 -- |
 -- @pluralize count singular@ pluralizes the given @singular@ word unless given
@@ -46,7 +46,7 @@
 strip = dropWhile isSpace . reverse . dropWhile isSpace . reverse
 
 -- |
--- ensure that lines are not longer then given `n`, insert line breaks at word
+-- ensure that lines are not longer than given `n`, insert line breaks at word
 -- boundaries
 lineBreaksAt :: Int -> String -> [String]
 lineBreaksAt n input = case words input of
@@ -129,4 +129,13 @@
 -- occurs, the exception is returned instead.  Unlike `try` it is agnostic to
 -- asynchronous exceptions.
 safeTry :: IO a -> IO (Either SomeException a)
-safeTry action = withAsync (action >>= evaluate) waitCatch
+safeTry action = bracket runAction cancelAction waitForAction
+  where
+    runAction = async ((action >>= evaluate))
+    waitForAction = waitCatch
+    cancelAction a = do
+      cancel a
+      -- It is important to wait here to make sure all finalizers in action have
+      -- been run. Otherwise the main thread can exit before they have finished
+      -- and the finalizers are only partially run.
+      waitCatch a -- We use waitCatch to hide the ThreadKilled exception
diff --git a/hspec-core/src/Test/Hspec/FailureReport.hs b/hspec-core/src/Test/Hspec/FailureReport.hs
deleted file mode 100644
--- a/hspec-core/src/Test/Hspec/FailureReport.hs
+++ /dev/null
@@ -1,49 +0,0 @@
-{-# LANGUAGE CPP #-}
-module Test.Hspec.FailureReport (
-  FailureReport (..)
-, writeFailureReport
-, readFailureReport
-) where
-
-#ifndef __GHCJS__
-import           System.SetEnv
-import           Test.Hspec.Core.Util (safeTry)
-#endif
-import           System.IO
-import           Test.Hspec.Compat
-import           Test.Hspec.Core.Util (Path)
-
-data FailureReport = FailureReport {
-  failureReportSeed :: Integer
-, failureReportMaxSuccess :: Int
-, failureReportMaxSize :: Int
-, failureReportMaxDiscardRatio :: Int
-, failureReportPaths :: [Path]
-} deriving (Eq, Show, Read)
-
-writeFailureReport :: FailureReport -> IO ()
-#ifdef __GHCJS__
-writeFailureReport _ = return ()
-  -- ghcjs currently does not support setting environment variables
-  -- (https://github.com/ghcjs/ghcjs/issues/263). Since writing a failure report
-  -- into the environment is a non-essential feature we just disable this to be
-  -- able to run hspec test-suites with ghcjs at all. Should be reverted once
-  -- the issue is fixed.
-#else
-writeFailureReport x = do
-  -- on Windows this can throw an exception when the input is too large, hence
-  -- we use `safeTry` here
-  safeTry (setEnv "HSPEC_FAILURES" $ show x) >>= either onError return
-  where
-    onError err = do
-      hPutStrLn stderr ("WARNING: Could not write environment variable HSPEC_FAILURES (" ++ show err ++ ")")
-#endif
-
-readFailureReport :: IO (Maybe FailureReport)
-readFailureReport = do
-  mx <- lookupEnv "HSPEC_FAILURES"
-  case mx >>= readMaybe of
-    Nothing -> do
-      hPutStrLn stderr "WARNING: Could not read environment variable HSPEC_FAILURES; `--rerun' is ignored!"
-      return Nothing
-    x -> return x
diff --git a/hspec-core/src/Test/Hspec/Options.hs b/hspec-core/src/Test/Hspec/Options.hs
deleted file mode 100644
--- a/hspec-core/src/Test/Hspec/Options.hs
+++ /dev/null
@@ -1,198 +0,0 @@
-module Test.Hspec.Options (
-  Config(..)
-, ColorMode (..)
-, defaultConfig
-, filterOr
-, parseOptions
-) where
-
-import           Prelude ()
-import           Control.Monad
-import           Test.Hspec.Compat
-
-import           System.IO
-import           System.Exit
-import           System.Console.GetOpt
-
-import           Test.Hspec.Core.Formatters
-import           Test.Hspec.Core.Util
-import           Test.Hspec.Core.Example (Params(..), defaultParams)
-
-data Config = Config {
-  configDryRun :: Bool
-, configPrintCpuTime :: Bool
-, configFastFail :: Bool
-
--- |
--- A predicate that is used to filter the spec before it is run.  Only examples
--- that satisfy the predicate are run.
-, configRerun :: Bool
-, configFilterPredicate :: Maybe (Path -> Bool)
-, configSkipPredicate :: Maybe (Path -> Bool)
-, configQuickCheckSeed :: Maybe Integer
-, configQuickCheckMaxSuccess :: Maybe Int
-, configQuickCheckMaxDiscardRatio :: Maybe Int
-, configQuickCheckMaxSize :: Maybe Int
-, configSmallCheckDepth :: Int
-, configColorMode :: ColorMode
-, configFormatter :: Maybe Formatter
-, configHtmlOutput :: Bool
-, configOutputFile :: Either Handle FilePath
-, configConcurrentJobs :: Maybe Int
-}
-
-defaultConfig :: Config
-defaultConfig = Config {
-  configDryRun = False
-, configPrintCpuTime = False
-, configFastFail = False
-, configRerun = False
-, configFilterPredicate = Nothing
-, configSkipPredicate = Nothing
-, configQuickCheckSeed = Nothing
-, configQuickCheckMaxSuccess = Nothing
-, configQuickCheckMaxDiscardRatio = Nothing
-, configQuickCheckMaxSize = Nothing
-, configSmallCheckDepth = paramsSmallCheckDepth defaultParams
-, configColorMode = ColorAuto
-, configFormatter = Nothing
-, configHtmlOutput = False
-, configOutputFile = Left stdout
-, configConcurrentJobs = Nothing
-}
-
-filterOr :: Maybe (Path -> Bool) -> Maybe (Path -> Bool) -> Maybe (Path -> Bool)
-filterOr p1_ p2_ = case (p1_, p2_) of
-  (Just p1, Just p2) -> Just $ \path -> p1 path || p2 path
-  _ -> p1_ <|> p2_
-
-addMatch :: String -> Config -> Config
-addMatch s c = c {configFilterPredicate = Just (filterPredicate s) `filterOr` configFilterPredicate c}
-
-addSkip :: String -> Config -> Config
-addSkip s c = c {configSkipPredicate = Just (filterPredicate s) `filterOr` configSkipPredicate c}
-
-setDepth :: Int -> Config -> Config
-setDepth n c = c {configSmallCheckDepth = n}
-
-setMaxSuccess :: Int -> Config -> Config
-setMaxSuccess n c = c {configQuickCheckMaxSuccess = Just n}
-
-setMaxSize :: Int -> Config -> Config
-setMaxSize n c = c {configQuickCheckMaxSize = Just n}
-
-setMaxDiscardRatio :: Int -> Config -> Config
-setMaxDiscardRatio n c = c {configQuickCheckMaxDiscardRatio = Just n}
-
-setSeed :: Integer -> Config -> Config
-setSeed n c = c {configQuickCheckSeed = Just n}
-
-data ColorMode = ColorAuto | ColorNever | ColorAlways
-  deriving (Eq, Show)
-
-formatters :: [(String, Formatter)]
-formatters = [
-    ("specdoc", specdoc)
-  , ("progress", progress)
-  , ("failed-examples", failed_examples)
-  , ("silent", silent)
-  ]
-
-formatHelp :: String
-formatHelp = unlines (addLineBreaks "use a custom formatter; this can be one of:" ++ map (("   " ++) . fst) formatters)
-
-type Result = Either NoConfig Config
-
-data NoConfig = Help | InvalidArgument String String
-
-data Arg a = Arg {
-  _argumentName   :: String
-, _argumentParser :: String -> Maybe a
-, _argumentSetter :: a -> Config -> Config
-}
-
-mkOption :: [Char] -> String -> Arg a -> String -> OptDescr (Result -> Result)
-mkOption shortcut name (Arg argName parser setter) help = Option shortcut [name] (ReqArg arg argName) help
-  where
-    arg :: String -> Result -> Result
-    arg input x = x >>= \c -> case parser input of
-      Just n -> Right (setter n c)
-      Nothing -> Left (InvalidArgument name input)
-
-addLineBreaks :: String -> [String]
-addLineBreaks = lineBreaksAt 44
-
-options :: [OptDescr (Result -> Result)]
-options = [
-    Option   []  ["help"]             (NoArg (const $ Left Help))         (h "display this help and exit")
-  , mkOption "m"  "match"             (Arg "PATTERN" return addMatch)     (h "only run examples that match given PATTERN")
-  , mkOption []   "skip"              (Arg "PATTERN" return addSkip)      (h "skip examples that match given PATTERN")
-  , Option   []  ["color"]            (NoArg setColor)                    (h "colorize the output")
-  , Option   []  ["no-color"]         (NoArg setNoColor)                  (h "do not colorize the output")
-  , mkOption "f"  "format"            (Arg "FORMATTER" readFormatter setFormatter) formatHelp
-  , mkOption "o"  "out"               (Arg "FILE" return setOutputFile)   (h "write output to a file instead of STDOUT")
-  , mkOption []   "depth"             (Arg "N" readMaybe setDepth)        (h "maximum depth of generated test values for SmallCheck properties")
-  , mkOption "a"  "qc-max-success"    (Arg "N" readMaybe setMaxSuccess)   (h "maximum number of successful tests before a QuickCheck property succeeds")
-  , mkOption ""   "qc-max-size"       (Arg "N" readMaybe setMaxSize)      (h "size to use for the biggest test cases")
-  , mkOption ""   "qc-max-discard"    (Arg "N" readMaybe setMaxDiscardRatio) (h "maximum number of discarded tests per successful test before giving up")
-  , mkOption []   "seed"              (Arg "N" readMaybe setSeed)         (h "used seed for QuickCheck properties")
-  , Option   []  ["print-cpu-time"]   (NoArg setPrintCpuTime)             (h "include used CPU time in summary")
-  , Option   []  ["dry-run"]          (NoArg setDryRun)                   (h "pretend that everything passed; don't verify anything")
-  , Option   []  ["fail-fast"]        (NoArg setFastFail)                 (h "abort on first failure")
-  , Option   "r" ["rerun"]            (NoArg  setRerun)                   (h "rerun all examples that failed in the previously test run (only works in GHCi)")
-  , mkOption "j"  "jobs"              (Arg "N" readMaxJobs setMaxJobs)    (h "run at most N parallelizable tests simultaneously (default: number of available processors)")
-  ]
-  where
-    h = unlines . addLineBreaks
-
-    readFormatter :: String -> Maybe Formatter
-    readFormatter = (`lookup` formatters)
-
-    readMaxJobs :: String -> Maybe Int
-    readMaxJobs s = do
-      n <- readMaybe s
-      guard $ n > 0
-      return n
-
-    setFormatter :: Formatter -> Config -> Config
-    setFormatter f c = c {configFormatter = Just f}
-
-    setOutputFile :: String -> Config -> Config
-    setOutputFile file c = c {configOutputFile = Right file}
-
-    setMaxJobs :: Int -> Config -> Config
-    setMaxJobs n c = c {configConcurrentJobs = Just n}
-
-    setPrintCpuTime x = x >>= \c -> return c {configPrintCpuTime = True}
-    setDryRun       x = x >>= \c -> return c {configDryRun = True}
-    setFastFail     x = x >>= \c -> return c {configFastFail = True}
-    setRerun        x = x >>= \c -> return c {configRerun = True}
-    setNoColor      x = x >>= \c -> return c {configColorMode = ColorNever}
-    setColor        x = x >>= \c -> return c {configColorMode = ColorAlways}
-
-undocumentedOptions :: [OptDescr (Result -> Result)]
-undocumentedOptions = [
-    -- for compatibility with test-framework
-    mkOption [] "maximum-generated-tests" (Arg "NUMBER" readMaybe setMaxSuccess) "how many automated tests something like QuickCheck should try, by default"
-
-    -- undocumented for now, as we probably want to change this to produce a
-    -- standalone HTML report in the future
-  , Option []  ["html"]                    (NoArg setHtml)                    "produce HTML output"
-
-    -- now a noop
-  , Option "v" ["verbose"]                 (NoArg id)                         "do not suppress output to stdout when evaluating examples"
-  ]
-  where
-    setHtml :: Result -> Result
-    setHtml x = x >>= \c -> return c {configHtmlOutput = True}
-
-parseOptions :: Config -> String -> [String] -> Either (ExitCode, String) Config
-parseOptions c prog args = case getOpt Permute (options ++ undocumentedOptions) args of
-    (opts, [], []) -> case foldl' (flip id) (Right c) opts of
-        Left Help                         -> Left (ExitSuccess, usageInfo ("Usage: " ++ prog ++ " [OPTION]...\n\nOPTIONS") options)
-        Left (InvalidArgument flag value) -> tryHelp ("invalid argument `" ++ value ++ "' for `--" ++ flag ++ "'\n")
-        Right x -> Right x
-    (_, _, err:_)  -> tryHelp err
-    (_, arg:_, _)  -> tryHelp ("unexpected argument `" ++ arg ++ "'\n")
-  where
-    tryHelp msg = Left (ExitFailure 1, prog ++ ": " ++ msg ++ "Try `" ++ prog ++ " --help' for more information.\n")
diff --git a/hspec-core/src/Test/Hspec/Timer.hs b/hspec-core/src/Test/Hspec/Timer.hs
deleted file mode 100644
--- a/hspec-core/src/Test/Hspec/Timer.hs
+++ /dev/null
@@ -1,14 +0,0 @@
-module Test.Hspec.Timer where
-
-import           Data.IORef
-import           Data.Time.Clock.POSIX
-
-newTimer :: POSIXTime -> IO (IO Bool)
-newTimer delay = do
-  ref <- getPOSIXTime >>= newIORef
-  return $ do
-    t0 <- readIORef ref
-    t1 <- getPOSIXTime
-    if delay < t1 - t0
-      then writeIORef ref t1 >> return True
-      else return False
diff --git a/hspec-core/vendor/Data/Algorithm/Diff.hs b/hspec-core/vendor/Data/Algorithm/Diff.hs
new file mode 100644
--- /dev/null
+++ b/hspec-core/vendor/Data/Algorithm/Diff.hs
@@ -0,0 +1,114 @@
+{-# LANGUAGE DeriveFunctor #-}
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  Data.Algorithm.Diff
+-- Copyright   :  (c) Sterling Clover 2008-2011, Kevin Charter 2011
+-- License     :  BSD 3 Clause
+-- Maintainer  :  s.clover@gmail.com
+-- Stability   :  experimental
+-- Portability :  portable
+--
+-- This is an implementation of the O(ND) diff algorithm as described in
+-- \"An O(ND) Difference Algorithm and Its Variations (1986)\"
+-- <http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.4.6927>. It is O(mn) in space.
+-- The algorithm is the same one used by standared Unix diff.
+-----------------------------------------------------------------------------
+
+module Data.Algorithm.Diff
+    ( Diff(..)
+    -- * Comparing lists for differences
+    , getDiff
+    , getDiffBy
+
+    -- * Finding chunks of differences
+    , getGroupedDiff
+    , getGroupedDiffBy
+    ) where
+
+import Prelude hiding (pi)
+
+import Data.Array
+
+data DI = F | S | B deriving (Show, Eq)
+
+-- | A value is either from the 'First' list, the 'Second' or from 'Both'.
+-- 'Both' contains both the left and right values, in case you are using a form
+-- of equality that doesn't check all data (for example, if you are using a
+-- newtype to only perform equality on side of a tuple).
+data Diff a = First a | Second a | Both a a deriving (Show, Eq, Functor)
+
+data DL = DL {poi :: !Int, poj :: !Int, path::[DI]} deriving (Show, Eq)
+
+instance Ord DL
+        where x <= y = if poi x == poi y
+                then  poj x > poj y
+                else poi x <= poi y
+
+canDiag :: (a -> a -> Bool) -> [a] -> [a] -> Int -> Int -> Int -> Int -> Bool
+canDiag eq as bs lena lenb = \ i j ->
+   if i < lena && j < lenb then (arAs ! i) `eq` (arBs ! j) else False
+    where arAs = listArray (0,lena - 1) as
+          arBs = listArray (0,lenb - 1) bs
+
+dstep :: (Int -> Int -> Bool) -> [DL] -> [DL]
+dstep cd dls = hd:pairMaxes rst
+  where (hd:rst) = nextDLs dls
+        nextDLs [] = []
+        nextDLs (dl:rest) = dl':dl'':nextDLs rest
+          where dl'  = addsnake cd $ dl {poi=poi dl + 1, path=(F : pdl)}
+                dl'' = addsnake cd $ dl {poj=poj dl + 1, path=(S : pdl)}
+                pdl = path dl
+        pairMaxes [] = []
+        pairMaxes [x] = [x]
+        pairMaxes (x:y:rest) = max x y:pairMaxes rest
+
+addsnake :: (Int -> Int -> Bool) -> DL -> DL
+addsnake cd dl
+    | cd pi pj = addsnake cd $
+                 dl {poi = pi + 1, poj = pj + 1, path=(B : path dl)}
+    | otherwise   = dl
+    where pi = poi dl; pj = poj dl
+
+lcs :: (a -> a -> Bool) -> [a] -> [a] -> [DI]
+lcs eq as bs = path . head . dropWhile (\dl -> poi dl /= lena || poj dl /= lenb) .
+            concat . iterate (dstep cd) . (:[]) . addsnake cd $
+            DL {poi=0,poj=0,path=[]}
+            where cd = canDiag eq as bs lena lenb
+                  lena = length as; lenb = length bs
+
+-- | Takes two lists and returns a list of differences between them. This is
+-- 'getDiffBy' with '==' used as predicate.
+getDiff :: (Eq t) => [t] -> [t] -> [Diff t]
+getDiff = getDiffBy (==)
+
+-- | Takes two lists and returns a list of differences between them, grouped
+-- into chunks. This is 'getGroupedDiffBy' with '==' used as predicate.
+getGroupedDiff :: (Eq t) => [t] -> [t] -> [Diff [t]]
+getGroupedDiff = getGroupedDiffBy (==)
+
+-- | A form of 'getDiff' with no 'Eq' constraint. Instead, an equality predicate
+-- is taken as the first argument.
+getDiffBy :: (t -> t -> Bool) -> [t] -> [t] -> [Diff t]
+getDiffBy eq a b = markup a b . reverse $ lcs eq a b
+    where markup (x:xs)   ys   (F:ds) = First x  : markup xs ys ds
+          markup   xs   (y:ys) (S:ds) = Second y : markup xs ys ds
+          markup (x:xs) (y:ys) (B:ds) = Both x y : markup xs ys ds
+          markup _ _ _ = []
+
+getGroupedDiffBy :: (t -> t -> Bool) -> [t] -> [t] -> [Diff [t]]
+getGroupedDiffBy eq a b = go $ getDiffBy eq a b
+    where go (First x  : xs) = let (fs, rest) = goFirsts  xs in First  (x:fs)     : go rest
+          go (Second x : xs) = let (fs, rest) = goSeconds xs in Second (x:fs)     : go rest
+          go (Both x y : xs) = let (fs, rest) = goBoth    xs
+                                   (fxs, fys) = unzip fs
+                               in Both (x:fxs) (y:fys) : go rest
+          go [] = []
+
+          goFirsts  (First x  : xs) = let (fs, rest) = goFirsts  xs in (x:fs, rest)
+          goFirsts  xs = ([],xs)
+
+          goSeconds (Second x : xs) = let (fs, rest) = goSeconds xs in (x:fs, rest)
+          goSeconds xs = ([],xs)
+
+          goBoth    (Both x y : xs) = let (fs, rest) = goBoth xs    in ((x,y):fs, rest)
+          goBoth    xs = ([],xs)
diff --git a/hspec-discover/src/Test/Hspec/Discover/Run.hs b/hspec-discover/src/Test/Hspec/Discover/Run.hs
--- a/hspec-discover/src/Test/Hspec/Discover/Run.hs
+++ b/hspec-discover/src/Test/Hspec/Discover/Run.hs
@@ -48,7 +48,7 @@
         hPutStrLn stderr err
         exitFailure
       Right conf -> do
-        when (configNested conf) (hPutStrLn stderr "hspec-discover: WARNING - The `--nested' flag is deprecated and will be removed in a future release!")
+        when (configNested conf) (hPutStrLn stderr "hspec-discover: WARNING - The `--nested' option is deprecated and will be removed in a future release!")
         specs <- findSpecs src
         writeFile dst (mkSpecModule src conf specs)
     _ -> do
diff --git a/hspec-meta.cabal b/hspec-meta.cabal
--- a/hspec-meta.cabal
+++ b/hspec-meta.cabal
@@ -1,12 +1,12 @@
--- This file has been generated from package.yaml by hpack version 0.15.0.
+-- This file has been generated from package.yaml by hpack version 0.18.0.
 --
 -- see: https://github.com/sol/hpack
 
 name:             hspec-meta
-version:          2.3.2
+version:          2.4.4
 license:          MIT
 license-file:     LICENSE
-copyright:        (c) 2011-2015 Simon Hengel,
+copyright:        (c) 2011-2017 Simon Hengel,
                   (c) 2011-2012 Trystan Spangler,
                   (c) 2011 Greg Weber
 maintainer:       Simon Hengel <sol@typeful.net>
@@ -21,7 +21,7 @@
                   in-development version of Hspec.
 
 extra-source-files:
-    changelog
+    CHANGES.markdown
 
 source-repository head
   type: git
@@ -32,6 +32,7 @@
   hs-source-dirs:
       src
       hspec-core/src
+      hspec-core/vendor/
   build-depends:
       base == 4.*
     , transformers >= 0.2.2.0
@@ -46,6 +47,9 @@
     , time
     , async
     , call-stack
+    , array
+    , directory
+    , filepath
   exposed-modules:
       Test.Hspec.Meta
   other-modules:
@@ -56,23 +60,27 @@
       Test.Hspec.HUnit
       Test.Hspec.QuickCheck
       Test.Hspec.Runner
-      Test.Hspec.Compat
-      Test.Hspec.Config
+      Test.Hspec.Core.Compat
+      Test.Hspec.Core.Config
       Test.Hspec.Core.Example
+      Test.Hspec.Core.FailureReport
       Test.Hspec.Core.Formatters
+      Test.Hspec.Core.Formatters.Diff
+      Test.Hspec.Core.Formatters.Free
       Test.Hspec.Core.Formatters.Internal
+      Test.Hspec.Core.Formatters.Monad
       Test.Hspec.Core.Hooks
+      Test.Hspec.Core.Options
       Test.Hspec.Core.QuickCheck
       Test.Hspec.Core.QuickCheckUtil
       Test.Hspec.Core.Runner
       Test.Hspec.Core.Runner.Eval
       Test.Hspec.Core.Spec
       Test.Hspec.Core.Spec.Monad
+      Test.Hspec.Core.Timer
       Test.Hspec.Core.Tree
       Test.Hspec.Core.Util
-      Test.Hspec.FailureReport
-      Test.Hspec.Options
-      Test.Hspec.Timer
+      Data.Algorithm.Diff
       Paths_hspec_meta
   default-language: Haskell2010
 
@@ -96,6 +104,9 @@
     , time
     , async
     , call-stack
+    , array
+    , directory
+    , filepath
     , filepath
     , directory
   other-modules:
diff --git a/src/Test/Hspec.hs b/src/Test/Hspec.hs
--- a/src/Test/Hspec.hs
+++ b/src/Test/Hspec.hs
@@ -17,16 +17,28 @@
 , module Test.Hspec.Expectations
 
 -- * Defining a spec
-, describe
-, context
 , it
 , specify
+, describe
+, context
 , example
-, pending
-, pendingWith
 , parallel
 , runIO
 
+-- * Pending spec items
+-- |
+-- During a test run a /pending/ spec item is:
+--
+-- 1. not executed
+--
+-- 1. reported as \"pending\"
+, pending
+, pendingWith
+, xit
+, xspecify
+, xdescribe
+, xcontext
+
 -- * Hooks
 , ActionWith
 , before
@@ -65,11 +77,3 @@
 -- >   putStrLn
 example :: Expectation -> Expectation
 example = id
-
--- | @context@ is an alias for `describe`.
-context :: String -> SpecWith a -> SpecWith a
-context = describe
-
--- | @specify@ is an alias for `it`.
-specify :: Example a => String -> a -> SpecWith (Arg a)
-specify = it
