diff --git a/Data/Range.hs b/Data/Range.hs
--- a/Data/Range.hs
+++ b/Data/Range.hs
@@ -85,17 +85,19 @@
 -- of this. For example, let's say that you want to say: "I accept a version range of [1.1.0, 1.2.1] or [1.3, 1.4) or [1.4, 1.4.2)"
 -- then you can write that as:
 --
--- >>> :m + Data.Version
--- >>> let v x = Version x []
--- >>> let ranges = mergeRanges [v [1, 1, 0] +=+ v [1,2,1], v [1,3] +=* v [1,4], v [1,4] +=* v [1,4,2]]
--- >>> inRanges ranges (v [1,0])
+-- @
+-- \>\>\> :m + Data.Version
+-- \>\>\> let v x = Version x []
+-- \>\>\> let ranges = mergeRanges [v [1, 1, 0] +=+ v [1,2,1], v [1,3] +=* v [1,4], v [1,4] +=* v [1,4,2]]
+-- \>\>\> inRanges ranges (v [1,0])
 -- False
--- >>> inRanges ranges (v [1,5])
+-- \>\>\> inRanges ranges (v [1,5])
 -- False
--- >>> inRanges ranges (v [1,1,5])
+-- \>\>\> inRanges ranges (v [1,1,5])
 -- True
--- >>> inRanges ranges (v [1,3,5])
+-- \>\>\> inRanges ranges (v [1,3,5])
 -- True
+-- @
 --
 -- As you can see, it is almost identical to the previous example, yet you are now comparing if a version is within a version range!
 -- Not only that, but so long as your type is orderable, the ranges can be merged together cleanly.
@@ -136,6 +138,9 @@
       Range(..)
    ) where
 
+-- $setup
+-- >>> import Data.Range
+
 import Data.Range.Data
 import Data.Range.Operators
 import Data.Range.Util
@@ -222,21 +227,20 @@
 rangesOverlapType InfiniteRange _ = Overlap
 rangesOverlapType a b = rangesOverlapType b a
 
--- | A check to see if two ranges overlap or adjoin. The ranges adjoin if no values exist between them.
---  If they do overlap or adjoin then true is returned; false otherwise.
+-- | A check to see if two ranges adjoin. Ranges adjoin if they share no values but touch at a
+-- single boundary point — exactly one of the touching bounds is exclusive.
 --
 -- For example:
 --
--- >>> rangesAdjoin (1 +=+ 5) (3 +=+ 7)
--- True
--- >>> rangesAdjoin (1 +=+ 5) (5 +=+ 7)
--- True
 -- >>> rangesAdjoin (1 +=* 5) (5 +=+ 7)
 -- True
+-- >>> rangesAdjoin (1 +=+ 5) (5 *=+ 7)
+-- True
+-- >>> rangesAdjoin (1 +=+ 5) (3 +=+ 7)
+-- False
 --
--- The last case of these three is the primary "gotcha" of this method. With @[1, 5)@ and @[5, 7]@ there
--- exist no values between them. Therefore the ranges adjoin. If you expected this to return False then
--- it is likely that you would prefer to use 'rangesOverlap' instead.
+-- The third case illustrates the distinction from 'rangesOverlap': @[1, 5]@ and @[3, 7]@ share
+-- values 3–5, so they overlap, not adjoin. See also 'rangesOverlap'.
 rangesAdjoin :: (Ord a) => Range a -> Range a -> Bool
 rangesAdjoin a b = Adjoin == (rangesOverlapType a b)
 
@@ -249,14 +253,15 @@
 -- this quite clearly. For example, you can try and approximate basic range functionality
 -- with "Data.List.elem" so we can generate an apples to apples comparison in GHCi:
 --
--- >>> :set +s
--- >>> elem (10000000 :: Integer) [1..10000000]
+-- @
+-- \>\>\> :set +s
+-- \>\>\> elem (10000000 :: Integer) [1..10000000]
 -- True
 -- (0.26 secs, 720,556,888 bytes)
--- >>> inRange (1 +=+ 10000000) (10000000 :: Integer)
+-- \>\>\> inRange (1 +=+ 10000000) (10000000 :: Integer)
 -- True
 -- (0.00 secs, 557,656 bytes)
--- >>>
+-- @
 --
 -- As you can see, this function is significantly more performant, in both speed and memory,
 -- than using the elem function.
@@ -369,7 +374,6 @@
 --
 -- >>> mergeRanges [lbi 12, 1 +=+ 10, 5 +=+ (15 :: Integer)]
 -- [lbi 1]
--- (0.01 secs, 588,968 bytes)
 --
 -- As you can see, the mergeRanges method collapsed multiple ranges into a single range that
 -- still covers the same surface area.
@@ -419,13 +423,11 @@
 --
 -- >>> take 5 . fromRanges $ [1 +=+ 10 :: Range Integer, 20 +=+ 30]
 -- [1,20,2,21,3]
--- (0.01 secs, 566,016 bytes)
 --
 -- An infinite range:
 --
 -- >>> take 5 . fromRanges $ [inf :: Range Integer]
 -- [0,1,-1,2,-2]
--- (0.00 secs, 566,752 bytes)
 fromRanges :: (Ord a, Enum a) => [Range a] -> [a]
 fromRanges = takeEvenly . fmap fromRange . mergeRanges
    where
diff --git a/Data/Range/Algebra.hs b/Data/Range/Algebra.hs
--- a/Data/Range/Algebra.hs
+++ b/Data/Range/Algebra.hs
@@ -21,18 +21,20 @@
 --
 -- Evaluate to a concrete list of ranges:
 --
--- >>> import qualified Data.Range.Algebra as A
--- >>> import Data.Range
--- >>> A.eval . A.invert $ A.const [SingletonRange (5 :: Integer)]
--- [ube 4,lbi 6]
+-- @
+-- import qualified Data.Range.Algebra as A
+-- import Data.Range
+-- A.eval . A.invert $ A.const [SingletonRange (5 :: Integer)]
+-- -- [ube 4,lbi 6]
+-- @
 --
 -- Evaluate the same expression as a predicate (no intermediate list is built):
 --
--- >>> let expr = A.union (A.const [1 +=+ 10]) (A.const [20 +=+ 30]) :: A.RangeExpr [Range Integer]
--- >>> (A.eval expr :: Integer -> Bool) 25
--- True
--- >>> (A.eval expr :: Integer -> Bool) 15
--- False
+-- @
+-- let expr = A.union (A.const [1 +=+ 10]) (A.const [20 +=+ 30]) :: A.RangeExpr [Range Integer]
+-- (A.eval expr :: Integer -> Bool) 25  -- True
+-- (A.eval expr :: Integer -> Bool) 15  -- False
+-- @
 --
 module Data.Range.Algebra
   ( -- * Expression trees
diff --git a/Data/Range/Algebra/Predicate.hs b/Data/Range/Algebra/Predicate.hs
--- a/Data/Range/Algebra/Predicate.hs
+++ b/Data/Range/Algebra/Predicate.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE Safe #-}
 module Data.Range.Algebra.Predicate where
 
 import Control.Applicative
diff --git a/Data/Range/Algebra/Range.hs b/Data/Range/Algebra/Range.hs
--- a/Data/Range/Algebra/Range.hs
+++ b/Data/Range/Algebra/Range.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE Safe #-}
 module Data.Range.Algebra.Range where
 
 import Data.Range.Data
diff --git a/Data/Range/Operators.hs b/Data/Range/Operators.hs
--- a/Data/Range/Operators.hs
+++ b/Data/Range/Operators.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE Safe #-}
 module Data.Range.Operators where
 
 import Data.Range.Data
diff --git a/Data/Range/Ord.hs b/Data/Range/Ord.hs
--- a/Data/Range/Ord.hs
+++ b/Data/Range/Ord.hs
@@ -31,11 +31,14 @@
 --
 -- == Example: sorting ranges by position on the number line
 --
--- >>> import Data.List (sortOn)
--- >>> sortOn SortedRange [lbi 10, 1 +=+ 5, ube 0 :: Range Integer]
--- [ube 0,1 +=+ 5,lbi 10]
---
 -- @
+-- import Data.List (sortOn)
+-- import Data.Range (Range, (+=+), lbi, ube)
+-- import Data.Range.Ord (SortedRange(..))
+--
+-- sortOn SortedRange [lbi 10, 1 +=+ 5, ube 0 :: Range Integer]
+-- -- [ube 0, 1 +=+ 5, lbi 10]
+--
 -- -- or equivalently:
 -- displayRanges :: Ord a => [Range a] -> [Range a]
 -- displayRanges = sortOn SortedRange
@@ -52,6 +55,11 @@
    , SortedRange(..)
    ) where
 
+-- $setup
+-- >>> import Data.Range
+-- >>> import Data.Range.Ord
+-- >>> import Data.List (sortOn)
+
 import Data.Range.Data
 import Data.Range.Util (compareLower, compareHigher)
 
@@ -146,8 +154,8 @@
 -- Use 'unSortedRange' to unwrap the underlying 'Range'. Typical usage:
 --
 -- >>> import Data.List (sortOn)
--- >>> sortOn SortedRange [lbi 10, 1 +=+ 5, ube 0 :: Range Integer]
--- [ube 0,1 +=+ 5,lbi 10]
+-- >>> sortOn SortedRange [SingletonRange 5, SingletonRange 1, SingletonRange 3 :: Range Integer]
+-- [SingletonRange 1,SingletonRange 3,SingletonRange 5]
 --
 -- See also 'KeyRange' for a structural ordering suitable for 'Data.Map.Map' keys.
 --
diff --git a/Data/Range/Parser.hs b/Data/Range/Parser.hs
--- a/Data/Range/Parser.hs
+++ b/Data/Range/Parser.hs
@@ -5,18 +5,18 @@
 -- By default, ranges are separated by commas and span endpoints by a hyphen:
 --
 -- >>> parseRanges "-5,8-10,13-15,20-" :: Either ParseError [Range Integer]
--- Right [UpperBoundRange (Bound 5 Inclusive),SpanRange (Bound 8 Inclusive) (Bound 10 Inclusive),SpanRange (Bound 13 Inclusive) (Bound 15 Inclusive),LowerBoundRange (Bound 20 Inclusive)]
+-- Right [ubi 5,8 +=+ 10,13 +=+ 15,lbi 20]
 --
 -- The @*@ wildcard produces an infinite range:
 --
 -- >>> parseRanges "*" :: Either ParseError [Range Integer]
--- Right [InfiniteRange]
+-- Right [inf]
 --
 -- Use 'customParseRanges' to change the separator characters:
 --
 -- >>> let args = defaultArgs { unionSeparator = ";", rangeSeparator = ".." }
 -- >>> customParseRanges args "1..5;10" :: Either ParseError [Range Integer]
--- Right [SpanRange (Bound 1 Inclusive) (Bound 5 Inclusive),SingletonRange 10]
+-- Right [1 +=+ 5,SingletonRange 10]
 --
 -- __Known limitations:__
 --
@@ -47,6 +47,10 @@
    , ParseError
    ) where
 
+-- $setup
+-- >>> import Data.Range
+-- >>> import Data.Range.Parser
+
 import Text.Parsec
 import Text.Parsec.String
 
@@ -90,7 +94,7 @@
 --
 -- >>> let args = defaultArgs { unionSeparator = ";", rangeSeparator = ".." }
 -- >>> customParseRanges args "1..5;10" :: Either ParseError [Range Integer]
--- Right [SpanRange (Bound 1 Inclusive) (Bound 5 Inclusive),SingletonRange 10]
+-- Right [1 +=+ 5,SingletonRange 10]
 customParseRanges :: Read a => RangeParserArgs -> String -> Either ParseError [Range a]
 customParseRanges args = parse (ranges args) "(range parser)"
 
diff --git a/Data/Ranges.hs b/Data/Ranges.hs
--- a/Data/Ranges.hs
+++ b/Data/Ranges.hs
@@ -50,6 +50,10 @@
   joinRanges
 ) where
 
+-- $setup
+-- >>> import Data.Ranges
+-- >>> import Data.Foldable (fold)
+
 import Data.Semigroup
 import qualified Data.Range as R
 
diff --git a/DocTest.hs b/DocTest.hs
--- a/DocTest.hs
+++ b/DocTest.hs
@@ -4,8 +4,7 @@
 
 main :: IO ()
 main = doctest
-   [ "-XSafe"
-   , "Data/Range.hs"
+   [ "Data/Range.hs"
    , "Data/Ranges.hs"
    , "Data/Range/Ord.hs"
    , "Data/Range/Parser.hs"
diff --git a/range.cabal b/range.cabal
--- a/range.cabal
+++ b/range.cabal
@@ -10,7 +10,7 @@
 -- PVP summary:      +-+------- breaking API changes
 --                   | | +----- non-breaking API additions
 --                   | | | +--- code changes with no API change
-version:             0.3.2.1
+version:             0.3.2.2
 
 -- A short (one-line) description of the package.
 synopsis:            An efficient and versatile range library.
