diff --git a/Data/Function.hs b/Data/Function.hs
--- a/Data/Function.hs
+++ b/Data/Function.hs
@@ -39,20 +39,44 @@
 -- | @'fix' f@ is the least fixed point of the function @f@,
 -- i.e. the least defined @x@ such that @f x = x@.
 --
--- For example, we can write the factorial function using direct recursion as
+-- When @f@ is strict, this means that because, by the definition of strictness,
+-- @f &#x22a5; = &#x22a5;@ and such the least defined fixed point of any strict function is @&#x22a5;@.
 --
+-- ==== __Examples__
+--
+-- We can write the factorial function using direct recursion as
+--
 -- >>> let fac n = if n <= 1 then 1 else n * fac (n-1) in fac 5
 -- 120
 --
 -- This uses the fact that Haskell’s @let@ introduces recursive bindings. We can
 -- rewrite this definition using 'fix',
 --
--- >>> fix (\rec n -> if n <= 1 then 1 else n * rec (n-1)) 5
--- 120
---
 -- Instead of making a recursive call, we introduce a dummy parameter @rec@;
 -- when used within 'fix', this parameter then refers to 'fix'’s argument, hence
 -- the recursion is reintroduced.
+--
+-- >>> fix (\rec n -> if n <= 1 then 1 else n * rec (n-1)) 5
+-- 120
+--
+-- Using 'fix', we can implement versions of 'Data.List.repeat' as @'fix' '.' '(:)'@
+-- and 'Data.List.cycle' as @'fix' '.' '(++)'@
+--
+-- >>> take 10 $ fix (0:)
+-- [0,0,0,0,0,0,0,0,0,0]
+--
+-- >>> map (fix (\rec n -> if n < 2 then n else rec (n - 1) + rec (n - 2))) [1..10]
+-- [1,1,2,3,5,8,13,21,34,55]
+--
+-- ==== __Implementation Details__
+--
+-- The current implementation of 'fix' uses structural sharing
+--
+-- @'fix' f = let x = f x in x@
+--
+-- A more straightforward but non-sharing version would look like
+--
+-- @'fix' f = f ('fix' f)@
 fix :: (a -> a) -> a
 fix f = let x = f x in x
 
@@ -60,12 +84,21 @@
 -- unary function @u@ to two arguments @x@ and @y@. From the opposite
 -- perspective, it transforms two inputs and combines the outputs.
 --
--- @((+) \``on`\` f) x y = f x + f y@
+-- @(op \``on`\` f) x y = f x \``op`\` f y@
 --
--- Typical usage: @'Data.List.sortBy' ('Prelude.compare' \`on\` 'Prelude.fst')@.
+-- ==== __Examples__
 --
--- Algebraic properties:
+-- >>> sortBy (compare `on` length) [[0, 1, 2], [0, 1], [], [0]]
+-- [[],[0],[0,1],[0,1,2]]
 --
+-- >>> ((+) `on` length) [1, 2, 3] [-1]
+-- 4
+--
+-- >>> ((,) `on` (*2)) 2 3
+-- (4,6)
+--
+-- ==== __Algebraic properties__
+--
 -- * @(*) \`on\` 'id' = (*) -- (if (*) &#x2209; {&#x22a5;, 'const' &#x22a5;})@
 --
 -- * @((*) \`on\` f) \`on\` g = (*) \`on\` (f . g)@
@@ -118,9 +151,19 @@
 -- convenience.  Its precedence is one higher than that of the forward
 -- application operator '$', which allows '&' to be nested in '$'.
 --
+--
+-- This is a version of @'flip' 'id'@, where 'id' is specialized from @a -> a@ to @(a -> b) -> (a -> b)@
+-- which by the associativity of @(->)@ is @(a -> b) -> a -> b@.
+-- flipping this yields @a -> (a -> b) -> b@ which is the type signature of '&'
+--
+-- ==== __Examples__
+--
 -- >>> 5 & (+1) & show
 -- "6"
 --
+-- >>> sqrt $ [1 / n^2 | n <- [1..1000]] & sum & (*6)
+-- 3.1406380562059946
+--
 -- @since 4.8.0.0
 (&) :: forall r a (b :: TYPE r). a -> (a -> b) -> b
 x & f = f x
@@ -130,7 +173,15 @@
 --
 -- It is equivalent to @'flip' ('Data.Bool.bool' 'id')@.
 --
--- Algebraic properties:
+-- ==== __Examples__
+--
+-- >>> map (\x -> applyWhen (odd x) (*2) x) [1..10]
+-- [2,2,6,4,10,6,14,8,18,10]
+--
+-- >>> map (\x -> applyWhen (length x > 6) ((++ "...") . take 3) x) ["Hi!", "This is amazing", "Hope you're doing well today!", ":D"]
+-- ["Hi!","Thi...","Hop...",":D"]
+--
+-- ==== __Algebraic properties__
 --
 -- * @applyWhen 'True' = 'id'@
 --
diff --git a/Data/List.hs b/Data/List.hs
--- a/Data/List.hs
+++ b/Data/List.hs
@@ -227,15 +227,21 @@
 -- the elements of the first list occur, in order, in the second. The
 -- elements do not have to occur consecutively.
 --
--- @'isSubsequenceOf' x y@ is equivalent to @'elem' x ('subsequences' y)@.
+-- @'isSubsequenceOf' x y@ is equivalent to @x \`'elem'\` ('subsequences' y)@.
 --
+-- Note: 'isSubsequenceOf' is often used in infix form.
+--
 -- @since 4.8.0.0
 --
--- >>> isSubsequenceOf "GHC" "The Glorious Haskell Compiler"
+-- ==== __Examples__
+--
+-- >>> "GHC" `isSubsequenceOf` "The Glorious Haskell Compiler"
 -- True
--- >>> isSubsequenceOf ['a','d'..'z'] ['a'..'z']
+--
+-- >>> ['a','d'..'z'] `isSubsequenceOf` ['a'..'z']
 -- True
--- >>> isSubsequenceOf [1..10] [10,9..0]
+--
+-- >>> [1..10] `isSubsequenceOf` [10,9..0]
 -- False
 --
 -- For the result to be 'True', the first list must be finite;
@@ -243,11 +249,12 @@
 --
 -- >>> [0,2..10] `isSubsequenceOf` [0..]
 -- True
+--
 -- >>> [0..] `isSubsequenceOf` [0,2..10]
 -- False
+--
 -- >>> [0,2..] `isSubsequenceOf` [0..]
 -- * Hangs forever*
---
 isSubsequenceOf :: (Eq a) => [a] -> [a] -> Bool
 isSubsequenceOf []    _                    = True
 isSubsequenceOf _     []                   = False
diff --git a/Data/Monoid.hs b/Data/Monoid.hs
--- a/Data/Monoid.hs
+++ b/Data/Monoid.hs
@@ -127,14 +127,18 @@
 -- @'First' a@ is isomorphic to @'Alt' 'Maybe' a@, but precedes it
 -- historically.
 --
--- >>> getFirst (First (Just "hello") <> First Nothing <> First (Just "world"))
--- Just "hello"
---
 -- Beware that @Data.Monoid.@'First' is different from
 -- @Data.Semigroup.@'Data.Semigroup.First'. The former returns the first non-'Nothing',
 -- so @Data.Monoid.First Nothing <> x = x@. The latter simply returns the first value,
 -- thus @Data.Semigroup.First Nothing <> x = Data.Semigroup.First Nothing@.
 --
+-- ==== __Examples__
+--
+-- >>> First (Just "hello") <> First Nothing <> First (Just "world")
+-- First {getFirst = Just "hello"}
+--
+-- >>> First Nothing <> mempty
+-- First {getFirst = Nothing}
 newtype First a = First { getFirst :: Maybe a }
         deriving ( Eq          -- ^ @since 2.01
                  , Ord         -- ^ @since 2.01
@@ -162,14 +166,17 @@
 -- @'Last' a@ is isomorphic to @'Dual' ('First' a)@, and thus to
 -- @'Dual' ('Alt' 'Maybe' a)@
 --
--- >>> getLast (Last (Just "hello") <> Last Nothing <> Last (Just "world"))
--- Just "world"
---
--- Beware that @Data.Monoid.@'Last' is different from
 -- @Data.Semigroup.@'Data.Semigroup.Last'. The former returns the last non-'Nothing',
 -- so @x <> Data.Monoid.Last Nothing = x@. The latter simply returns the last value,
 -- thus @x <> Data.Semigroup.Last Nothing = Data.Semigroup.Last Nothing@.
 --
+-- ==== __Examples__
+--
+-- >>> Last (Just "hello") <> Last Nothing <> Last (Just "world")
+-- Last {getLast = Just "world"}
+--
+-- >>> Last Nothing <> mempty
+-- Last {getLast = Nothing}
 newtype Last a = Last { getLast :: Maybe a }
         deriving ( Eq          -- ^ @since 2.01
                  , Ord         -- ^ @since 2.01
@@ -194,6 +201,14 @@
 
 -- | This data type witnesses the lifting of a 'Monoid' into an
 -- 'Applicative' pointwise.
+--
+-- ==== __Examples__
+--
+-- >>> Ap (Just [1, 2, 3]) <> Ap Nothing
+-- Ap {getAp = Nothing}
+--
+-- >>> Ap [Sum 10, Sum 20] <> Ap [Sum 1, Sum 2]
+-- Ap {getAp = [Sum {getSum = 11},Sum {getSum = 12},Sum {getSum = 21},Sum {getSum = 22}]}
 --
 -- @since 4.12.0.0
 newtype Ap f a = Ap { getAp :: f a }
diff --git a/Data/OldList.hs b/Data/OldList.hs
--- a/Data/OldList.hs
+++ b/Data/OldList.hs
@@ -230,13 +230,9 @@
 -- List functions
 
 -- | The 'dropWhileEnd' function drops the largest suffix of a list
--- in which the given predicate holds for all elements.  For example:
+-- in which the given predicate holds for all elements.
 --
--- >>> dropWhileEnd isSpace "foo\n"
--- "foo"
--- >>> dropWhileEnd isSpace "foo bar"
--- "foo bar"
--- > dropWhileEnd isSpace ("foo\n" ++ undefined) == "foo" ++ undefined
+-- ==== __Laziness__
 --
 -- This function is lazy in spine, but strict in elements,
 -- which makes it different from 'reverse' '.' 'dropWhile' @p@ '.' 'reverse',
@@ -244,6 +240,7 @@
 --
 -- >>> take 1 (dropWhileEnd (< 0) (1 : undefined))
 -- [1]
+--
 -- >>> take 1 (reverse $ dropWhile (< 0) $ reverse (1 : undefined))
 -- *** Exception: Prelude.undefined
 --
@@ -251,9 +248,20 @@
 --
 -- >>> last (dropWhileEnd (< 0) [undefined, 1])
 -- *** Exception: Prelude.undefined
+--
 -- >>> last (reverse $ dropWhile (< 0) $ reverse [undefined, 1])
 -- 1
 --
+-- ==== __Examples__
+--
+-- >>> dropWhileEnd isSpace "foo\n"
+-- "foo"
+--
+-- >>> dropWhileEnd isSpace "foo bar"
+-- "foo bar"
+-- >>> dropWhileEnd (> 10) [1..20]
+-- [1,2,3,4,5,6,7,8,9,10]
+--
 -- @since 4.5.0.0
 dropWhileEnd :: (a -> Bool) -> [a] -> [a]
 dropWhileEnd p = foldr (\x xs -> if p x && null xs then [] else x : xs) []
@@ -262,6 +270,8 @@
 -- prefix from a list. It returns 'Nothing' if the list did not start with the
 -- prefix given, or 'Just' the list after the prefix, if it does.
 --
+-- ===== __Examples__
+--
 -- >>> stripPrefix "foo" "foobar"
 -- Just "bar"
 --
@@ -284,16 +294,29 @@
 -- or 'Nothing' if there is no such element.
 -- For the result to be 'Nothing', the list must be finite.
 --
+-- ==== __Examples__
+--
 -- >>> elemIndex 4 [0..]
 -- Just 4
+--
+-- >>> elemIndex 'o' "haskell"
+-- Nothing
+--
+-- >>> elemIndex 0 [1..]
+-- * hangs forever *
 elemIndex      :: Eq a => a -> [a] -> Maybe Int
 elemIndex x xs = findIndex (x==) xs -- arity 2 so that we don't get a PAP; #21345
 
 -- | The 'elemIndices' function extends 'elemIndex', by returning the
 -- indices of all elements equal to the query element, in ascending order.
 --
+-- ==== __Examples__
+--
 -- >>> elemIndices 'o' "Hello World"
 -- [4,7]
+--
+-- >>> elemIndices 1 [1, 2, 3, 1, 2, 3]
+-- [0,3]
 elemIndices      :: Eq a => a -> [a] -> [Int]
 elemIndices x xs = findIndices (x==) xs -- arity 2 so that we don't get a PAP; #21345
 
@@ -302,11 +325,16 @@
 -- there is no such element.
 -- For the result to be 'Nothing', the list must be finite.
 --
+-- ==== __Examples__
+--
 -- >>> find (> 4) [1..]
 -- Just 5
 --
 -- >>> find (< 0) [1..10]
 -- Nothing
+--
+-- >>> find ('a' `elem`) ["john", "marcus", "paul"]
+-- Just "marcus"
 find            :: (a -> Bool) -> [a] -> Maybe a
 find p          = listToMaybe . filter p
 
@@ -315,16 +343,32 @@
 -- or 'Nothing' if there is no such element.
 -- For the result to be 'Nothing', the list must be finite.
 --
+-- ==== __Examples__
+--
 -- >>> findIndex isSpace "Hello World!"
 -- Just 5
+--
+-- >>> findIndex odd [0, 2, 4, 6]
+-- Nothing
+--
+-- >>> findIndex even [1..]
+-- Just 1
+--
+-- >>> findIndex odd [0, 2 ..]
+-- * hangs forever *
 findIndex       :: (a -> Bool) -> [a] -> Maybe Int
 findIndex p     = listToMaybe . findIndices p
 
 -- | The 'findIndices' function extends 'findIndex', by returning the
 -- indices of all elements satisfying the predicate, in ascending order.
 --
+-- ==== __Examples__
+--
 -- >>> findIndices (`elem` "aeiou") "Hello World!"
 -- [1,4,7]
+--
+-- >>> findIndices (\l -> length l > 3) ["a", "bcde", "fgh", "ijklmnop"]
+-- [1,3]
 findIndices      :: (a -> Bool) -> [a] -> [Int]
 #if defined(USE_REPORT_PRELUDE)
 findIndices p xs = [ i | (x,i) <- zip xs [0..], p x]
@@ -342,8 +386,11 @@
 -- | \(\mathcal{O}(\min(m,n))\). The 'isPrefixOf' function takes two lists and
 -- returns 'True' iff the first list is a prefix of the second.
 --
+-- ==== __Examples__
+--
 -- >>> "Hello" `isPrefixOf` "Hello World!"
 -- True
+--
 -- >>> "Hello" `isPrefixOf` "Wello Horld!"
 -- False
 --
@@ -352,10 +399,13 @@
 --
 -- >>> [0..] `isPrefixOf` [1..]
 -- False
+--
 -- >>> [0..] `isPrefixOf` [0..99]
 -- False
+--
 -- >>> [0..99] `isPrefixOf` [0..]
 -- True
+--
 -- >>> [0..] `isPrefixOf` [0..]
 -- * Hangs forever *
 --
@@ -372,8 +422,11 @@
 -- | The 'isSuffixOf' function takes two lists and returns 'True' iff
 -- the first list is a suffix of the second.
 --
+-- ==== __Examples__
+--
 -- >>> "ld!" `isSuffixOf` "Hello World!"
 -- True
+--
 -- >>> "World" `isSuffixOf` "Hello World!"
 -- False
 --
@@ -381,6 +434,7 @@
 --
 -- >>> [0..] `isSuffixOf` [0..99]
 -- False
+--
 -- >>> [0..99] `isSuffixOf` [0..]
 -- * Hangs forever *
 --
@@ -423,8 +477,11 @@
 -- iff the first list is contained, wholly and intact,
 -- anywhere within the second.
 --
+-- ==== __Examples__
+--
 -- >>> isInfixOf "Haskell" "I really like Haskell."
 -- True
+--
 -- >>> isInfixOf "Ial" "I really like Haskell."
 -- False
 --
@@ -433,11 +490,12 @@
 --
 -- >>> [20..50] `isInfixOf` [0..]
 -- True
+--
 -- >>> [0..] `isInfixOf` [20..50]
 -- False
+--
 -- >>> [0..] `isInfixOf` [0..]
 -- * Hangs forever *
---
 isInfixOf               :: (Eq a) => [a] -> [a] -> Bool
 isInfixOf needle haystack = any (isPrefixOf needle) (tails haystack)
 
@@ -446,8 +504,6 @@
 -- name 'nub' means \`essence\'.) It is a special case of 'nubBy', which allows
 -- the programmer to supply their own equality test.
 --
--- >>> nub [1,2,3,4,3,2,1,2,4,3,5]
--- [1,2,3,4,5]
 --
 -- If there exists @instance Ord a@, it's faster to use `nubOrd` from the `containers` package
 -- ([link to the latest online documentation](https://hackage.haskell.org/package/containers/docs/Data-Containers-ListUtils.html#v:nubOrd)),
@@ -458,17 +514,31 @@
 -- 'map' @Data.List.NonEmpty.@'Data.List.NonEmpty.head' . @Data.List.NonEmpty.@'Data.List.NonEmpty.group' . 'sort',
 -- which takes \(\mathcal{O}(n \log n)\) time, requires @instance Ord a@ and doesn't
 -- preserve the order.
-
 --
+-- ==== __Examples__
+--
+-- >>> nub [1,2,3,4,3,2,1,2,4,3,5]
+-- [1,2,3,4,5]
+--
+-- >>> nub "hello, world!"
+-- "helo, wrd!"
 nub                     :: (Eq a) => [a] -> [a]
 nub                     =  nubBy (==)
 
 -- | The 'nubBy' function behaves just like 'nub', except it uses a
--- user-supplied equality predicate instead of the overloaded '=='
+-- user-supplied equality predicate instead of the overloaded '(==)'
 -- function.
 --
+-- ==== __Examples__
+--
 -- >>> nubBy (\x y -> mod x 3 == mod y 3) [1,2,4,5,6]
 -- [1,2,6]
+--
+-- >>> nubBy (/=) [2, 7, 1, 8, 2, 8, 1, 8, 2, 8]
+-- [2,2,2]
+--
+-- >>> nubBy (>) [1, 2, 3, 2, 1, 5, 4, 5, 3, 2]
+-- [1,2,3,5,5]
 nubBy                   :: (a -> a -> Bool) -> [a] -> [a]
 #if defined(USE_REPORT_PRELUDE)
 nubBy eq []             =  []
@@ -496,21 +566,31 @@
 
 
 -- | \(\mathcal{O}(n)\). 'delete' @x@ removes the first occurrence of @x@ from
--- its list argument. For example,
+-- its list argument.
 --
+-- It is a special case of 'deleteBy', which allows the programmer to
+-- supply their own equality test.
+--
+-- ==== __Examples__
+--
 -- >>> delete 'a' "banana"
 -- "bnana"
 --
--- It is a special case of 'deleteBy', which allows the programmer to
--- supply their own equality test.
+-- >>> delete "not" ["haskell", "is", "not", "awesome"]
+-- ["haskell","is","awesome"]
 delete                  :: (Eq a) => a -> [a] -> [a]
 delete                  =  deleteBy (==)
 
 -- | \(\mathcal{O}(n)\). The 'deleteBy' function behaves like 'delete', but
 -- takes a user-supplied equality predicate.
 --
+-- ==== __Examples__
+--
 -- >>> deleteBy (<=) 4 [1..10]
 -- [1,2,3,5,6,7,8,9,10]
+--
+-- >>> deleteBy (/=) 5 [5, 5, 4, 3, 5, 2]
+-- [5,5,3,5,2]
 deleteBy                :: (a -> a -> Bool) -> a -> [a] -> [a]
 deleteBy _  _ []        = []
 deleteBy eq x (y:ys)    = if x `eq` y then ys else y : deleteBy eq x ys
@@ -520,27 +600,30 @@
 -- @ys@ in turn (if any) has been removed from @xs@.  Thus
 -- @(xs ++ ys) \\\\ xs == ys@.
 --
--- >>> "Hello World!" \\ "ell W"
--- "Hoorld!"
---
 -- It is a special case of 'deleteFirstsBy', which allows the programmer
 -- to supply their own equality test.
 --
+-- ==== __Examples__
+--
+-- >>> "Hello World!" \\ "ell W"
+-- "Hoorld!"
+--
 -- The second list must be finite, but the first may be infinite.
 --
 -- >>> take 5 ([0..] \\ [2..4])
 -- [0,1,5,6,7]
+--
 -- >>> take 5 ([0..] \\ [2..])
 -- * Hangs forever *
---
 (\\)                    :: (Eq a) => [a] -> [a] -> [a]
 (\\)                    =  foldl (flip delete)
 
 -- | The 'union' function returns the list union of the two lists.
 -- It is a special case of 'unionBy', which allows the programmer to supply
 -- their own equality test.
--- For example,
 --
+-- ==== __Examples__
+--
 -- >>> "dog" `union` "cow"
 -- "dogcw"
 --
@@ -548,7 +631,7 @@
 -- will be used. If the second list contains equal elements, only the first one
 -- will be retained:
 --
--- >>> import Data.Semigroup
+-- >>> import Data.Semigroup(Arg(..))
 -- >>> union [Arg () "dog"] [Arg () "cow"]
 -- [Arg () "dog"]
 -- >>> union [] [Arg () "dog", Arg () "cow"]
@@ -564,20 +647,31 @@
 --
 -- 'union' is productive even if both arguments are infinite.
 --
+-- >>> [0, 2 ..] `union` [1, 3 ..]
+-- [0,2,4,6,8,10,12..
 union                   :: (Eq a) => [a] -> [a] -> [a]
 union                   = unionBy (==)
 
 -- | The 'unionBy' function is the non-overloaded version of 'union'.
 -- Both arguments may be infinite.
 --
+-- ==== __Examples__
+--
+-- >>> unionBy (>) [3, 4, 5] [1, 2, 3, 4, 5, 6]
+-- [3,4,5,4,5,6]
+--
+-- >>> import Data.Semigroup (Arg(..))
+-- >>> unionBy (/=) [Arg () "Saul"] [Arg () "Kim"]
+-- [Arg () "Saul", Arg () "Kim"]
 unionBy                 :: (a -> a -> Bool) -> [a] -> [a] -> [a]
 unionBy eq xs ys        =  xs ++ foldl (flip (deleteBy eq)) (nubBy eq ys) xs
 
 -- | The 'intersect' function takes the list intersection of two lists.
 -- It is a special case of 'intersectBy', which allows the programmer to
 -- supply their own equality test.
--- For example,
 --
+-- ===== __Examples__
+--
 -- >>> [1,2,3,4] `intersect` [2,4,6,8]
 -- [2,4]
 --
@@ -621,19 +715,25 @@
 intersectBy eq xs ys    =  [x | x <- xs, any (eq x) ys]
 
 -- | \(\mathcal{O}(n)\). The 'intersperse' function takes an element and a list
--- and \`intersperses\' that element between the elements of the list. For
--- example,
+-- and \`intersperses\' that element between the elements of the list.
 --
--- >>> intersperse ',' "abcde"
--- "a,b,c,d,e"
+-- ==== __Laziness__
 --
--- 'intersperse' has the following laziness properties:
+-- 'intersperse' has the following properties
 --
 -- >>> take 1 (intersperse undefined ('a' : undefined))
 -- "a"
+--
 -- >>> take 2 (intersperse ',' ('a' : undefined))
 -- "a*** Exception: Prelude.undefined
 --
+-- ==== __Examples__
+--
+-- >>> intersperse ',' "abcde"
+-- "a,b,c,d,e"
+--
+-- >>> intersperse 1 [3, 4, 5]
+-- [3,1,4,1,5]
 intersperse             :: a -> [a] -> [a]
 intersperse _   []      = []
 intersperse sep (x:xs)  = x : prependToAll sep xs
@@ -651,22 +751,40 @@
 -- It inserts the list @xs@ in between the lists in @xss@ and concatenates the
 -- result.
 --
--- >>> intercalate ", " ["Lorem", "ipsum", "dolor"]
--- "Lorem, ipsum, dolor"
+-- ==== __Laziness__
 --
--- 'intercalate' has the following laziness properties:
+-- 'intercalate' has the following properties:
 --
 -- >>> take 5 (intercalate undefined ("Lorem" : undefined))
 -- "Lorem"
+--
 -- >>> take 6 (intercalate ", " ("Lorem" : undefined))
 -- "Lorem*** Exception: Prelude.undefined
 --
+-- ==== __Examples__
+--
+-- >>> intercalate ", " ["Lorem", "ipsum", "dolor"]
+-- "Lorem, ipsum, dolor"
+--
+-- >>> intercalate [0, 1] [[2, 3], [4, 5, 6], []]
+-- [2,3,0,1,4,5,6,0,1]
+--
+-- >>> intercalate [1, 2, 3] [[], []]
+-- [1,2,3]
 intercalate :: [a] -> [[a]] -> [a]
 intercalate xs xss = concat (intersperse xs xss)
 
 -- | The 'transpose' function transposes the rows and columns of its argument.
--- For example,
 --
+-- ==== __Laziness__
+--
+-- 'transpose' is lazy in its elements
+--
+-- >>> take 1 (transpose ['a' : undefined, 'b' : undefined])
+-- ["ab"]
+--
+-- ==== __Examples__
+--
 -- >>> transpose [[1,2,3],[4,5,6]]
 -- [[1,4],[2,5],[3,6]]
 --
@@ -679,12 +797,6 @@
 --
 -- >>> transpose (repeat [])
 -- * Hangs forever *
---
--- 'transpose' is lazy:
---
--- >>> take 1 (transpose ['a' : undefined, 'b' : undefined])
--- ["ab"]
---
 transpose :: [[a]] -> [[a]]
 transpose [] = []
 transpose ([] : xss) = transpose xss
@@ -741,8 +853,16 @@
 --
 -- > partition p xs == (filter p xs, filter (not . p) xs)
 --
+-- ==== __Examples__
+--
 -- >>> partition (`elem` "aeiou") "Hello World!"
 -- ("eoo","Hll Wrld!")
+--
+-- >>> partition even [1..10]
+-- ([2,4,6,8,10],[1,3,5,7,9])
+--
+-- >>> partition (< 5) [1..10]
+-- ([1,2,3,4],[5,6,7,8,9,10])
 partition               :: (a -> Bool) -> [a] -> ([a],[a])
 {-# INLINE partition #-}
 partition p xs = foldr (select p) ([],[]) xs
@@ -812,12 +932,25 @@
 -- call, the result will also be sorted. It is a special case of 'insertBy',
 -- which allows the programmer to supply their own comparison function.
 --
--- >>> insert 4 [1,2,3,5,6,7]
+-- ==== __Examples__
+--
+-- >>> insert (-1) [1, 2, 3]
+-- [-1,1,2,3]
+--
+-- >>> insert 'd' "abcefg"
+-- "abcdefg"
+--
+-- >>> insert 4 [1, 2, 3, 5, 6, 7]
 -- [1,2,3,4,5,6,7]
 insert :: Ord a => a -> [a] -> [a]
 insert e ls = insertBy (compare) e ls
 
 -- | \(\mathcal{O}(n)\). The non-overloaded version of 'insert'.
+--
+-- ==== __Examples__
+--
+-- >>> insertBy (\x y -> compare (length x) (length y)) [1, 2] [[1], [1, 2, 3], [1, 2, 3, 4]]
+-- [[1],[1,2],[1,2,3],[1,2,3,4]]
 insertBy :: (a -> a -> Ordering) -> a -> [a] -> [a]
 insertBy _   x [] = [x]
 insertBy cmp x ys@(y:ys')
@@ -830,10 +963,15 @@
 -- and returns the greatest element of the list by the comparison function.
 -- The list must be finite and non-empty.
 --
+-- ==== __Examples__
+--
 -- We can use this to find the longest entry of a list:
 --
 -- >>> maximumBy (\x y -> compare (length x) (length y)) ["Hello", "World", "!", "Longest", "bar"]
 -- "Longest"
+--
+-- >>> minimumBy (\(a, b) (c, d) -> compare (abs (a - b)) (abs (c - d))) [(10, 15), (1, 2), (3, 5)]
+-- (10, 15)
 maximumBy               :: (a -> a -> Ordering) -> [a] -> a
 maximumBy _ []          =  errorWithoutStackTrace "List.maximumBy: empty list"
 maximumBy cmp xs        =  foldl1 maxBy xs
@@ -847,10 +985,15 @@
 -- and returns the least element of the list by the comparison function.
 -- The list must be finite and non-empty.
 --
+-- ==== __Examples__
+--
 -- We can use this to find the shortest entry of a list:
 --
 -- >>> minimumBy (\x y -> compare (length x) (length y)) ["Hello", "World", "!", "Longest", "bar"]
 -- "!"
+--
+-- >>> minimumBy (\(a, b) (c, d) -> compare (abs (a - b)) (abs (c - d))) [(10, 15), (1, 2), (3, 5)]
+-- (1, 2)
 minimumBy               :: (a -> a -> Ordering) -> [a] -> a
 minimumBy _ []          =  errorWithoutStackTrace "List.minimumBy: empty list"
 minimumBy cmp xs        =  foldl1 minBy xs
@@ -864,6 +1007,8 @@
 -- type which is an instance of 'Num'. It is, however, less efficient than
 -- 'length'.
 --
+-- ==== __Examples__
+--
 -- >>> genericLength [1, 2, 3] :: Int
 -- 3
 -- >>> genericLength [1, 2, 3] :: Float
@@ -1199,18 +1344,24 @@
 -- returns the first list with the first occurrence of each element of
 -- the second list removed. This is the non-overloaded version of '(\\)'.
 --
+-- > (\\) == deleteFirstsBy (==)
+--
 -- The second list must be finite, but the first may be infinite.
 --
+-- ==== __Examples__
+--
+-- >>> deleteFirstsBy (>) [1..10] [3, 4, 5]
+-- [4,5,6,7,8,9,10]
+--
+-- >>> deleteFirstsBy (/=) [1..10] [1, 3, 5]
+-- [4,5,6,7,8,9,10]
 deleteFirstsBy          :: (a -> a -> Bool) -> [a] -> [a] -> [a]
 deleteFirstsBy eq       =  foldl (flip (deleteBy eq))
 
 -- | The 'group' function takes a list and returns a list of lists such
 -- that the concatenation of the result is equal to the argument.  Moreover,
 -- each sublist in the result is non-empty and all elements are equal
--- to the first one.  For example,
---
--- >>> group "Mississippi"
--- ["M","i","ss","i","ss","i","pp","i"]
+-- to the first one.
 --
 -- 'group' is a special case of 'groupBy', which allows the programmer to supply
 -- their own equality test.
@@ -1218,6 +1369,13 @@
 -- It's often preferable to use @Data.List.NonEmpty.@'Data.List.NonEmpty.group',
 -- which provides type-level guarantees of non-emptiness of inner lists.
 --
+-- ==== __Examples__
+--
+-- >>> group "Mississippi"
+-- ["M","i","ss","i","ss","i","pp","i"]
+--
+-- >>> group [1, 1, 1, 2, 2, 3, 4, 5, 5]
+-- [[1,1,1],[2,2],[3],[4],[5,5]]
 group                   :: Eq a => [a] -> [[a]]
 group                   =  groupBy (==)
 
@@ -1233,26 +1391,47 @@
 -- It's often preferable to use @Data.List.NonEmpty.@'Data.List.NonEmpty.groupBy',
 -- which provides type-level guarantees of non-emptiness of inner lists.
 --
+-- ==== __Examples__
+--
+-- >>> groupBy (/=) [1, 1, 1, 2, 3, 1, 4, 4, 5]
+-- [[1],[1],[1,2,3],[1,4,4,5]]
+--
+-- >>> groupBy (>) [1, 3, 5, 1, 4, 2, 6, 5, 4]
+-- [[1],[3],[5,1,4,2],[6,5,4]]
+--
+-- >>> groupBy (const not) [True, False, True, False, False, False, True]
+-- [[True,False],[True,False,False,False],[True]]
 groupBy                 :: (a -> a -> Bool) -> [a] -> [[a]]
 groupBy _  []           =  []
 groupBy eq (x:xs)       =  (x:ys) : groupBy eq zs
                            where (ys,zs) = span (eq x) xs
 
 -- | The 'inits' function returns all initial segments of the argument,
--- shortest first.  For example,
+-- shortest first.
 --
--- >>> inits "abc"
--- ["","a","ab","abc"]
+-- 'inits' is semantically equivalent to @'map' 'reverse' . 'scanl' ('flip' (:)) []@,
+-- but under the hood uses a queue to amortize costs of 'reverse'.
 --
+-- ==== __Laziness__
+--
 -- Note that 'inits' has the following strictness property:
 -- @inits (xs ++ _|_) = inits xs ++ _|_@
 --
 -- In particular,
 -- @inits _|_ = [] : _|_@
 --
--- 'inits' is semantically equivalent to @'map' 'reverse' . 'scanl' ('flip' (:)) []@,
--- but under the hood uses a queue to amortize costs of 'reverse'.
+-- ==== __Examples__
 --
+-- >>> inits "abc"
+-- ["","a","ab","abc"]
+--
+-- >>> inits []
+-- [[]]
+--
+-- inits is productive on infinite lists:
+--
+-- >>> take 5 $ inits [1..]
+-- [[],[1],[1,2],[1,2,3],[1,2,3,4]]
 inits                   :: [a] -> [[a]]
 inits                   = map toListSB . scanl' snocSB emptySB
 {-# NOINLINE inits #-}
@@ -1262,13 +1441,29 @@
 -- loss of sharing if allowed to fuse with a producer.
 
 -- | \(\mathcal{O}(n)\). The 'tails' function returns all final segments of the
--- argument, longest first. For example,
+-- argument, longest first.
 --
--- >>> tails "abc"
--- ["abc","bc","c",""]
+-- ==== __Laziness__
 --
 -- Note that 'tails' has the following strictness property:
 -- @tails _|_ = _|_ : _|_@
+--
+-- >>> tails undefined
+-- [*** Exception: Prelude.undefined
+--
+-- >>> drop 1 (tails [undefined, 1, 2])
+-- [[1, 2], [2], []]
+--
+-- ==== __Examples__
+--
+-- >>> tails "abc"
+-- ["abc","bc","c",""]
+--
+-- >>> tails [1, 2, 3]
+-- [[1,2,3],[2,3],[3],[]]
+--
+-- >>> tails []
+-- [[]]
 tails                   :: [a] -> [[a]]
 {-# INLINABLE tails #-}
 tails lst               =  build (\c n ->
@@ -1279,13 +1474,7 @@
 
 -- | The 'subsequences' function returns the list of all subsequences of the argument.
 --
--- >>> subsequences "abc"
--- ["","a","b","ab","c","ac","bc","abc"]
---
--- This function is productive on infinite inputs:
---
--- >>> take 8 $ subsequences ['a'..]
--- ["","a","b","ab","c","ac","bc","abc"]
+-- ==== __Laziness__
 --
 -- 'subsequences' does not look ahead unless it must:
 --
@@ -1294,6 +1483,15 @@
 -- >>> take 2 (subsequences ('a' : undefined))
 -- ["","a"]
 --
+-- ==== __Examples__
+--
+-- >>> subsequences "abc"
+-- ["","a","b","ab","c","ac","bc","abc"]
+--
+-- This function is productive on infinite inputs:
+--
+-- >>> take 8 $ subsequences ['a'..]
+-- ["","a","b","ab","c","ac","bc","abc"]
 subsequences            :: [a] -> [[a]]
 subsequences xs         =  [] : nonEmptySubsequences xs
 
@@ -1310,23 +1508,32 @@
 
 -- | The 'permutations' function returns the list of all permutations of the argument.
 --
--- >>> permutations "abc"
--- ["abc","bac","cba","bca","cab","acb"]
+-- Note that the order of permutations is not lexicographic.
+-- It satisfies the following property:
 --
+-- > map (take n) (take (product [1..n]) (permutations ([1..n] ++ undefined))) == permutations [1..n]
+--
+-- ==== __Laziness__
+--
 -- The 'permutations' function is maximally lazy:
 -- for each @n@, the value of @'permutations' xs@ starts with those permutations
 -- that permute @'take' n xs@ and keep @'drop' n xs@.
 --
--- This function is productive on infinite inputs:
+-- ==== __Examples__
 --
--- >>> take 6 $ map (take 3) $ permutations ['a'..]
+-- >>> permutations "abc"
 -- ["abc","bac","cba","bca","cab","acb"]
 --
--- Note that the order of permutations is not lexicographic.
--- It satisfies the following property:
+-- >>> permutations [1, 2]
+-- [[1,2],[2,1]]
 --
--- > map (take n) (take (product [1..n]) (permutations ([1..n] ++ undefined))) == permutations [1..n]
+-- >>> permutations []
+-- [[]]
 --
+-- This function is productive on infinite inputs:
+--
+-- >>> take 6 $ map (take 3) $ permutations ['a'..]
+-- ["abc","bac","cba","bca","cab","acb"]
 permutations :: [a] -> [[a]]
 -- See https://stackoverflow.com/questions/24484348/what-does-this-list-permutations-implementation-in-haskell-exactly-do/24564307#24564307
 -- for the analysis of this rather cryptic implementation.
@@ -1384,24 +1591,33 @@
 -- Elements are arranged from lowest to highest, keeping duplicates in
 -- the order they appeared in the input.
 --
+-- The argument must be finite.
+--
+-- ==== __Examples__
+--
 -- >>> sort [1,6,4,3,2,5]
 -- [1,2,3,4,5,6]
 --
--- The argument must be finite.
+-- >>> sort "haskell"
+-- "aehklls"
 --
+-- >>> import Data.Semigroup(Arg(..))
+-- >>> sort [Arg ":)" 0, Arg ":D" 0, Arg ":)" 1, Arg ":3" 0, Arg ":D" 1]
+-- [Arg ":)" 0,Arg ":)" 1,Arg ":3" 0,Arg ":D" 0,Arg ":D" 1]
 sort :: (Ord a) => [a] -> [a]
 
 -- | The 'sortBy' function is the non-overloaded version of 'sort'.
 -- The argument must be finite.
 --
--- >>> sortBy (\(a,_) (b,_) -> compare a b) [(2, "world"), (4, "!"), (1, "Hello")]
--- [(1,"Hello"),(2,"world"),(4,"!")]
---
 -- The supplied comparison relation is supposed to be reflexive and antisymmetric,
 -- otherwise, e. g., for @\_ _ -> GT@, the ordered list simply does not exist.
 -- The relation is also expected to be transitive: if it is not then 'sortBy'
 -- might fail to find an ordered permutation, even if it exists.
 --
+-- ==== __Examples__
+--
+-- >>> sortBy (\(a,_) (b,_) -> compare a b) [(2, "world"), (4, "!"), (1, "Hello")]
+-- [(1,"Hello"),(2,"world"),(4,"!")]
 sortBy :: (a -> a -> Ordering) -> [a] -> [a]
 
 #if defined(USE_REPORT_PRELUDE)
@@ -1567,10 +1783,15 @@
 -- Elements are arranged from lowest to highest, keeping duplicates in
 -- the order they appeared in the input.
 --
+-- The argument must be finite.
+--
+-- ==== __Examples__
+--
 -- >>> sortOn fst [(2, "world"), (4, "!"), (1, "Hello")]
 -- [(1,"Hello"),(2,"world"),(4,"!")]
 --
--- The argument must be finite.
+-- >>> sortOn length ["jim", "creed", "pam", "michael", "dwight", "kevin"]
+-- ["jim","pam","creed","kevin","dwight","michael"]
 --
 -- @since 4.8.0.0
 sortOn :: Ord b => (a -> b) -> [a] -> [a]
@@ -1579,9 +1800,17 @@
 
 -- | Construct a list from a single element.
 --
+-- ==== __Examples__
+--
 -- >>> singleton True
 -- [True]
 --
+-- >>> singleton [1, 2, 3]
+--[[1,2,3]]
+--
+-- >>> singleton 'c'
+-- "c"
+--
 -- @since 4.15.0.0
 --
 singleton :: a -> [a]
@@ -1605,16 +1834,19 @@
 -- > f' (f x y) = Just (x,y)
 -- > f' z       = Nothing
 --
--- A simple use of unfoldr:
 --
--- >>> unfoldr (\b -> if b == 0 then Nothing else Just (b, b-1)) 10
--- [10,9,8,7,6,5,4,3,2,1]
---
--- Laziness:
+-- ==== __Laziness__
 --
 -- >>> take 1 (unfoldr (\x -> Just (x, undefined)) 'a')
 -- "a"
 --
+-- ==== __Examples__
+--
+-- >>> unfoldr (\b -> if b == 0 then Nothing else Just (b, b-1)) 10
+-- [10,9,8,7,6,5,4,3,2,1]
+--
+-- >>> take 10 $ unfoldr (\(x, y) -> Just (x, (y, x + y))) (0, 1)
+-- [0,1,1,2,3,5,8,13,21,54]
 
 -- Note [INLINE unfoldr]
 -- ~~~~~~~~~~~~~~~~~~~~~
@@ -1656,30 +1888,35 @@
 -- @\\n@ characters.  The @\\n@ terminator is optional in a final non-empty
 -- line of the argument string.
 --
--- For example:
+-- When the argument string is empty, or ends in a @\\n@ character, it can be
+-- recovered by passing the result of 'lines' to the 'unlines' function.
+-- Otherwise, 'unlines' appends the missing terminating @\\n@.  This makes
+-- @unlines . lines@ /idempotent/:
 --
+-- > (unlines . lines) . (unlines . lines) = (unlines . lines)
+--
+-- ==== __Examples__
+--
 -- >>> lines ""           -- empty input contains no lines
 -- []
+--
 -- >>> lines "\n"         -- single empty line
 -- [""]
+--
 -- >>> lines "one"        -- single unterminated line
 -- ["one"]
+--
 -- >>> lines "one\n"      -- single non-empty line
 -- ["one"]
+--
 -- >>> lines "one\n\n"    -- second line is empty
 -- ["one",""]
+--
 -- >>> lines "one\ntwo"   -- second line is unterminated
 -- ["one","two"]
+--
 -- >>> lines "one\ntwo\n" -- two non-empty lines
 -- ["one","two"]
---
--- When the argument string is empty, or ends in a @\\n@ character, it can be
--- recovered by passing the result of 'lines' to the 'unlines' function.
--- Otherwise, 'unlines' appends the missing terminating @\\n@.  This makes
--- @unlines . lines@ /idempotent/:
---
--- > (unlines . lines) . (unlines . lines) = (unlines . lines)
---
 lines                   :: String -> [String]
 lines ""                =  []
 -- Somehow GHC doesn't detect the selector thunks in the below code,
@@ -1696,6 +1933,8 @@
 -- | Appends a @\\n@ character to each input string, then concatenates the
 -- results. Equivalent to @'foldMap' (\s -> s '++' "\\n")@.
 --
+-- ==== __Examples__
+--
 -- >>> unlines ["Hello", "World", "!"]
 -- "Hello\nWorld\n!\n"
 --
@@ -1717,11 +1956,13 @@
 -- by white space (as defined by 'isSpace'). This function trims any white spaces
 -- at the beginning and at the end.
 --
+-- ==== __Examples__
+--
 -- >>> words "Lorem ipsum\ndolor"
 -- ["Lorem","ipsum","dolor"]
+--
 -- >>> words " foo bar "
 -- ["foo","bar"]
---
 words                   :: String -> [String]
 {-# NOINLINE [1] words #-}
 words s                 =  case dropWhile {-partain:Char.-}isSpace s of
@@ -1745,9 +1986,6 @@
 
 -- | 'unwords' joins words with separating spaces (U+0020 SPACE).
 --
--- >>> unwords ["Lorem", "ipsum", "dolor"]
--- "Lorem ipsum dolor"
---
 -- 'unwords' is neither left nor right inverse of 'words':
 --
 -- >>> words (unwords [" "])
@@ -1755,6 +1993,13 @@
 -- >>> unwords (words "foo\nbar")
 -- "foo bar"
 --
+-- ==== __Examples__
+--
+-- >>> unwords ["Lorem", "ipsum", "dolor"]
+-- "Lorem ipsum dolor"
+--
+-- >>> unwords ["foo", "bar", "", "baz"]
+-- "foo bar  baz"
 unwords                 :: [String] -> String
 #if defined(USE_REPORT_PRELUDE)
 unwords []              =  ""
diff --git a/Data/Semigroup.hs b/Data/Semigroup.hs
--- a/Data/Semigroup.hs
+++ b/Data/Semigroup.hs
@@ -26,6 +26,7 @@
 --
 -- The 'Min' 'Semigroup' instance for 'Int' is defined to always pick the smaller
 -- number:
+--
 -- >>> Min 1 <> Min 2 <> Min 3 <> Min 4 :: Min Int
 -- Min {getMin = 1}
 --
@@ -48,6 +49,7 @@
 --
 -- >>> sconcat (1 :| [2, 3, 4]) :: Min Int
 -- Min {getMin = 1}
+--
 -- >>> sconcat (1 :| [2, 3, 4]) :: Max Int
 -- Max {getMax = 4}
 --
@@ -120,28 +122,56 @@
 
 -- | A generalization of 'Data.List.cycle' to an arbitrary 'Semigroup'.
 -- May fail to terminate for some values in some semigroups.
+--
+-- ==== __Examples__
+--
+-- >>> take 10 $ cycle1 [1, 2, 3]
+-- [1,2,3,1,2,3,1,2,3,1]
+--
+-- >>> cycle1 (Right 1)
+-- Right 1
+--
+-- >>> cycle1 (Left 1)
+-- * hangs forever *
 cycle1 :: Semigroup m => m -> m
 cycle1 xs = xs' where xs' = xs <> xs'
 
 -- | This lets you use a difference list of a 'Semigroup' as a 'Monoid'.
 --
--- === __Example:__
--- >>> let hello = diff "Hello, "
+-- ==== __Examples__
+--
+-- > let hello = diff "Hello, "
+--
 -- >>> appEndo hello "World!"
 -- "Hello, World!"
+--
 -- >>> appEndo (hello <> mempty) "World!"
 -- "Hello, World!"
+--
 -- >>> appEndo (mempty <> hello) "World!"
 -- "Hello, World!"
--- >>> let world = diff "World"
--- >>> let excl = diff "!"
+--
+-- > let world = diff "World"
+-- > let excl = diff "!"
+--
 -- >>> appEndo (hello <> (world <> excl)) mempty
 -- "Hello, World!"
+--
 -- >>> appEndo ((hello <> world) <> excl) mempty
 -- "Hello, World!"
 diff :: Semigroup m => m -> Endo m
 diff = Endo . (<>)
 
+-- | The 'Min' 'Monoid' and 'Semigroup' always choose the smaller element as
+-- by the 'Ord' instance and 'min' of the contained type.
+--
+-- ==== __Examples__
+--
+-- >>> Min 42 <> Min 3
+-- Min 3
+--
+-- >>> sconcat $ Min 1 :| [ Min n | n <- [2 .. 100]]
+-- Min {getMin = 1}
 newtype Min a = Min { getMin :: a }
   deriving ( Bounded  -- ^ @since 4.9.0.0
            , Eq       -- ^ @since 4.9.0.0
@@ -217,6 +247,16 @@
   signum (Min a) = Min (signum a)
   fromInteger    = Min . fromInteger
 
+-- | The 'Max' 'Monoid' and 'Semigroup' always choose the bigger element as
+-- by the 'Ord' instance and 'max' of the contained type.
+--
+-- ==== __Examples__
+--
+-- >>> Max 42 <> Max 3
+-- Max 42
+--
+-- >>> sconcat $ Max 1 :| [ Max n | n <- [2 .. 100]]
+-- Max {getMax = 100}
 newtype Max a = Max { getMax :: a }
   deriving ( Bounded  -- ^ @since 4.9.0.0
            , Eq       -- ^ @since 4.9.0.0
@@ -294,8 +334,16 @@
 -- | 'Arg' isn't itself a 'Semigroup' in its own right, but it can be
 -- placed inside 'Min' and 'Max' to compute an arg min or arg max.
 --
+-- ==== __Examples__
+--
 -- >>> minimum [ Arg (x * x) x | x <- [-10 .. 10] ]
 -- Arg 0 0
+--
+-- >>> maximum [ Arg (-0.2*x^2 + 1.5*x + 1) x | x <- [-10 .. 10] ]
+-- Arg 3.8 4.0
+--
+-- >>> minimum [ Arg (-0.2*x^2 + 1.5*x + 1) x | x <- [-10 .. 10] ]
+-- Arg (-34.0) (-10.0)
 data Arg a b = Arg
   a
   -- ^ The argument used for comparisons in 'Eq' and 'Ord'.
@@ -310,13 +358,23 @@
   )
 
 -- |
+-- ==== __Examples__
+--
 -- >>> Min (Arg 0 ()) <> Min (Arg 1 ())
 -- Min {getMin = Arg 0 ()}
+--
+-- >>> minimum [ Arg (length name) name | name <- ["violencia", "lea", "pixie"]]
+-- Arg 3 "lea"
 type ArgMin a b = Min (Arg a b)
 
 -- |
+-- ==== __Examples__
+--
 -- >>> Max (Arg 0 ()) <> Max (Arg 1 ())
 -- Max {getMax = Arg 1 ()}
+--
+-- >>> maximum [ Arg (length name) name | name <- ["violencia", "lea", "pixie"]]
+-- Arg 9 "violencia"
 type ArgMax a b = Max (Arg a b)
 
 -- | @since 4.9.0.0
@@ -364,6 +422,13 @@
 -- The latter returns the first non-'Nothing',
 -- thus @Data.Monoid.First Nothing <> x = x@.
 --
+-- ==== __Examples__
+--
+-- >>> First 0 <> First 10
+-- First 0
+--
+-- >>> sconcat $ First 1 :| [ First n | n <- [2 ..] ]
+-- First 1
 newtype First a = First { getFirst :: a }
   deriving ( Bounded  -- ^ @since 4.9.0.0
            , Eq       -- ^ @since 4.9.0.0
@@ -427,6 +492,13 @@
 -- The latter returns the last non-'Nothing',
 -- thus @x <> Data.Monoid.Last Nothing = x@.
 --
+-- ==== __Examples__
+--
+-- >>> Last 0 <> Last 10
+-- Last {getLast = 10}
+--
+-- >>> sconcat $ Last 1 :| [ Last n | n <- [2..]]
+-- Last {getLast = * hangs forever *
 newtype Last a = Last { getLast :: a }
   deriving ( Bounded  -- ^ @since 4.9.0.0
            , Eq       -- ^ @since 4.9.0.0
@@ -526,7 +598,7 @@
 --
 -- > mtimesDefault n a = a <> a <> ... <> a  -- using <> (n-1) times
 --
--- In many cases, `stimes 0 a` for a `Monoid` will produce `mempty`.
+-- In many cases, @'stimes' 0 a@ for a `Monoid` will produce `mempty`.
 -- However, there are situations when it cannot do so. In particular,
 -- the following situation is fairly common:
 --
@@ -535,6 +607,7 @@
 --
 -- class Constraint1 a
 -- class Constraint1 a => Constraint2 a
+-- @
 --
 -- @
 -- instance Constraint1 a => 'Semigroup' (T a)
@@ -548,6 +621,14 @@
 -- 'Semigroup' instances, @mtimesDefault@ should be used when the
 -- multiplier might be zero. It is implemented using 'stimes' when
 -- the multiplier is nonzero and 'mempty' when it is zero.
+--
+-- ==== __Examples__
+--
+-- >>> mtimesDefault 0 "bark"
+-- []
+--
+-- >>> mtimesDefault 3 "meow"
+-- "meowmeowmeow"
 mtimesDefault :: (Integral b, Monoid a) => b -> a -> a
 mtimesDefault n x
   | n == 0    = mempty
diff --git a/Data/Semigroup/Internal.hs b/Data/Semigroup/Internal.hs
--- a/Data/Semigroup/Internal.hs
+++ b/Data/Semigroup/Internal.hs
@@ -40,7 +40,7 @@
 
 -- | This is a valid definition of 'stimes' for an idempotent 'Monoid'.
 --
--- When @mappend x x = x@, this definition should be preferred, because it
+-- When @x <> x = x@, this definition should be preferred, because it
 -- works in \(\mathcal{O}(1)\) rather than \(\mathcal{O}(\log n)\)
 stimesIdempotentMonoid :: (Integral b, Monoid a) => b -> a -> a
 stimesIdempotentMonoid n x = case compare n 0 of
@@ -104,10 +104,17 @@
     rep 0 = []
     rep i = x ++ rep (i - 1)
 
--- | The dual of a 'Monoid', obtained by swapping the arguments of 'mappend'.
+-- | The dual of a 'Monoid', obtained by swapping the arguments of '(<>)'.
 --
--- >>> getDual (mappend (Dual "Hello") (Dual "World"))
--- "WorldHello"
+-- > Dual a <> Dual b == Dual (b <> a)
+--
+-- ==== __Examples__
+--
+-- >>> Dual "Hello" <> Dual "World"
+-- Dual {getDual = "WorldHello"}
+--
+-- >>> Dual (Dual "Hello") <> Dual (Dual "World")
+-- Dual {getDual = Dual {getDual = "HelloWorld"}}
 newtype Dual a = Dual { getDual :: a }
         deriving ( Eq       -- ^ @since 2.01
                  , Ord      -- ^ @since 2.01
@@ -142,9 +149,17 @@
 
 -- | The monoid of endomorphisms under composition.
 --
+-- > Endo f <> Endo g == Endo (f . g)
+--
+-- ==== __Examples__
+--
 -- >>> let computation = Endo ("Hello, " ++) <> Endo (++ "!")
 -- >>> appEndo computation "Haskell"
 -- "Hello, Haskell!"
+--
+-- >>> let computation = Endo (*3) <> Endo (+1)
+-- >>> appEndo computation 1
+-- 6
 newtype Endo a = Endo { appEndo :: a -> a }
                deriving ( Generic -- ^ @since 4.7.0.0
                         )
@@ -158,13 +173,20 @@
 instance Monoid (Endo a) where
         mempty = Endo id
 
--- | Boolean monoid under conjunction ('&&').
+-- | Boolean monoid under conjunction '(&&)'.
 --
--- >>> getAll (All True <> mempty <> All False)
--- False
+-- > All x <> All y = All (x && y)
 --
--- >>> getAll (mconcat (map (\x -> All (even x)) [2,4,6,7,8]))
--- False
+-- ==== __Examples__
+--
+-- >>> All True <> mempty <> All False)
+-- All {getAll = False}
+--
+-- >>> mconcat (map (\x -> All (even x)) [2,4,6,7,8])
+-- All {getAll = False}
+--
+-- >>> All True <> mempty
+-- All {getAll = True}
 newtype All = All { getAll :: Bool }
         deriving ( Eq      -- ^ @since 2.01
                  , Ord     -- ^ @since 2.01
@@ -183,13 +205,20 @@
 instance Monoid All where
         mempty = All True
 
--- | Boolean monoid under disjunction ('||').
+-- | Boolean monoid under disjunction '(||)'.
 --
--- >>> getAny (Any True <> mempty <> Any False)
--- True
+-- > Any x <> Any y = Any (x || y)
 --
--- >>> getAny (mconcat (map (\x -> Any (even x)) [2,4,6,7,8]))
--- True
+-- ==== __Examples__
+--
+-- >>> Any True <> mempty <> Any False
+-- Any {getAny = True}
+--
+-- >>> mconcat (map (\x -> Any (even x)) [2,4,6,7,8])
+-- Any {getAny = True}
+--
+-- >>> Any False <> mempty
+-- Any {getAny = False}
 newtype Any = Any { getAny :: Bool }
         deriving ( Eq      -- ^ @since 2.01
                  , Ord     -- ^ @since 2.01
@@ -210,8 +239,15 @@
 
 -- | Monoid under addition.
 --
--- >>> getSum (Sum 1 <> Sum 2 <> mempty)
--- 3
+-- > Sum a <> Sum b = Sum (a + b)
+--
+-- ==== __Examples__
+--
+-- >>> Sum 1 <> Sum 2 <> mempty
+-- Sum {getSum = 3}
+--
+-- >>> mconcat [ Sum n | n <- [3 .. 9]]
+-- Sum {getSum = 42}
 newtype Sum a = Sum { getSum :: a }
         deriving ( Eq       -- ^ @since 2.01
                  , Ord      -- ^ @since 2.01
@@ -251,8 +287,15 @@
 
 -- | Monoid under multiplication.
 --
--- >>> getProduct (Product 3 <> Product 4 <> mempty)
--- 12
+-- > Product x <> Product y == Product (x * y)
+--
+-- ==== __Examples__
+--
+-- >>> Product 3 <> Product 4 <> mempty
+-- Product {getProduct = 12}
+--
+-- >>> mconcat [ Product n | n <- [2 .. 10]]
+-- Product {getProduct = 3628800}
 newtype Product a = Product { getProduct :: a }
         deriving ( Eq       -- ^ @since 2.01
                  , Ord      -- ^ @since 2.01
@@ -294,11 +337,14 @@
 
 -- | Monoid under '<|>'.
 --
--- >>> getAlt (Alt (Just 12) <> Alt (Just 24))
--- Just 12
+-- > Alt l <> Alt r == Alt (l <|> r)
 --
--- >>> getAlt $ Alt Nothing <> Alt (Just 24)
--- Just 24
+-- ==== __Examples__
+-- >>> Alt (Just 12) <> Alt (Just 24)
+-- Alt {getAlt = Just 12}
+--
+-- >>> Alt Nothing <> Alt (Just 24)
+-- Alt {getAlt = Just 24}
 --
 -- @since 4.8.0.0
 newtype Alt f a = Alt {getAlt :: f a}
diff --git a/Data/String.hs b/Data/String.hs
--- a/Data/String.hs
+++ b/Data/String.hs
@@ -37,8 +37,26 @@
 import Data.Functor.Identity (Identity (Identity))
 import Data.List (lines, words, unlines, unwords)
 
--- | Class for string-like datastructures; used by the overloaded string
---   extension (-XOverloadedStrings in GHC).
+-- | `IsString` is used in combination with the @-XOverloadedStrings@
+-- language extension to convert the literals to different string types.
+--
+-- For example, if you use the [text](https://hackage.haskell.org/package/text) package,
+-- you can say
+--
+-- @
+-- {-# LANGUAGE OverloadedStrings  #-}
+--
+-- myText = "hello world" :: Text
+-- @
+--
+-- Internally, the extension will convert this to the equivalent of
+--
+-- @
+-- myText = fromString @Text ("hello world" :: String)
+-- @
+--
+-- __Note:__ You can use @fromString@ in normal code as well,
+-- but the usual performance/memory efficiency problems with 'String' apply.
 class IsString a where
     fromString :: String -> a
 
diff --git a/GHC/Base.hs b/GHC/Base.hs
--- a/GHC/Base.hs
+++ b/GHC/Base.hs
@@ -250,8 +250,16 @@
 class Semigroup a where
         -- | An associative operation.
         --
+        -- ==== __Examples__
+        --
         -- >>> [1,2,3] <> [4,5,6]
         -- [1,2,3,4,5,6]
+        --
+        -- >>> Just [1, 2, 3] <> Just [4, 5, 6]
+        -- Just [1,2,3,4,5,6]
+        --
+        -- >>> putStr "Hello, " <> putStrLn "World!"
+        -- Hello, World!
         (<>) :: a -> a -> a
         a <> b = sconcat (a :| [ b ])
 
@@ -260,9 +268,20 @@
         -- The default definition should be sufficient, but this can be
         -- overridden for efficiency.
         --
+        -- ==== __Examples__
+        --
+        -- For the following examples, we will assume that we have:
+        --
         -- >>> import Data.List.NonEmpty (NonEmpty (..))
+        --
         -- >>> sconcat $ "Hello" :| [" ", "Haskell", "!"]
         -- "Hello Haskell!"
+        --
+        -- >>> sconcat $ Just [1, 2, 3] :| [Nothing, Just [4, 5, 6]]
+        -- Just [1,2,3,4,5,6]
+        --
+        -- >>> sconcat $ Left 1 :| [Right 2, Left 3, Right 4]
+        -- Right 2
         sconcat :: NonEmpty a -> a
         sconcat (a :| as) = go a as where
           go b (c:cs) = b <> go c cs
@@ -270,17 +289,25 @@
 
         -- | Repeat a value @n@ times.
         --
-        -- Given that this works on a 'Semigroup' it is allowed to fail if
-        -- you request 0 or fewer repetitions, and the default definition
-        -- will do so.
+        -- The default definition will raise an exception for a multiplier that is @<= 0@.
+        -- This may be overridden with an implementation that is total. For monoids
+        -- it is preferred to use 'stimesMonoid'.
         --
         -- By making this a member of the class, idempotent semigroups
         -- and monoids can upgrade this to execute in \(\mathcal{O}(1)\) by
         -- picking @stimes = 'Data.Semigroup.stimesIdempotent'@ or @stimes =
-        -- 'stimesIdempotentMonoid'@ respectively.
+        -- 'Data.Semigroup.stimesIdempotentMonoid'@ respectively.
         --
+        -- ==== __Examples__
+        --
         -- >>> stimes 4 [1]
         -- [1,1,1,1]
+        --
+        -- >>> stimes 5 (putStr "hi!")
+        -- hi!hi!hi!hi!hi!
+        --
+        -- >>> stimes 3 (Right ":)")
+        -- Right ":)"
         stimes :: Integral b => b -> a -> a
         stimes = stimesDefault
 
@@ -314,8 +341,12 @@
 class Semigroup a => Monoid a where
         -- | Identity of 'mappend'
         --
+        -- ==== __Examples__
         -- >>> "Hello world" <> mempty
         -- "Hello world"
+        --
+        -- >>> mempty <> [1, 2, 3]
+        -- [1,2,3]
         mempty :: a
         mempty = mconcat []
         {-# INLINE mempty #-}
@@ -1396,8 +1427,18 @@
 -- > map f [x1, x2, ..., xn] == [f x1, f x2, ..., f xn]
 -- > map f [x1, x2, ...] == [f x1, f x2, ...]
 --
+-- this means that @map id == id@
+--
+-- ==== __Examples__
+--
 -- >>> map (+1) [1, 2, 3]
 -- [2,3,4]
+--
+-- >>> map id [1, 2, 3]
+-- [1,2,3]
+--
+-- >>> map (\n -> 3 * n + 1) [1, 2, 3]
+-- [4,7,10]
 map :: (a -> b) -> [a] -> [b]
 {-# NOINLINE [0] map #-}
   -- We want the RULEs "map" and "map/coerce" to fire first.
@@ -1464,21 +1505,33 @@
 --              append
 ----------------------------------------------
 
--- | Append two lists, i.e.,
+-- | '(++)' appends two lists, i.e.,
 --
 -- > [x1, ..., xm] ++ [y1, ..., yn] == [x1, ..., xm, y1, ..., yn]
 -- > [x1, ..., xm] ++ [y1, ...] == [x1, ..., xm, y1, ...]
 --
 -- If the first list is not finite, the result is the first list.
 --
+-- ==== __Performance considerations__
+--
 -- This function takes linear time in the number of elements of the
 -- __first__ list. Thus it is better to associate repeated
 -- applications of '(++)' to the right (which is the default behaviour):
 -- @xs ++ (ys ++ zs)@ or simply @xs ++ ys ++ zs@, but not @(xs ++ ys) ++ zs@.
 -- For the same reason 'Data.List.concat' @=@ 'Data.List.foldr' '(++)' @[]@
 -- has linear performance, while 'Data.List.foldl' '(++)' @[]@ is prone
--- to quadratic slowdown.
-
+-- to quadratic slowdown
+--
+-- ==== __Examples__
+--
+-- >>> [1, 2, 3] ++ [4, 5, 6]
+-- [1,2,3,4,5,6]
+--
+-- >>> [] ++ [1, 2, 3]
+-- [1,2,3]
+--
+-- >>> [3, 2, 1] ++ []
+-- [3,2,1]
 (++) :: [a] -> [a] -> [a]
 {-# NOINLINE [2] (++) #-}
   -- Give time for the RULEs for (++) to fire in InitialPhase
@@ -1508,10 +1561,42 @@
 -- Type Char and String
 ----------------------------------------------
 
--- | A 'String' is a list of characters.  String constants in Haskell are values
--- of type 'String'.
+-- | 'String' is an alias for a list of characters.
 --
--- See "Data.List" for operations on lists.
+-- String constants in Haskell are values of type 'String'.
+-- That means if you write a string literal like @"hello world"@,
+-- it will have the type @[Char]@, which is the same as @String@.
+--
+-- __Note:__ You can ask the compiler to automatically infer different types
+-- with the @-XOverloadedStrings@ language extension, for example
+--  @"hello world" :: Text@. See t'Data.String.IsString' for more information.
+--
+-- Because @String@ is just a list of characters, you can use normal list functions
+-- to do basic string manipulation. See "Data.List" for operations on lists.
+--
+-- === __Performance considerations__
+--
+-- @[Char]@ is a relatively memory-inefficient type.
+-- It is a linked list of boxed word-size characters, internally it looks something like:
+--
+-- > ╭─────┬───┬──╮  ╭─────┬───┬──╮  ╭─────┬───┬──╮  ╭────╮
+-- > │ (:) │   │ ─┼─>│ (:) │   │ ─┼─>│ (:) │   │ ─┼─>│ [] │
+-- > ╰─────┴─┼─┴──╯  ╰─────┴─┼─┴──╯  ╰─────┴─┼─┴──╯  ╰────╯
+-- >         v               v               v
+-- >        'a'             'b'             'c'
+--
+-- The @String@ "abc" will use @5*3+1 = 16@ (in general @5n+1@)
+-- words of space in memory.
+--
+-- Furthermore, operations like '(++)' (string concatenation) are @O(n)@
+-- (in the left argument).
+--
+-- For historical reasons, the @base@ library uses @String@ in a lot of places
+-- for the conceptual simplicity, but library code dealing with user-data
+-- should use the [text](https://hackage.haskell.org/package/text)
+-- package for Unicode text, or the the
+-- [bytestring](https://hackage.haskell.org/package/bytestring) package
+-- for binary data.
 type String = [Char]
 
 unsafeChr :: Int -> Char
@@ -1558,6 +1643,20 @@
 -- | Identity function.
 --
 -- > id x = x
+--
+-- This function might seem useless at first glance, but it can be very useful
+-- in a higher order context.
+--
+-- ==== __Examples__
+--
+-- >>> length $ filter id [True, True, False, True]
+-- 3
+--
+-- >>> Just (Just 3) >>= id
+-- Just 3
+--
+-- >>> foldr id 0 [(^3), (*5), (+2)]
+-- 1000
 id                      :: a -> a
 id x                    =  x
 
@@ -1591,6 +1690,13 @@
 data Opaque = forall a. O a
 -- | @const x y@ always evaluates to @x@, ignoring its second argument.
 --
+-- > const x = \_ -> x
+--
+-- This function might seem useless at first glance, but it can be very useful
+-- in a higher order context.
+--
+-- ==== __Examples__
+--
 -- >>> const 42 "hello"
 -- 42
 --
@@ -1599,7 +1705,22 @@
 const                   :: a -> b -> a
 const x _               =  x
 
--- | Function composition.
+-- | Right to left function composition.
+--
+-- prop> (f . g) x = f (g x)
+--
+-- prop> f . id = f = id . f
+--
+-- ==== __Examples__
+--
+-- >>> map ((*2) . length) [[], [0, 1, 2], [0]]
+-- [0,6,2]
+--
+-- >>> foldr (.) id [(+1), (*3), (^3)] 2
+-- 25
+--
+-- >>> let (...) = (.).(.) in ((*2)...(+)) 5 10
+-- 30
 {-# INLINE (.) #-}
 -- Make sure it has TWO args only on the left, so that it inlines
 -- when applied to two functions, even if there is no final argument
@@ -1608,8 +1729,17 @@
 
 -- | @'flip' f@ takes its (first) two arguments in the reverse order of @f@.
 --
+-- prop> flip f x y = f y x
+--
+-- prop> flip . flip = id
+--
+-- ==== __Examples__
+--
 -- >>> flip (++) "hello" "world"
 -- "worldhello"
+--
+-- >>> let (.>) = flip (.) in (+1) .> show $ 5
+-- "6"
 flip                    :: (a -> b -> c) -> b -> a -> c
 flip f x y              =  f y x
 
@@ -1621,15 +1751,18 @@
 -- (\x -> undefined x) `seq` () and thus would just evaluate to (), but now
 -- it is equivalent to undefined `seq` () which diverges.
 
-{- | @($)@ is the __function application__ operator.
+{- | @'($)'@ is the __function application__ operator.
 
-Applying @($)@ to a function @f@ and an argument @x@ gives the same result as applying @f@ to @x@ directly. The definition is akin to this:
+Applying @'($)'@ to a function @f@ and an argument @x@ gives the same result as applying @f@ to @x@ directly. The definition is akin to this:
 
 @
 ($) :: (a -> b) -> a -> b
 ($) f x = f x
 @
 
+This is @'id'@ specialized from @a -> a@ to @(a -> b) -> (a -> b)@ which by the associativity of @(->)@
+is the same as @(a -> b) -> a -> b@.
+
 On the face of it, this may appear pointless! But it's actually one of the most useful and important operators in Haskell.
 
 The order of operations is very different between @($)@ and normal function application. Normal function application has precedence 10 - higher than any operator - and associates to the left. So these two definitions are equivalent:
@@ -1646,7 +1779,7 @@
 expr = (min 5) (1 + 5)
 @
 
-=== Uses
+==== __Examples__
 
 A common use cases of @($)@ is to avoid parentheses in complex expressions.
 
@@ -1675,7 +1808,7 @@
 >>> [6, 32]
 @
 
-=== Technical Remark (Representation Polymorphism)
+==== __Technical Remark (Representation Polymorphism)__
 
 @($)@ is fully representation-polymorphic. This allows it to also be used with arguments of unlifted and even unboxed kinds, such as unboxed integers:
 
diff --git a/GHC/IO/Handle/Text.hs b/GHC/IO/Handle/Text.hs
--- a/GHC/IO/Handle/Text.hs
+++ b/GHC/IO/Handle/Text.hs
@@ -174,16 +174,28 @@
 
 -- | Computation 'hGetLine' @hdl@ reads a line from the file or
 -- channel managed by @hdl@.
+-- 'hGetLine' does not return the newline as part of the result.
 --
+-- A line is separated by the newline
+-- set with 'System.IO.hSetNewlineMode' or 'nativeNewline' by default.
+-- The read newline character(s) are not returned as part of the result.
+--
+-- If 'hGetLine' encounters end-of-file at any point while reading
+-- in the middle of a line, it is treated as a line terminator and the (partial)
+-- line is returned.
+--
 -- This operation may fail with:
 --
 --  * 'isEOFError' if the end of file is encountered when reading
 --    the /first/ character of the line.
 --
--- If 'hGetLine' encounters end-of-file at any other point while reading
--- in a line, it is treated as a line terminator and the (partial)
--- line is returned.
-
+-- ==== __Examples__
+--
+-- >>> withFile "/home/user/foo" ReadMode hGetLine >>= putStrLn
+-- this is the first line of the file :O
+--
+-- >>> withFile "/home/user/bar" ReadMode (replicateM 3 . hGetLine)
+-- ["this is the first line","this is the second line","this is the third line"]
 hGetLine :: Handle -> IO String
 hGetLine h =
   wantReadableHandle_ "hGetLine" h $ \ handle_ ->
diff --git a/GHC/List.hs b/GHC/List.hs
--- a/GHC/List.hs
+++ b/GHC/List.hs
@@ -69,15 +69,16 @@
 
 -- | \(\mathcal{O}(1)\). Extract the first element of a list, which must be non-empty.
 --
+-- ===== __Examples__
+--
 -- >>> head [1, 2, 3]
 -- 1
+--
 -- >>> head [1..]
 -- 1
+--
 -- >>> head []
 -- *** Exception: Prelude.head: empty list
---
--- WARNING: This function is partial. You can use case-matching, 'uncons' or
--- 'listToMaybe' instead.
 head                    :: HasCallStack => [a] -> a
 head (x:_)              =  x
 head []                 =  badHead
@@ -105,10 +106,14 @@
 --
 -- @since 4.8.0.0
 --
+-- ==== __Examples__
+--
 -- >>> uncons []
 -- Nothing
+--
 -- >>> uncons [1]
 -- Just (1,[])
+--
 -- >>> uncons [1, 2, 3]
 -- Just (1,[2,3])
 uncons                  :: [a] -> Maybe (a, [a])
@@ -121,28 +126,34 @@
 -- * If the list is non-empty, returns @'Just' (xs, x)@,
 -- where @xs@ is the 'init'ial part of the list and @x@ is its 'last' element.
 --
--- @since 4.19.0.0
 --
+-- 'unsnoc' is dual to 'uncons': for a finite list @xs@
+--
+-- > unsnoc xs = (\(hd, tl) -> (reverse tl, hd)) <$> uncons (reverse xs)
+--
+-- ==== __Examples__
+--
 -- >>> unsnoc []
 -- Nothing
+--
 -- >>> unsnoc [1]
 -- Just ([],1)
+--
 -- >>> unsnoc [1, 2, 3]
 -- Just ([1,2],3)
 --
--- Laziness:
+-- ==== __Laziness__
 --
 -- >>> fst <$> unsnoc [undefined]
 -- Just []
+--
 -- >>> head . fst <$> unsnoc (1 : undefined)
 -- Just *** Exception: Prelude.undefined
+--
 -- >>> head . fst <$> unsnoc (1 : 2 : undefined)
 -- Just 1
 --
--- 'unsnoc' is dual to 'uncons': for a finite list @xs@
---
--- > unsnoc xs = (\(hd, tl) -> (reverse tl, hd)) <$> uncons (reverse xs)
---
+-- @since 4.19.0.0
 unsnoc :: [a] -> Maybe ([a], a)
 -- The lazy pattern ~(a, b) is important to be productive on infinite lists
 -- and not to be prone to stack overflows.
@@ -153,15 +164,16 @@
 -- | \(\mathcal{O}(1)\). Extract the elements after the head of a list, which
 -- must be non-empty.
 --
+-- ==== __Examples__
+--
 -- >>> tail [1, 2, 3]
 -- [2,3]
+--
 -- >>> tail [1]
 -- []
+--
 -- >>> tail []
 -- *** Exception: Prelude.tail: empty list
---
--- WARNING: This function is partial. You can use case-matching or 'uncons'
--- instead.
 tail                    :: HasCallStack => [a] -> [a]
 tail (_:xs)             =  xs
 tail []                 =  errorEmptyList "tail"
@@ -171,14 +183,18 @@
 -- | \(\mathcal{O}(n)\). Extract the last element of a list, which must be
 -- finite and non-empty.
 --
+-- WARNING: This function is partial. Consider using 'unsnoc' instead.
+--
+-- ==== __Examples__
+--
 -- >>> last [1, 2, 3]
 -- 3
+--
 -- >>> last [1..]
 -- * Hangs forever *
+--
 -- >>> last []
 -- *** Exception: Prelude.last: empty list
---
--- WARNING: This function is partial. Consider using 'unsnoc' instead.
 last                    :: HasCallStack => [a] -> a
 #if defined(USE_REPORT_PRELUDE)
 last [x]                =  x
@@ -199,14 +215,18 @@
 -- | \(\mathcal{O}(n)\). Return all the elements of a list except the last one.
 -- The list must be non-empty.
 --
+-- WARNING: This function is partial. Consider using 'unsnoc' instead.
+--
+-- ==== __Examples__
+--
 -- >>> init [1, 2, 3]
 -- [1,2]
+--
 -- >>> init [1]
 -- []
+--
 -- >>> init []
 -- *** Exception: Prelude.init: empty list
---
--- WARNING: This function is partial. Consider using 'unsnoc' instead.
 init                    :: HasCallStack => [a] -> [a]
 #if defined(USE_REPORT_PRELUDE)
 init [x]                =  []
@@ -270,8 +290,16 @@
 --
 -- > filter p xs = [ x | x <- xs, p x]
 --
+-- ==== __Examples__
+--
 -- >>> filter odd [1, 2, 3]
 -- [1,3]
+--
+-- >>> filter (\l -> length l > 3) ["Hello", ", ", "World", "!"]
+-- ["Hello","World"]
+--
+-- >>> filter (/= 3) [1, 2, 3, 4, 3, 2, 1]
+-- [1,2,4,2,1]
 {-# NOINLINE [1] filter #-}
 filter :: (a -> Bool) -> [a] -> [a]
 filter _pred []    = []
@@ -478,16 +506,23 @@
 --
 -- > last (scanl f z xs) == foldl f z xs
 --
+-- ==== __Examples__
+--
 -- >>> scanl (+) 0 [1..4]
 -- [0,1,3,6,10]
+--
 -- >>> scanl (+) 42 []
 -- [42]
+--
 -- >>> scanl (-) 100 [1..4]
 -- [100,99,97,94,90]
+--
 -- >>> scanl (\reversedString nextChar -> nextChar : reversedString) "foo" ['a', 'b', 'c', 'd']
 -- ["foo","afoo","bafoo","cbafoo","dcbafoo"]
+--
 -- >>> take 10 (scanl (+) 0 [1..])
 -- [0,1,3,6,10,15,21,28,36,45]
+--
 -- >>> take 1 (scanl undefined 'a' undefined)
 -- "a"
 
@@ -525,18 +560,26 @@
 --
 -- > scanl1 f [x1, x2, ...] == [x1, x1 `f` x2, ...]
 --
+-- ==== __Examples__
+--
 -- >>> scanl1 (+) [1..4]
 -- [1,3,6,10]
+--
 -- >>> scanl1 (+) []
 -- []
+--
 -- >>> scanl1 (-) [1..4]
 -- [1,-1,-4,-8]
+--
 -- >>> scanl1 (&&) [True, False, True, True]
 -- [True,False,False,False]
+--
 -- >>> scanl1 (||) [False, False, True, True]
 -- [False,False,True,True]
+--
 -- >>> take 10 (scanl1 (+) [1..])
 -- [1,3,6,10,15,21,28,36,45,55]
+--
 -- >>> take 1 (scanl1 undefined ('a' : undefined))
 -- "a"
 scanl1                  :: (a -> a -> a) -> [a] -> [a]
@@ -655,14 +698,20 @@
 --
 -- > head (scanr f z xs) == foldr f z xs.
 --
+-- ==== __Examples__
+--
 -- >>> scanr (+) 0 [1..4]
 -- [10,9,7,4,0]
+--
 -- >>> scanr (+) 42 []
 -- [42]
+--
 -- >>> scanr (-) 100 [1..4]
 -- [98,-97,99,-96,100]
+--
 -- >>> scanr (\nextChar reversedString -> nextChar : reversedString) "foo" ['a', 'b', 'c', 'd']
 -- ["abcdfoo","bcdfoo","cdfoo","dfoo","foo"]
+--
 -- >>> force $ scanr (+) 0 [1..]
 -- *** Exception: stack overflow
 {-# NOINLINE [1] scanr #-}
@@ -720,16 +769,23 @@
 -- | \(\mathcal{O}(n)\). 'scanr1' is a variant of 'scanr' that has no starting
 -- value argument.
 --
+-- ==== __Examples__
+--
 -- >>> scanr1 (+) [1..4]
 -- [10,9,7,4]
+--
 -- >>> scanr1 (+) []
 -- []
+--
 -- >>> scanr1 (-) [1..4]
 -- [-2,3,-1,4]
+--
 -- >>> scanr1 (&&) [True, False, True, True]
 -- [False,False,True,True]
+--
 -- >>> scanr1 (||) [True, True, False, False]
 -- [True,True,False,False]
+--
 -- >>> force $ scanr1 (+) [1..]
 -- *** Exception: stack overflow
 scanr1                  :: (a -> a -> a) -> [a] -> [a]
@@ -789,17 +845,27 @@
 --
 -- > iterate f x == [x, f x, f (f x), ...]
 --
+-- ==== __Laziness__
+--
 -- Note that 'iterate' is lazy, potentially leading to thunk build-up if
 -- the consumer doesn't force each iterate. See 'iterate'' for a strict
 -- variant of this function.
 --
+-- >>> take 1 $ iterate undefined 42
+-- [42]
+--
+-- ==== __Examples__
+--
 -- >>> take 10 $ iterate not True
 -- [True,False,True,False,True,False,True,False,True,False]
+--
 -- >>> take 10 $ iterate (+3) 42
 -- [42,45,48,51,54,57,60,63,66,69]
--- >>> take 1 $ iterate undefined 42
--- [42]
 --
+-- @iterate id == 'repeat'@:
+--
+-- >>> take 10 $ iterate id 1
+-- [1,1,1,1,1,1,1,1,1,1]
 {-# NOINLINE [1] iterate #-}
 iterate :: (a -> a) -> a -> [a]
 iterate f x =  x : iterate f (f x)
@@ -823,7 +889,6 @@
 --
 -- >>> take 1 $ iterate' undefined 42
 -- *** Exception: Prelude.undefined
---
 {-# NOINLINE [1] iterate' #-}
 iterate' :: (a -> a) -> a -> [a]
 iterate' f x =
@@ -845,8 +910,13 @@
 
 -- | 'repeat' @x@ is an infinite list, with @x@ the value of every element.
 --
--- >>> repeat 17
--- [17,17,17,17,17,17,17,17,17...
+-- ==== __Examples__
+--
+-- >>> take 10 $ repeat 17
+-- [17,17,17,17,17,17,17,17,17, 17]
+--
+-- >>> repeat undefined
+-- [*** Exception: Prelude.undefined
 repeat :: a -> [a]
 {-# INLINE [0] repeat #-}
 -- The pragma just gives the rules more chance to fire
@@ -867,10 +937,14 @@
 -- It is an instance of the more general 'Data.List.genericReplicate',
 -- in which @n@ may be of any integral type.
 --
+-- ==== __Examples__
+--
 -- >>> replicate 0 True
 -- []
+--
 -- >>> replicate (-1) True
 -- []
+--
 -- >>> replicate 4 True
 -- [True,True,True,True]
 {-# INLINE replicate #-}
@@ -881,15 +955,19 @@
 -- the infinite repetition of the original list.  It is the identity
 -- on infinite lists.
 --
+-- ==== __Examples__
+--
 -- >>> cycle []
 -- *** Exception: Prelude.cycle: empty list
+--
 -- >>> take 10 (cycle [42])
 -- [42,42,42,42,42,42,42,42,42,42]
+--
 -- >>> take 10 (cycle [2, 5, 7])
 -- [2,5,7,2,5,7,2,5,7,2]
+--
 -- >>> take 1 (cycle (42 : undefined))
 -- [42]
---
 cycle                   :: HasCallStack => [a] -> [a]
 cycle []                = errorEmptyList "cycle"
 cycle xs                = xs' where xs' = xs ++ xs'
@@ -897,22 +975,27 @@
 -- | 'takeWhile', applied to a predicate @p@ and a list @xs@, returns the
 -- longest prefix (possibly empty) of @xs@ of elements that satisfy @p@.
 --
--- >>> takeWhile (< 3) [1,2,3,4,1,2,3,4]
--- [1,2]
--- >>> takeWhile (< 9) [1,2,3]
--- [1,2,3]
--- >>> takeWhile (< 0) [1,2,3]
--- []
---
--- Laziness:
+-- ==== __Laziness__
 --
 -- >>> takeWhile (const False) undefined
 -- *** Exception: Prelude.undefined
+--
 -- >>> takeWhile (const False) (undefined : undefined)
 -- []
+--
 -- >>> take 1 (takeWhile (const True) (1 : undefined))
 -- [1]
 --
+-- ==== __Examples__
+--
+-- >>> takeWhile (< 3) [1,2,3,4,1,2,3,4]
+-- [1,2]
+--
+-- >>> takeWhile (< 9) [1,2,3]
+-- [1,2,3]
+--
+-- >>> takeWhile (< 0) [1,2,3]
+-- []
 {-# NOINLINE [1] takeWhile #-}
 takeWhile               :: (a -> Bool) -> [a] -> [a]
 takeWhile _ []          =  []
@@ -941,10 +1024,14 @@
 
 -- | 'dropWhile' @p xs@ returns the suffix remaining after 'takeWhile' @p xs@.
 --
+-- ==== __Examples__
+--
 -- >>> dropWhile (< 3) [1,2,3,4,5,1,2,3]
 -- [3,4,5,1,2,3]
+--
 -- >>> dropWhile (< 9) [1,2,3]
 -- []
+--
 -- >>> dropWhile (< 0) [1,2,3]
 -- [1,2,3]
 dropWhile               :: (a -> Bool) -> [a] -> [a]
@@ -956,28 +1043,35 @@
 -- | 'take' @n@, applied to a list @xs@, returns the prefix of @xs@
 -- of length @n@, or @xs@ itself if @n >= 'length' xs@.
 --
+-- It is an instance of the more general 'Data.List.genericTake',
+-- in which @n@ may be of any integral type.
+--
+-- ==== __Laziness__
+--
+-- >>> take 0 undefined
+-- []
+-- >>> take 2 (1 : 2 : undefined)
+-- [1,2]
+--
+-- ==== __Examples__
+--
 -- >>> take 5 "Hello World!"
 -- "Hello"
+--
 -- >>> take 3 [1,2,3,4,5]
 -- [1,2,3]
+--
 -- >>> take 3 [1,2]
 -- [1,2]
+--
 -- >>> take 3 []
 -- []
+--
 -- >>> take (-1) [1,2]
 -- []
--- >>> take 0 [1,2]
--- []
 --
--- Laziness:
---
--- >>> take 0 undefined
+-- >>> take 0 [1,2]
 -- []
--- >>> take 1 (1 : undefined)
--- [1]
---
--- It is an instance of the more general 'Data.List.genericTake',
--- in which @n@ may be of any integral type.
 take                   :: Int -> [a] -> [a]
 #if defined(USE_REPORT_PRELUDE)
 take n _      | n <= 0 =  []
@@ -1034,21 +1128,28 @@
 -- | 'drop' @n xs@ returns the suffix of @xs@
 -- after the first @n@ elements, or @[]@ if @n >= 'length' xs@.
 --
+-- It is an instance of the more general 'Data.List.genericDrop',
+-- in which @n@ may be of any integral type.
+--
+-- ==== __Examples__
+--
 -- >>> drop 6 "Hello World!"
 -- "World!"
+--
 -- >>> drop 3 [1,2,3,4,5]
 -- [4,5]
+--
 -- >>> drop 3 [1,2]
 -- []
+--
 -- >>> drop 3 []
 -- []
+--
 -- >>> drop (-1) [1,2]
 -- [1,2]
+--
 -- >>> drop 0 [1,2]
 -- [1,2]
---
--- It is an instance of the more general 'Data.List.genericDrop',
--- in which @n@ may be of any integral type.
 drop                   :: Int -> [a] -> [a]
 #if defined(USE_REPORT_PRELUDE)
 drop n xs     | n <= 0 =  xs
@@ -1071,34 +1172,45 @@
 -- | 'splitAt' @n xs@ returns a tuple where first element is @xs@ prefix of
 -- length @n@ and second element is the remainder of the list:
 --
+-- 'splitAt' is an instance of the more general 'Data.List.genericSplitAt',
+-- in which @n@ may be of any integral type.
+--
+-- ==== __Laziness__
+--
+-- It is equivalent to @('take' n xs, 'drop' n xs)@
+-- unless @n@ is @_|_@:
+-- @splitAt _|_ xs = _|_@, not @(_|_, _|_)@).
+--
+-- The first component of the tuple is produced lazily:
+--
+-- >>> fst (splitAt 0 undefined)
+-- []
+--
+-- >>> take 1 (fst (splitAt 10 (1 : undefined)))
+-- [1]
+--
+-- ==== __Examples__
+--
 -- >>> splitAt 6 "Hello World!"
 -- ("Hello ","World!")
+--
 -- >>> splitAt 3 [1,2,3,4,5]
 -- ([1,2,3],[4,5])
+--
 -- >>> splitAt 1 [1,2,3]
 -- ([1],[2,3])
+--
 -- >>> splitAt 3 [1,2,3]
 -- ([1,2,3],[])
+--
 -- >>> splitAt 4 [1,2,3]
 -- ([1,2,3],[])
+--
 -- >>> splitAt 0 [1,2,3]
 -- ([],[1,2,3])
+--
 -- >>> splitAt (-1) [1,2,3]
 -- ([],[1,2,3])
---
--- It is equivalent to @('take' n xs, 'drop' n xs)@
--- unless @n@ is @_|_@:
--- @splitAt _|_ xs = _|_@, not @(_|_, _|_)@).
---
--- The first component of the tuple is produced lazily:
---
--- >>> fst (splitAt 0 undefined)
--- []
--- >>> take 1 (fst (splitAt 10 (1 : undefined)))
--- [1]
---
--- 'splitAt' is an instance of the more general 'Data.List.genericSplitAt',
--- in which @n@ may be of any integral type.
 splitAt                :: Int -> [a] -> ([a],[a])
 
 #if defined(USE_REPORT_PRELUDE)
@@ -1120,16 +1232,9 @@
 -- first element is the longest prefix (possibly empty) of @xs@ of elements that
 -- satisfy @p@ and second element is the remainder of the list:
 --
--- >>> span (< 3) [1,2,3,4,1,2,3,4]
--- ([1,2],[3,4,1,2,3,4])
--- >>> span (< 9) [1,2,3]
--- ([1,2,3],[])
--- >>> span (< 0) [1,2,3]
--- ([],[1,2,3])
---
 -- 'span' @p xs@ is equivalent to @('takeWhile' p xs, 'dropWhile' p xs)@, even if @p@ is @_|_@.
 --
--- Laziness:
+-- ==== __Laziness__
 --
 -- >>> span undefined []
 -- ([],[])
@@ -1145,6 +1250,16 @@
 -- >>> take 10 (fst (span (const True) [1..]))
 -- [1,2,3,4,5,6,7,8,9,10]
 --
+-- ==== __Examples__
+--
+-- >>> span (< 3) [1,2,3,4,1,2,3,4]
+-- ([1,2],[3,4,1,2,3,4])
+--
+-- >>> span (< 9) [1,2,3]
+-- ([1,2,3],[])
+--
+-- >>> span (< 0) [1,2,3]
+-- ([],[1,2,3])
 span                    :: (a -> Bool) -> [a] -> ([a],[a])
 span _ xs@[]            =  (xs, xs)
 span p xs@(x:xs')
@@ -1155,25 +1270,21 @@
 -- first element is longest prefix (possibly empty) of @xs@ of elements that
 -- /do not satisfy/ @p@ and second element is the remainder of the list:
 --
--- >>> break (> 3) [1,2,3,4,1,2,3,4]
--- ([1,2,3],[4,1,2,3,4])
--- >>> break (< 9) [1,2,3]
--- ([],[1,2,3])
--- >>> break (> 9) [1,2,3]
--- ([1,2,3],[])
---
 -- 'break' @p@ is equivalent to @'span' ('not' . p)@
 -- and consequently to @('takeWhile' ('not' . p) xs, 'dropWhile' ('not' . p) xs)@,
 -- even if @p@ is @_|_@.
 --
--- Laziness:
+-- ==== __Laziness__
 --
 -- >>> break undefined []
 -- ([],[])
+--
 -- >>> fst (break (const True) undefined)
 -- *** Exception: Prelude.undefined
+--
 -- >>> fst (break (const True) (undefined : undefined))
 -- []
+--
 -- >>> take 1 (fst (break (const False) (1 : undefined)))
 -- [1]
 --
@@ -1182,6 +1293,16 @@
 -- >>> take 10 (fst (break (const False) [1..]))
 -- [1,2,3,4,5,6,7,8,9,10]
 --
+-- ==== __Examples__
+--
+-- >>> break (> 3) [1,2,3,4,1,2,3,4]
+-- ([1,2,3],[4,1,2,3,4])
+--
+-- >>> break (< 9) [1,2,3]
+-- ([],[1,2,3])
+--
+-- >>> break (> 9) [1,2,3]
+-- ([1,2,3],[])
 break                   :: (a -> Bool) -> [a] -> ([a],[a])
 #if defined(USE_REPORT_PRELUDE)
 break p                 =  span (not . p)
@@ -1193,15 +1314,30 @@
            | otherwise  =  let (ys,zs) = break p xs' in (x:ys,zs)
 #endif
 
--- | 'reverse' @xs@ returns the elements of @xs@ in reverse order.
+-- | \(\mathcal{O}(n)\). 'reverse' @xs@ returns the elements of @xs@ in reverse order.
 -- @xs@ must be finite.
 --
+-- ==== __Laziness__
+--
+-- 'reverse' is lazy in its elements.
+--
+-- >>> head (reverse [undefined, 1])
+-- 1
+--
+-- >>> reverse (1 : 2 : undefined)
+-- *** Exception: Prelude.undefined
+--
+-- ==== __Examples__
+--
 -- >>> reverse []
 -- []
+--
 -- >>> reverse [42]
 -- [42]
+--
 -- >>> reverse [2,5,7]
 -- [7,5,2]
+--
 -- >>> reverse [1..]
 -- * Hangs forever *
 reverse                 :: [a] -> [a]
@@ -1218,16 +1354,23 @@
 -- 'True', the list must be finite; 'False', however, results from a 'False'
 -- value at a finite index of a finite or infinite list.
 --
+-- ==== __Examples__
+--
 -- >>> and []
 -- True
+--
 -- >>> and [True]
 -- True
+--
 -- >>> and [False]
 -- False
+--
 -- >>> and [True, True, False]
 -- False
+--
 -- >>> and (False : repeat True) -- Infinite list [False,True,True,True,True,True,True...
 -- False
+--
 -- >>> and (repeat True)
 -- * Hangs forever *
 and                     :: [Bool] -> Bool
@@ -1248,16 +1391,23 @@
 -- 'False', the list must be finite; 'True', however, results from a 'True'
 -- value at a finite index of a finite or infinite list.
 --
+-- ==== __Examples__
+--
 -- >>> or []
 -- False
+--
 -- >>> or [True]
 -- True
+--
 -- >>> or [False]
 -- False
+--
 -- >>> or [True, True, False]
 -- True
+--
 -- >>> or (True : repeat False) -- Infinite list [True,False,False,False,False,False,False...
 -- True
+--
 -- >>> or (repeat False)
 -- * Hangs forever *
 or                      :: [Bool] -> Bool
@@ -1280,14 +1430,20 @@
 -- value for the predicate applied to an element at a finite index of a finite
 -- or infinite list.
 --
+-- ==== __Examples__
+--
 -- >>> any (> 3) []
 -- False
+--
 -- >>> any (> 3) [1,2]
 -- False
+--
 -- >>> any (> 3) [1,2,3,4,5]
 -- True
+--
 -- >>> any (> 3) [1..]
 -- True
+--
 -- >>> any (> 3) [0, -1..]
 -- * Hangs forever *
 any                     :: (a -> Bool) -> [a] -> Bool
@@ -1311,14 +1467,20 @@
 -- value for the predicate applied to an element at a finite index of a finite
 -- or infinite list.
 --
+-- ==== __Examples__
+--
 -- >>> all (> 3) []
 -- True
+--
 -- >>> all (> 3) [1,2]
 -- False
+--
 -- >>> all (> 3) [1,2,3,4,5]
 -- False
+--
 -- >>> all (> 3) [1..]
 -- False
+--
 -- >>> all (> 3) [4..]
 -- * Hangs forever *
 all                     :: (a -> Bool) -> [a] -> Bool
@@ -1341,14 +1503,20 @@
 -- 'False', the list must be finite; 'True', however, results from an element
 -- equal to @x@ found at a finite index of a finite or infinite list.
 --
+-- ==== __Examples__
+--
 -- >>> 3 `elem` []
 -- False
+--
 -- >>> 3 `elem` [1,2]
 -- False
+--
 -- >>> 3 `elem` [1,2,3,4,5]
 -- True
+--
 -- >>> 3 `elem` [1..]
 -- True
+--
 -- >>> 3 `elem` [4..]
 -- * Hangs forever *
 elem                    :: (Eq a) => a -> [a] -> Bool
@@ -1366,14 +1534,20 @@
 
 -- | 'notElem' is the negation of 'elem'.
 --
+-- ==== __Examples__
+--
 -- >>> 3 `notElem` []
 -- True
+--
 -- >>> 3 `notElem` [1,2]
 -- True
+--
 -- >>> 3 `notElem` [1,2,3,4,5]
 -- False
+--
 -- >>> 3 `notElem` [1..]
 -- False
+--
 -- >>> 3 `notElem` [4..]
 -- * Hangs forever *
 notElem                 :: (Eq a) => a -> [a] -> Bool
@@ -1393,13 +1567,16 @@
 -- list.
 -- For the result to be 'Nothing', the list must be finite.
 --
+-- ==== __Examples__
+--
 -- >>> lookup 2 []
 -- Nothing
+--
 -- >>> lookup 2 [(1, "first")]
 -- Nothing
+--
 -- >>> lookup 2 [(1, "first"), (2, "second"), (3, "third")]
 -- Just "second"
---
 lookup                  :: (Eq a) => a -> [(a,b)] -> Maybe b
 lookup _key []          =  Nothing
 lookup  key ((x,y):xys)
@@ -1411,10 +1588,16 @@
 --
 -- > concatMap f xs == (concat . map f) xs
 --
+-- ==== __Examples__
+--
 -- >>> concatMap (\i -> [-i,i]) []
 -- []
--- >>> concatMap (\i -> [-i,i]) [1,2,3]
+--
+-- >>> concatMap (\i -> [-i, i]) [1, 2, 3]
 -- [-1,1,-2,2,-3,3]
+--
+-- >>> concatMap ('replicate' 3) [0, 2, 4]
+-- [0,0,0,2,2,2,4,4,4]
 concatMap               :: (a -> [b]) -> [a] -> [b]
 concatMap f             =  foldr ((++) . f) []
 
@@ -1428,12 +1611,16 @@
 
 -- | Concatenate a list of lists.
 --
+-- ==== __Examples__
+--
+-- >>> concat [[1,2,3], [4,5], [6], []]
+-- [1,2,3,4,5,6]
+--
 -- >>> concat []
 -- []
+--
 -- >>> concat [[42]]
 -- [42]
--- >>> concat [[1,2,3], [4,5], [6], []]
--- [1,2,3,4,5,6]
 concat :: [[a]] -> [a]
 concat = foldr (++) []
 
@@ -1449,19 +1636,24 @@
 -- It is an instance of the more general 'Data.List.genericIndex',
 -- which takes an index of any integral type.
 --
+-- WARNING: This function is partial, and should only be used if you are
+-- sure that the indexing will not fail. Otherwise, use 'Data.List.!?'.
+--
+-- WARNING: This function takes linear time in the index.
+--
+-- ==== __Examples__
+--
 -- >>> ['a', 'b', 'c'] !! 0
 -- 'a'
+--
 -- >>> ['a', 'b', 'c'] !! 2
 -- 'c'
+--
 -- >>> ['a', 'b', 'c'] !! 3
 -- *** Exception: Prelude.!!: index too large
+--
 -- >>> ['a', 'b', 'c'] !! (-1)
 -- *** Exception: Prelude.!!: negative index
---
--- WARNING: This function is partial, and should only be used if you are
--- sure that the indexing will not fail. Otherwise, use 'Data.List.!?'.
---
--- WARNING: This function takes linear time in the index.
 #if defined(USE_REPORT_PRELUDE)
 (!!)                    :: [a] -> Int -> a
 xs     !! n | n < 0 =  errorWithoutStackTrace "Prelude.!!: negative index"
@@ -1493,18 +1685,23 @@
 -- | List index (subscript) operator, starting from 0. Returns 'Nothing'
 -- if the index is out of bounds
 --
+-- This is the total variant of the partial '!!' operator.
+--
+-- WARNING: This function takes linear time in the index.
+--
+-- ==== __Examples__
+--
 -- >>> ['a', 'b', 'c'] !? 0
 -- Just 'a'
+--
 -- >>> ['a', 'b', 'c'] !? 2
 -- Just 'c'
+--
 -- >>> ['a', 'b', 'c'] !? 3
 -- Nothing
+--
 -- >>> ['a', 'b', 'c'] !? (-1)
 -- Nothing
---
--- This is the total variant of the partial '!!' operator.
---
--- WARNING: This function takes linear time in the index.
 (!?) :: [a] -> Int -> Maybe a
 
 {-# INLINABLE (!?) #-}
@@ -1615,31 +1812,36 @@
 -- | \(\mathcal{O}(\min(m,n))\). 'zip' takes two lists and returns a list of
 -- corresponding pairs.
 --
--- >>> zip [1, 2] ['a', 'b']
--- [(1,'a'),(2,'b')]
+-- 'zip' is right-lazy:
 --
+-- >>> zip [] undefined
+-- []
+-- >>> zip undefined []
+-- *** Exception: Prelude.undefined
+-- ...
+--
+-- 'zip' is capable of list fusion, but it is restricted to its
+-- first list argument and its resulting list.
+--
+-- ==== __Examples__
+--
+-- >>> zip [1, 2, 3] ['a', 'b', 'c']
+-- [(1,'a'),(2,'b'),(3,'c')]
+--
 -- If one input list is shorter than the other, excess elements of the longer
 -- list are discarded, even if one of the lists is infinite:
 --
 -- >>> zip [1] ['a', 'b']
 -- [(1,'a')]
+--
 -- >>> zip [1, 2] ['a']
 -- [(1,'a')]
+--
 -- >>> zip [] [1..]
 -- []
--- >>> zip [1..] []
--- []
 --
--- 'zip' is right-lazy:
---
--- >>> zip [] undefined
+-- >>> zip [1..] []
 -- []
--- >>> zip undefined []
--- *** Exception: Prelude.undefined
--- ...
---
--- 'zip' is capable of list fusion, but it is restricted to its
--- first list argument and its resulting list.
 {-# NOINLINE [1] zip #-}  -- See Note [Fusion for zipN/zipWithN]
 zip :: [a] -> [b] -> [(a,b)]
 zip []     _bs    = []
@@ -1686,12 +1888,7 @@
 -- > zipWith (,) xs ys == zip xs ys
 -- > zipWith f [x1,x2,x3..] [y1,y2,y3..] == [f x1 y1, f x2 y2, f x3 y3..]
 --
--- For example, @'zipWith' (+)@ is applied to two lists to produce the list of
--- corresponding sums:
 --
--- >>> zipWith (+) [1, 2, 3] [4, 5, 6]
--- [5,7,9]
---
 -- 'zipWith' is right-lazy:
 --
 -- >>> let f = undefined
@@ -1700,6 +1897,17 @@
 --
 -- 'zipWith' is capable of list fusion, but it is restricted to its
 -- first list argument and its resulting list.
+--
+-- ==== __Examples__
+--
+-- @'zipWith' '(+)'@ can be applied to two lists to produce the list of
+-- corresponding sums:
+--
+-- >>> zipWith (+) [1, 2, 3] [4, 5, 6]
+-- [5,7,9]
+--
+-- >>> zipWith (++) ["hello ", "foo"] ["world!", "bar"]
+-- ["hello world!","foobar"]
 {-# NOINLINE [1] zipWith #-}  -- See Note [Fusion for zipN/zipWithN]
 zipWith :: (a->b->c) -> [a]->[b]->[c]
 zipWith f = go
@@ -1719,7 +1927,7 @@
 "zipWithList"   [1]  forall f.  foldr2 (zipWithFB (:) f) [] = zipWith f
   #-}
 
--- | The 'zipWith3' function takes a function which combines three
+-- | \(\mathcal{O}(\min(l,m,n))\). The 'zipWith3' function takes a function which combines three
 -- elements, as well as three lists and returns a list of the function applied
 -- to corresponding elements, analogous to 'zipWith'.
 -- It is capable of list fusion, but it is restricted to its
@@ -1727,6 +1935,14 @@
 --
 -- > zipWith3 (,,) xs ys zs == zip3 xs ys zs
 -- > zipWith3 f [x1,x2,x3..] [y1,y2,y3..] [z1,z2,z3..] == [f x1 y1 z1, f x2 y2 z2, f x3 y3 z3..]
+--
+-- ==== __Examples__
+--
+-- >>> zipWith3 (\x y z -> [x, y, z]) "123" "abc" "xyz"
+-- ["1ax","2by","3cz"]
+--
+-- >>> zipWith3 (\x y z -> (x * y) + z) [1, 2, 3] [4, 5, 6] [7, 8, 9]
+-- [11,18,27]
 {-# NOINLINE [1] zipWith3 #-}
 zipWith3                :: (a->b->c->d) -> [a]->[b]->[c]->[d]
 zipWith3 z = go
@@ -1746,8 +1962,11 @@
 -- | 'unzip' transforms a list of pairs into a list of first components
 -- and a list of second components.
 --
+-- ==== __Examples__
+--
 -- >>> unzip []
 -- ([],[])
+--
 -- >>> unzip [(1, 'a'), (2, 'b')]
 -- ([1,2],"ab")
 unzip    :: [(a,b)] -> ([a],[b])
@@ -1757,10 +1976,13 @@
 unzip    =  foldr (\(a,b) ~(as,bs) -> (a:as,b:bs)) ([],[])
 
 -- | The 'unzip3' function takes a list of triples and returns three
--- lists, analogous to 'unzip'.
+-- lists of the respective components, analogous to 'unzip'.
 --
+-- ==== __Examples__
+--
 -- >>> unzip3 []
 -- ([],[],[])
+--
 -- >>> unzip3 [(1, 'a', True), (2, 'b', False)]
 -- ([1,2],"ab",[True,False])
 unzip3   :: [(a,b,c)] -> ([a],[b],[c])
diff --git a/System/Posix/Internals.hs b/System/Posix/Internals.hs
--- a/System/Posix/Internals.hs
+++ b/System/Posix/Internals.hs
@@ -517,7 +517,7 @@
     c_isatty :: CInt -> IO CInt
 foreign import javascript interruptible "h$base_lseek"
    c_lseek :: CInt -> COff -> CInt -> IO COff
-foreign import javascript interruptible "h$base_lstat" -- fixme wrong type
+foreign import javascript interruptible "h$base_lstat"
    lstat :: CFilePath -> Ptr CStat -> IO CInt
 foreign import javascript interruptible "h$base_open"
    c_open :: CFilePath -> CInt -> CMode -> IO CInt
diff --git a/base.cabal b/base.cabal
--- a/base.cabal
+++ b/base.cabal
@@ -1,6 +1,6 @@
 cabal-version:  3.0
 name:           base
-version:        4.19.0.0
+version:        4.19.1.0
 -- NOTE: Don't forget to update ./changelog.md
 
 license:        BSD-3-Clause
diff --git a/changelog.md b/changelog.md
--- a/changelog.md
+++ b/changelog.md
@@ -1,5 +1,10 @@
 # Changelog for [`base` package](http://hackage.haskell.org/package/base)
 
+## 4.19.1.0 *October 2023*
+  * Shipped with GHC 9.8.2
+  * Improve documentation of various functions
+  * Implement primitives like `lstat` and `rmdir`, for the JS backend.
+
 ## 4.19.0.0 *October 2023*
 
   * Shipped with GHC 9.8.1
@@ -40,7 +45,8 @@
   * Deprecate `Data.List.NonEmpty.unzip` ([CLC proposal #86](https://github.com/haskell/core-libraries-committee/issues/86))
   * Fixed exponent overflow/underflow bugs in the `Read` instances for `Float` and `Double` ([CLC proposal #192](https://github.com/haskell/core-libraries-committee/issues/192))
   * Implement `copyBytes`, `fillBytes`, `moveBytes` and `stimes` for `Data.Array.Byte.ByteArray` using primops ([CLC proposal #188](https://github.com/haskell/core-libraries-committee/issues/188))
-  * Add rewrite rules for conversion between `Int64`/`Word64` and `Float`/`Double` on 64-bit architectures ([CLC proposal #203](https://github.com/haskell/core-libraries-committee/issues/203)).
+  * Add rewrite rules for conversion between `Int64` / `Word64` and `Float` / `Double` on 64-bit architectures ([CLC proposal #203](https://github.com/haskell/core-libraries-committee/issues/203)).
+  * `Generic` instances for tuples now expose `Unit`, `Tuple2`, `Tuple3`, ..., `Tuple64` as the actual names for tuple type constructors ([GHC proposal #475](https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/0475-tuple-syntax.rst)).
 
 ## 4.18.0.0 *March 2023*
 
diff --git a/jsbits/base.js b/jsbits/base.js
--- a/jsbits/base.js
+++ b/jsbits/base.js
@@ -247,6 +247,39 @@
         h$unsupported(-1, c);
 }
 
+function h$lstat(file, file_off, stat, stat_off) {
+  TRACE_IO("lstat")
+#ifndef GHCJS_BROWSER
+  if(h$isNode()) {
+    try {
+      var fs = h$fs.lstatSync(h$decodeUtf8z(file, file_off));
+      h$base_fillStat(fs, stat, stat_off);
+      return 0;
+    } catch(e) {
+      h$setErrno(e);
+      return -1;
+    }
+  } else
+#endif
+    h$unsupported(-1);
+}
+
+function h$rmdir(file, file_off) {
+  TRACE_IO("rmdir")
+#ifndef GHCJS_BROWSER
+  if(h$isNode()) {
+    try {
+      var fs = h$fs.rmdirSync(h$decodeUtf8z(file, file_off));
+      return 0;
+    } catch(e) {
+      h$setErrno(e);
+      return -1;
+    }
+  } else
+#endif
+    h$unsupported(-1);
+}
+
 function h$rename(old_path, old_path_off, new_path, new_path_off) {
   TRACE_IO("rename")
 #ifndef GHCJS_BROWSER
@@ -554,21 +587,32 @@
 const h$base_o_binary   = 0x00000;
 const h$base_at_fdcwd   = -100;
 
+function h$base_stat_check_mode(mode,p) {
+  // inspired by Node's checkModeProperty
+  var r = (mode & h$fs.constants.S_IFMT) === p;
+  return r ? 1 : 0;
+}
 
+function h$base_stat_check_mode(mode,p) {
+  // inspired by Node's checkModeProperty
+  var r = (mode & h$fs.constants.S_IFMT) === p;
+  return r ? 1 : 0;
+}
+
 function h$base_c_s_isreg(mode) {
-    return 1;
+  return h$base_stat_check_mode(mode,h$fs.constants.S_IFREG);
 }
 function h$base_c_s_ischr(mode) {
-    return 0;
+  return h$base_stat_check_mode(mode,h$fs.constants.S_IFCHR);
 }
 function h$base_c_s_isblk(mode) {
-    return 0;
+  return h$base_stat_check_mode(mode,h$fs.constants.S_IFBLK);
 }
 function h$base_c_s_isdir(mode) {
-    return 0; // fixme
+  return h$base_stat_check_mode(mode,h$fs.constants.S_IFDIR);
 }
 function h$base_c_s_isfifo(mode) {
-    return 0;
+  return h$base_stat_check_mode(mode,h$fs.constants.S_IFIFO);
 }
 function h$base_c_fcntl_read(fd,cmd) {
     return -1;
