diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,204 @@
+# Changelog
+
+## [1.1.1.0] - 2024-07-18
+
+### Added
+
+* `GenValid` instances for various newtypes in base
+
+## [1.1.0.0] - 2022-08-30
+
+### Added
+
+* `genListOf1`
+* `genMaybe`
+* `shrinkMaybe`
+* `shrinkNonEmpty`
+* `shrinkTriple`
+* `shrinkQuadruple`
+
+### Changed
+
+* Sped up shrinking of `NonEmpty` lists by an order of magnitude.
+* Changed shrinking of `Ratio`s to be 10x faster.
+* Reimplemented `shrinkT2`, `shrinkT3` and `shrinkT4` in terms of `shrinkTuple`, `shrinkTriple` and `shrinkQuadruple`.
+
+## [1.0.0.1] - 2021-11-20
+
+### Changed
+
+* Added compatibility with `lts-18.16`
+
+## [1.0.0.0] - 2021-11-20
+
+### Changed
+
+* The default implementation of `GenValid` now uses `genValidStructurally` and `shrinkValidStructurally`.
+
+### Removed
+
+* `GenUnchecked`: It is no longer necessary after changing the default implementation of `GenValid`.
+* `GenInvalid`: It was a misfeature.
+* `GenRelativeValidity`: It was a misfeature. 
+
+## [0.11.0.1] - 2021-06-20
+
+### Changed
+
+Updated the base lower bound to be more accurate.
+
+## [0.11.0.1] - 2020-04-28
+
+### Changed
+
+* Got rid of some unnecessary extra special values in the generator for double
+
+## [0.11.0.0] - 2020-04-12
+
+### Changed
+
+* Changed the genUnchecked and shrinkUnchecked definitions for Word8, Word16, Word32 and Int8, Int16, Int32 according to their new validity instance in validity 0.10.0.0
+
+## [0.10.0.2] - 2020-03-18
+
+### Changed
+
+* Better lower bound for genvalidity
+
+## [0.10.0.1] - 2020-02-20
+
+### Changed
+
+* Fixed a bug where shrinking ratios of bounded types would crash if the minimum bound was in the numerator.
+
+## [0.10.0.0] - 2020-02-10
+
+### Added
+
+* `Data.GenValidity.Utils.genIntX`
+* `Data.GenValidity.Utils.genWordX`
+* `Data.GenValidity.Utils.genFloat`
+* `Data.GenValidity.Utils.genDouble`
+* `Data.GenValidity.Utils.genFloatX`
+* `Data.GenValidity.Utils.genInteger`
+
+
+### Changed
+
+* Improved the cabal file
+* Sped up the 'genValid' generators for the following types
+  * ()
+  * Bool
+  * Ordering
+  * Char
+  * Int, Int8, Int16, Int32, Int64
+  * Word, Word8, Word16, Word32, Word64
+  * Float, Double
+* Improved the generators of
+  * Int, Int8, Int16, Int32, Int64
+    Now also generates extreme values, but mostly uniform values.
+  * Word, Word8, Word16, Word32, Word64
+    Now also generates extreme values, but mostly uniform values.
+  * Float, Double
+    Now also generates values around the bounds, but mostly uniform values.
+  * Natural, Integer
+    Now also generates numbers larger than can be contained in a single Word/Int.
+  * Ratio
+    Fixed a bug: no longer generates invalid ratios for fixed-sized numerators
+* Removed a lot of shrinking tests
+
+## [0.9.1.0] - 2019-12-04
+
+### Added
+
+* `genSplit6`, `genSplit7`, `genSplit8`
+* `genNonEmptyOf`
+
+### Changed
+
+* Changed `arbPartition` to generate nicer partitions.
+  This influences `genListOf` and `genTreeOf` and the
+  instances for all collections as well.
+
+## [0.9.0.1] - 2019-09-27
+
+### Changed
+
+* Tests for `genUtf16SurrogateCodePoint`
+
+## [0.9.0.0] - 2019-09-23
+
+### Added
+
+* `genUtf16SurrogateCodePoint`
+
+### Changed
+
+* Changed `GenValid Char` to generate UTF16 surrogate codepoints 10% of the time
+* Changed `GenValid Char` to ignore sizes.
+
+## [0.8.0.0] - 2019-03-06
+
+### Added
+
+* 'shrinkTuple'
+
+### Changed
+
+* Removed the 'GenUnchecked' constraint for 'GenValid' and 'GenInvalid'.
+
+## [0.7.0.2] - 2019-02-28
+
+### Added
+
+* 'shrinkT4'
+
+### Changed
+
+* Clearer docs
+
+## [0.7.0.1] - 2019-02-21
+
+### Changed
+
+* Sped up the shrinking test suite.
+
+## [0.7.0.0] - 2018-11-07
+
+### Changed
+
+* `genUnchecked` of `Double` and `Float` now generates `NaN`, `+Infinity`, `-Infinity` and `-0` according to the new version of `validity`.
+
+## [0.6.1.0] - 2018-10-06
+
+### Changed
+
+* Changed 'genValid`, `genUnchecked` and `genInvalid` for NonEmpty to better take the size into account.
+* Sped up `shrinkUnchecked` for `Maybe`
+* Sped up `shrinkValid` for `Maybe`
+* Sped up `shrinkUnchecked` for `Either`
+* Sped up `shrinkValid` for `Either`
+* Sped up `shrinkUnchecked` for `(,)`
+* Sped up `shrinkUnchecked` for `(,,)`
+* Sped up `shrinkUnchecked` for `(,,,)`
+* Sped up `shrinkValid` for lists
+* Sped up `shrinkValid` for `NonEmpty` lists
+
+## [0.6.0.0] - 2018-08-25
+
+### Added
+
+* `genValidStructurally` and `genValidStructurallyWithoutExtraChecking`
+* `shrinkValidStructurally` and `shrinkValidStructurallyWithoutExtraFiltering` with `structurallyValidRecursivelyShrink` and `structurallyValidSubterms`
+
+### Changed
+
+* `-0` is now a valid value for `Double` and `Float`.
+* `genUnchecked :: Gen Double` now also generates invalid values.
+* `arbPartition` now shuffles the partitions, which means that `genListOf` produces lists of elements with shuffled sizes.
+  This also fixes the same problem with `instance GenUnchecked a => GenUnchecked [a]`.
+
+## Older versions
+
+No history before version 0.6.0.0
+
diff --git a/LICENSE b/LICENSE
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
 The MIT License (MIT)
 
-Copyright (c) 2016-2020 Tom Sydney Kerckhove
+Copyright (c) 2016-2021 Tom Sydney Kerckhove
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/Setup.hs b/Setup.hs
deleted file mode 100644
--- a/Setup.hs
+++ /dev/null
@@ -1,3 +0,0 @@
-import Distribution.Simple
-
-main = defaultMain
diff --git a/genvalidity.cabal b/genvalidity.cabal
--- a/genvalidity.cabal
+++ b/genvalidity.cabal
@@ -1,11 +1,11 @@
 cabal-version: 1.12
 
--- This file has been generated from package.yaml by hpack version 0.34.4.
+-- This file has been generated from package.yaml by hpack version 0.36.0.
 --
 -- see: https://github.com/sol/hpack
 
 name:           genvalidity
-version:        0.11.0.2
+version:        1.1.1.0
 synopsis:       Testing utilities for the validity library
 description:    Note: There are companion instance packages for this library:
                 .
@@ -33,10 +33,13 @@
 bug-reports:    https://github.com/NorfairKing/validity/issues
 author:         Tom Sydney Kerckhove
 maintainer:     syd@cs-syd.eu
-copyright:      Copyright: (c) 2016-2020 Tom Sydney Kerckhove
+copyright:      Copyright: (c) 2016-2021 Tom Sydney Kerckhove
 license:        MIT
 license-file:   LICENSE
 build-type:     Simple
+extra-source-files:
+    LICENSE
+    CHANGELOG.md
 
 source-repository head
   type: git
@@ -44,7 +47,6 @@
 
 library
   exposed-modules:
-      Data.GenRelativeValidity
       Data.GenValidity
       Data.GenValidity.Utils
   other-modules:
@@ -53,10 +55,10 @@
       src
   ghc-options: -Wno-redundant-constraints
   build-depends:
-      QuickCheck >=2.12
-    , base >=4.11 && <5
-    , random
-    , validity >=0.9
+      QuickCheck >=2.13
+    , base >=4.13 && <5
+    , random >=1.1
+    , validity >=0.12.1
   default-language: Haskell2010
 
 test-suite genvalidity-test
@@ -73,7 +75,7 @@
   ghc-options: -threaded -rtsopts -with-rtsopts=-N -Wall
   build-depends:
       QuickCheck
-    , base >=4.11 && <5
+    , base >=4.13 && <5
     , genvalidity
     , hspec
     , hspec-core
diff --git a/src/Data/GenRelativeValidity.hs b/src/Data/GenRelativeValidity.hs
deleted file mode 100644
--- a/src/Data/GenRelativeValidity.hs
+++ /dev/null
@@ -1,32 +0,0 @@
-{-# LANGUAGE MultiParamTypeClasses #-}
-
-module Data.GenRelativeValidity
-  ( module Data.RelativeValidity,
-    module Data.GenRelativeValidity,
-  )
-where
-
-import Data.GenValidity
-import Data.RelativeValidity
-import Test.QuickCheck
-
-class
-  (GenUnchecked a, RelativeValidity a b) =>
-  GenRelativeUnchecked a b
-  where
-  genUncheckedFor :: b -> Gen a
-  genUncheckedFor _ = genUnchecked
-
-class
-  (GenValid a, RelativeValidity a b) =>
-  GenRelativeValid a b
-  where
-  genValidFor :: b -> Gen a
-  genValidFor b = genValid `suchThat` (`isValidFor` b)
-
-class
-  (GenUnchecked a, RelativeValidity a b, GenRelativeUnchecked a b) =>
-  GenRelativeInvalid a b
-  where
-  genInvalidFor :: b -> Gen a
-  genInvalidFor b = genUncheckedFor b `suchThat` (not . (`isValidFor` b))
diff --git a/src/Data/GenValidity.hs b/src/Data/GenValidity.hs
--- a/src/Data/GenValidity.hs
+++ b/src/Data/GenValidity.hs
@@ -1,45 +1,36 @@
-{-# LANGUAGE CPP #-}
 {-# LANGUAGE DefaultSignatures #-}
+{-# LANGUAGE DerivingStrategies #-}
 {-# LANGUAGE FlexibleContexts #-}
 {-# LANGUAGE FlexibleInstances #-}
-{-# LANGUAGE MagicHash #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
 {-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE StandaloneDeriving #-}
 {-# LANGUAGE TypeOperators #-}
-
-#if __GLASGOW_HASKELL__ >= 710
-#define OVERLAPPING_ {-# OVERLAPPING #-}
-#else
-{-# LANGUAGE OverlappingInstances  #-}
-#define OVERLAPPING_
-#endif
-#if MIN_VERSION_base(4,9,0)
 {-# OPTIONS_GHC -fno-warn-redundant-constraints #-}
-#endif
 
 -- |
 --
---    @GenValidity@ exists to make tests involving @Validity@ types easier and speed
---    up the generation of data for them.
+--    @GenValid@ exists to make tests involving @Validity@ types easier and
+--    speed up the generation of data for them.
 --
---    Let's use the example from @Data.Validity@ again: A datatype that represents
---    primes.
---    To implement tests for this datatype, we would have to be able to generate
---    both primes and non-primes. We could do this with
---    @(Prime <$> arbitrary) `suchThat` isValid@
---    but this is tedious and inefficient.
+--    To implement tests for this datatype, we would have to be able to
+--    generate both primes. We could do this with a generator like this one:
 --
+--    > (Prime <$> 'arbitrary') `suchThat` isValid
+--
+--    However, this is tedious and inefficient, as well as quite
+--    naive (because 'arbitrary' tends to use very naive generators).
+--
 --    The @GenValid@ type class allows you to specify how to (efficiently)
 --    generate valid data of the given type to allow for easier and quicker testing.
---    Just instantiating @GenUnchecked@ already gives you access to a default instance
---    of @GenValid@ and @GenInvalid@ but writing custom implementations of these functions
---    may speed up the generation of data.
+--    The default implementation of `GenValid` already gives you a generator and shrinking function
+--    for free:
 --
+--    > instance GenValid Prime
+--
 --    For example, to generate primes, we don't have to consider even numbers other
 --    than 2. A more efficient implementation could then look as follows:
 --
---    > instance GenUnchecked Prime where
---    >     genUnchecked = Prime <$> arbitrary
---
 --    > instance GenValid Prime where
 --    >     genValid = Prime <$>
 --    >        (oneof
@@ -55,17 +46,14 @@
 --    >         myFunction input `shouldSatisfy` isRight
 --
 --    > it "produces valid output when it succeeds" $ do
---    >     forAllUnchecked $ \input ->
+--    >     forAllValid $ \input ->
 --    >         case myFunction input of
 --    >             Nothing -> return () -- Can happen
 --    >             Just output -> output `shouldSatisfy` isValid
 --
---    Definitely also look at the genvalidity-property and genvalidity-hspec packages
---    for more info on how to use this package.
+--    Definitely also look at the companion packages for more info on how to use this package.
 module Data.GenValidity
-  ( GenUnchecked (..),
-    GenValid (..),
-    GenInvalid (..),
+  ( GenValid (..),
 
     -- * Helper functions
     genValidStructurally,
@@ -88,14 +76,6 @@
     module Data.Validity,
 
     -- * The Generics magic
-    genericGenUnchecked,
-    GGenUnchecked (..),
-    genericShrinkUnchecked,
-    uncheckedRecursivelyShrink,
-    GUncheckedRecursivelyShrink (..),
-    uncheckedSubterms,
-    GUncheckedSubterms (..),
-    GUncheckedSubtermsIncl (..),
     GGenValid (..),
     GValidRecursivelyShrink (..),
     structurallyValidSubterms,
@@ -104,112 +84,43 @@
   )
 where
 
-#if MIN_VERSION_base(4,9,0)
-import Data.List.NonEmpty (NonEmpty((:|)))
-#endif
-#if MIN_VERSION_base(4,8,0)
-import Data.Word (Word64)
-import GHC.Word (Word8(..), Word16(..), Word32(..), Word(..))
-#else
-import Data.Word (Word, Word64)
-import GHC.Word (Word8(..), Word16(..), Word32(..), Word(..))
-#endif
-
 import Control.Monad (guard)
 import Data.Char (chr)
 import Data.Fixed (Fixed (..), HasResolution)
-#if MIN_VERSION_base(4,8,0)
-import GHC.Natural
-#else
-import Control.Applicative ((<*>), (<$>), pure)
-#endif
-
+import Data.Functor.Const (Const (Const))
+import Data.Functor.Identity (Identity (Identity))
 import Data.GenValidity.Utils
-import Data.Int (Int64)
+import Data.Int (Int16, Int32, Int64, Int8)
+import Data.List.NonEmpty (NonEmpty)
+import Data.Monoid (Alt, Dual)
+import qualified Data.Monoid as Monoid
 import Data.Ratio ((%))
+import qualified Data.Semigroup as Semigroup
 import Data.Validity
+import Data.Word (Word16, Word32, Word64, Word8)
 import GHC.Generics
-import GHC.Int (Int (..), Int16 (..), Int32 (..), Int8 (..))
 import GHC.Real (Ratio (..))
+import Numeric.Natural
 import Test.QuickCheck hiding (Fixed)
 
 {-# ANN module "HLint: ignore Reduce duplication" #-}
 
--- | A class of types for which truly arbitrary values can be generated.
---
--- === How to instantiate 'GenUnchecked'
---
--- __Step 1__: Try to instantiate 'GenUnchecked' via 'Generic'.
---         __this is probably what you want__
---
--- An instance of this class can be made automatically if the type in question
--- has a 'Generic' instance. This instance will try to use 'genUnchecked' to
--- generate all structural sub-parts of the value that is being generated.
---
--- Example:
---
--- > {-# LANGUAGE DeriveGeneric #-}
--- >
--- > data MyType = MyType Rational String
--- >     deriving (Show, Eq, Generic)
--- >
--- > instance GenUnchecked MyType
---
--- generates something like:
---
--- > instance GenUnchecked MyType where
--- >     genUnchecked = MyType <$> genUnchecked <*> genUnchecked
---
--- If this is not possible because there is no 'GenUnchecked' instance available for one of the
--- sub-parts of your type, __then do not instantiate 'GenUnchecked' for your type__.
--- Just continue with 'GenValid' instead.
---
--- __Step 2__: If an instatiation via 'Generic' is not possible, then you should emulate what
---         'genericGenUnchecked' does.
---         This means that all sub-parts should be  generated using 'genUnchecked'.
---         Make sure to generate any possible value, valid or not, that can exist at runtime
---         even when taking the existence of 'Unsafe.Coerce.unsafeCoerce' into account.
---
--- === Warning: Invalid values can be funky
---
--- Some types have serious validity constraints. See 'Rational' for example.
--- These can behave very strangely when they are not valid.
--- In that case, __do not override 'GenUnchecked' such that 'genUnchecked' only generates valid values__.
--- In that case, do not override 'genUnchecked' at all.
--- Instead, use 'genValid' from 'GenValid' (see below) instead and consider not instantiating 'GenUnchecked' at all.
-class GenUnchecked a where
-  genUnchecked :: Gen a
-  default genUnchecked ::
-    (Generic a, GGenUnchecked (Rep a)) =>
-    Gen a
-  genUnchecked = genericGenUnchecked
-
-  shrinkUnchecked :: a -> [a]
-  default shrinkUnchecked ::
-    (Generic a, GUncheckedRecursivelyShrink (Rep a), GUncheckedSubterms (Rep a) a) =>
-    a ->
-    [a]
-  shrinkUnchecked = genericShrinkUnchecked
-
--- | A class of types for which valid values can be generated.
+-- | A class of types for which valid values can be generated to be valid.
 --
 -- === How to instantiate 'GenValid'
 --
 -- __Step 1__: Try to instantiate 'GenValid' without overriding any functions.
---             This is only possible if your type has a 'GenUnchecked' instance.
---             If it doesn't, go to step 2.
 --             It is possible that, if few values are valid or if validity
---             checking is expensive, that the resulting generator is too slow.
+--             checking is expensive, the resulting generator is too slow.
 --             In that case, go to Step 2.
 --
--- __Step 2__: Try to instantiate 'GenValid' using the helper functions via 'Generic'
---             This involves using 'genValidStructurally' to override 'genValid' and
---             using 'shrinkValidStructurally' to override 'shrinkValid'.
---             __Every time you override 'genValid', you should also override 'shrinkValid'__
+-- __Step 2__: Consider using 'genValidStructurallyWithoutExtraChecking' and
+--             'shrinkValidStructurallyWithoutExtraFiltering' to speed up generation.
+--             This only works if your type has a derived or trivial 'Validity'
+--             instance.
 --
--- __Step 3__: If the above is not possible due to lack of a 'Generic' instance,
---             then you should emulate what 'genValidStructurally' does.
---             This means that all sub-parts should be generated using 'genValid'.
+-- __Step 3__: If that still is not fast enough, consider writing your own
+--             generator and shrinking function.
 --             Make sure to generate any possible valid value, but only valid values.
 --
 -- === A note about 'Arbitrary'
@@ -217,79 +128,37 @@
 -- If you also write @Arbitrary@ instances for @GenValid@ types, it may be
 -- best to simply use
 --
--- > arbitrary = genValid
--- > shrink = shrinkValid
-class Validity a => GenValid a where
+-- > instance Arbitrary A where
+-- >   arbitrary = genValid
+-- >   shrink = shrinkValid
+class (Validity a) => GenValid a where
   -- | Generate a valid datum, this should cover all possible valid values in
   -- the type
   --
   -- The default implementation is as follows:
   --
-  -- >  genValid = genUnchecked `suchThat` isValid
+  -- >  genValid = genValidStructurally
   --
   -- To speed up testing, it may be a good idea to implement this yourself.
   -- If you do, make sure that it is possible to generate all possible valid
   -- data, otherwise your testing may not cover all cases.
   genValid :: Gen a
-  default genValid :: GenUnchecked a => Gen a
-  genValid = genUnchecked `suchThat` isValid
+  default genValid :: (Generic a, GGenValid (Rep a)) => Gen a
+  genValid = genValidStructurally
 
   -- | Shrink a valid value.
   --
   -- The default implementation is as follows:
   --
-  -- >  shrinkValid = filter isValid . shrinkUnchecked
+  -- >  shrinkValid = shrinkValidStructurally
   --
   -- It is important that this shrinking function only shrinks values to valid values.
   -- If `shrinkValid` ever shrinks a value to an invalid value, the test that is being shrunk for
   -- might fail for a different reason than for the reason that it originally failed.
   -- This would lead to very confusing error messages.
   shrinkValid :: a -> [a]
-  default shrinkValid :: GenUnchecked a => a -> [a]
-  shrinkValid = filter isValid . shrinkUnchecked
-
--- | A class of types for which invalid values can be generated.
---
--- === How to instantiate 'GenInvalid'
---
--- __Step 1__: Realise that you probably do not want to.
---             It makes no sense, and serves no purpose, to instantiate 'GenInvalid' for types
---             which contain no invalid values. (In fact, the default implementation will go into
---             an infinite loop for such types.)
---             You should only instantiate 'GenInvalid' if you explicitly want to use it
---             to write tests that deal with invalid values, or if you are writing a container
---             for parametric values.
---
--- __Step 2__: Instantiate 'GenInvalid' without overriding any functions.
-class Validity a => GenInvalid a where
-  genInvalid :: Gen a
-
-  -- | Generate an invalid datum, this should cover all possible invalid
-  -- values
-  --
-  -- > genInvalid = genUnchecked `suchThat` isInvalid
-  --
-  -- To speed up testing, it may be a good idea to implement this yourself.
-  -- If you do, make sure that it is possible to generate all possible
-  -- invalid data, otherwise your testing may not cover all cases.
-  default genInvalid :: GenUnchecked a => Gen a
-  genInvalid = genUnchecked `suchThat` isInvalid
-
-  shrinkInvalid :: a -> [a]
-  default shrinkInvalid :: GenUnchecked a => a -> [a]
-  shrinkInvalid = filter isInvalid . shrinkUnchecked
-
-instance (GenUnchecked a, GenUnchecked b) => GenUnchecked (a, b) where
-  genUnchecked =
-    sized $ \n -> do
-      (r, s) <- genSplit n
-      a <- resize r genUnchecked
-      b <- resize s genUnchecked
-      return (a, b)
-  shrinkUnchecked (a, b) =
-    ((,) <$> shrinkUnchecked a <*> shrinkUnchecked b)
-      ++ [(a', b) | a' <- shrinkUnchecked a]
-      ++ [(a, b') | b' <- shrinkUnchecked b]
+  default shrinkValid :: (Generic a, GValidRecursivelyShrink (Rep a), GValidSubterms (Rep a) a) => a -> [a]
+  shrinkValid = shrinkValidStructurally
 
 instance (GenValid a, GenValid b) => GenValid (a, b) where
   genValid =
@@ -300,53 +169,11 @@
       return (a, b)
   shrinkValid = shrinkTuple shrinkValid shrinkValid
 
-instance (GenUnchecked a, GenInvalid a, GenUnchecked b, GenInvalid b) => GenInvalid (a, b) where
-  genInvalid =
-    sized $ \n -> do
-      (r, s) <- genSplit n
-      oneof
-        [ do
-            a <- resize r genUnchecked
-            b <- resize s genInvalid
-            return (a, b),
-          do
-            a <- resize r genInvalid
-            b <- resize s genUnchecked
-            return (a, b)
-        ]
-
-instance (GenUnchecked a, GenUnchecked b) => GenUnchecked (Either a b) where
-  genUnchecked = oneof [Left <$> genUnchecked, Right <$> genUnchecked]
-  shrinkUnchecked (Left a) = Left <$> shrinkUnchecked a
-  shrinkUnchecked (Right b) = Right <$> shrinkUnchecked b
-
 instance (GenValid a, GenValid b) => GenValid (Either a b) where
   genValid = oneof [Left <$> genValid, Right <$> genValid]
   shrinkValid (Left a) = Left <$> shrinkValid a
   shrinkValid (Right b) = Right <$> shrinkValid b
 
--- | This instance ensures that the generated tupse contains at least one invalid element. The other element is unchecked.
-instance (GenInvalid a, GenInvalid b) => GenInvalid (Either a b) where
-  genInvalid = oneof [Left <$> genInvalid, Right <$> genInvalid]
-  shrinkInvalid (Left v) = Left <$> shrinkInvalid v
-  shrinkInvalid (Right v) = Right <$> shrinkInvalid v
-
-instance
-  (GenUnchecked a, GenUnchecked b, GenUnchecked c) =>
-  GenUnchecked (a, b, c)
-  where
-  genUnchecked =
-    sized $ \n -> do
-      (r, s, t) <- genSplit3 n
-      a <- resize r genUnchecked
-      b <- resize s genUnchecked
-      c <- resize t genUnchecked
-      return (a, b, c)
-  shrinkUnchecked (a, b, c) =
-    [ (a', b', c')
-      | (a', (b', c')) <- shrinkUnchecked (a, (b, c))
-    ]
-
 instance (GenValid a, GenValid b, GenValid c) => GenValid (a, b, c) where
   genValid =
     sized $ \n -> do
@@ -355,59 +182,7 @@
       b <- resize s genValid
       c <- resize t genValid
       return (a, b, c)
-  shrinkValid (a, b, c) =
-    [ (a', b', c')
-      | (a', (b', c')) <- shrinkValid (a, (b, c))
-    ]
-
--- | This instance ensures that the generated triple contains at least one invalid element. The other two are unchecked.
-instance
-  ( GenUnchecked a,
-    GenUnchecked b,
-    GenUnchecked c,
-    GenInvalid a,
-    GenInvalid b,
-    GenInvalid c
-  ) =>
-  GenInvalid (a, b, c)
-  where
-  genInvalid =
-    sized $ \n -> do
-      (r, s, t) <- genSplit3 n
-      oneof
-        [ do
-            a <- resize r genInvalid
-            b <- resize s genUnchecked
-            c <- resize t genUnchecked
-            return (a, b, c),
-          do
-            a <- resize r genUnchecked
-            b <- resize s genInvalid
-            c <- resize t genUnchecked
-            return (a, b, c),
-          do
-            a <- resize r genUnchecked
-            b <- resize s genUnchecked
-            c <- resize t genInvalid
-            return (a, b, c)
-        ]
-
-instance
-  (GenUnchecked a, GenUnchecked b, GenUnchecked c, GenUnchecked d) =>
-  GenUnchecked (a, b, c, d)
-  where
-  genUnchecked =
-    sized $ \n -> do
-      (r, s, t, u) <- genSplit4 n
-      a <- resize r genUnchecked
-      b <- resize s genUnchecked
-      c <- resize t genUnchecked
-      d <- resize u genUnchecked
-      return (a, b, c, d)
-  shrinkUnchecked (a, b, c, d) =
-    [ (a', b', c', d')
-      | (a', (b', (c', d'))) <- shrinkUnchecked (a, (b, (c, d)))
-    ]
+  shrinkValid = shrinkTriple shrinkValid shrinkValid shrinkValid
 
 instance
   (GenValid a, GenValid b, GenValid c, GenValid d) =>
@@ -421,71 +196,7 @@
       c <- resize t genValid
       d <- resize u genValid
       return (a, b, c, d)
-  shrinkValid (a, b, c, d) =
-    [ (a', b', c', d')
-      | (a', (b', (c', d'))) <- shrinkValid (a, (b, (c, d)))
-    ]
-
--- | This instance ensures that the generated triple contains at least one invalid element. The other two are unchecked.
-instance
-  ( GenUnchecked a,
-    GenUnchecked b,
-    GenUnchecked c,
-    GenUnchecked d,
-    GenInvalid a,
-    GenInvalid b,
-    GenInvalid c,
-    GenInvalid d
-  ) =>
-  GenInvalid (a, b, c, d)
-  where
-  genInvalid =
-    sized $ \n -> do
-      (r, s, t, u) <- genSplit4 n
-      oneof
-        [ do
-            a <- resize r genInvalid
-            b <- resize s genUnchecked
-            c <- resize t genUnchecked
-            d <- resize u genUnchecked
-            return (a, b, c, d),
-          do
-            a <- resize r genUnchecked
-            b <- resize s genInvalid
-            c <- resize t genUnchecked
-            d <- resize u genUnchecked
-            return (a, b, c, d),
-          do
-            a <- resize r genUnchecked
-            b <- resize s genUnchecked
-            c <- resize t genInvalid
-            d <- resize u genUnchecked
-            return (a, b, c, d),
-          do
-            a <- resize r genUnchecked
-            b <- resize s genUnchecked
-            c <- resize t genUnchecked
-            d <- resize u genInvalid
-            return (a, b, c, d)
-        ]
-
-instance
-  (GenUnchecked a, GenUnchecked b, GenUnchecked c, GenUnchecked d, GenUnchecked e) =>
-  GenUnchecked (a, b, c, d, e)
-  where
-  genUnchecked =
-    sized $ \n -> do
-      (r, s, t, u, v) <- genSplit5 n
-      a <- resize r genUnchecked
-      b <- resize s genUnchecked
-      c <- resize t genUnchecked
-      d <- resize u genUnchecked
-      e <- resize v genUnchecked
-      return (a, b, c, d, e)
-  shrinkUnchecked (a, b, c, d, e) =
-    [ (a', b', c', d', e')
-      | (a', (b', (c', (d', e')))) <- shrinkUnchecked (a, (b, (c, (d, e))))
-    ]
+  shrinkValid = shrinkQuadruple shrinkValid shrinkValid shrinkValid shrinkValid
 
 instance
   (GenValid a, GenValid b, GenValid c, GenValid d, GenValid e) =>
@@ -505,146 +216,41 @@
       | (a', (b', (c', (d', e')))) <- shrinkValid (a, (b, (c, (d, e))))
     ]
 
--- | This instance ensures that the generated triple contains at least one invalid element. The other two are unchecked.
-instance
-  ( GenUnchecked a,
-    GenUnchecked b,
-    GenUnchecked c,
-    GenUnchecked d,
-    GenUnchecked e,
-    GenInvalid a,
-    GenInvalid b,
-    GenInvalid c,
-    GenInvalid d,
-    GenInvalid e
-  ) =>
-  GenInvalid (a, b, c, d, e)
-  where
-  genInvalid =
-    sized $ \n -> do
-      (r, s, t, u, v) <- genSplit5 n
-      oneof
-        [ do
-            a <- resize r genInvalid
-            b <- resize s genUnchecked
-            c <- resize t genUnchecked
-            d <- resize u genUnchecked
-            e <- resize v genUnchecked
-            return (a, b, c, d, e),
-          do
-            a <- resize r genUnchecked
-            b <- resize s genInvalid
-            c <- resize t genUnchecked
-            d <- resize u genUnchecked
-            e <- resize v genUnchecked
-            return (a, b, c, d, e),
-          do
-            a <- resize r genUnchecked
-            b <- resize s genUnchecked
-            c <- resize t genInvalid
-            d <- resize u genUnchecked
-            e <- resize v genUnchecked
-            return (a, b, c, d, e),
-          do
-            a <- resize r genUnchecked
-            b <- resize s genUnchecked
-            c <- resize t genUnchecked
-            d <- resize u genInvalid
-            e <- resize v genUnchecked
-            return (a, b, c, d, e),
-          do
-            a <- resize r genUnchecked
-            b <- resize s genUnchecked
-            c <- resize t genUnchecked
-            d <- resize u genUnchecked
-            e <- resize v genInvalid
-            return (a, b, c, d, e)
-        ]
-
-instance GenUnchecked a => GenUnchecked (Maybe a) where
-  genUnchecked = oneof [pure Nothing, Just <$> genUnchecked]
-  shrinkUnchecked Nothing = []
-  shrinkUnchecked (Just a) = Nothing : (Just <$> shrinkUnchecked a)
-
-instance GenValid a => GenValid (Maybe a) where
-  genValid = oneof [pure Nothing, Just <$> genValid]
-  shrinkValid Nothing = []
-  shrinkValid (Just a) = Nothing : (Just <$> shrinkValid a)
-
-instance GenInvalid a => GenInvalid (Maybe a) where
-  genInvalid = Just <$> genInvalid
-  shrinkInvalid Nothing = [] -- Should not happen
-  shrinkInvalid (Just a) = Just <$> shrinkInvalid a
-
-#if MIN_VERSION_base(4,9,0)
-instance GenUnchecked a => GenUnchecked (NonEmpty a) where
-    genUnchecked = genNonEmptyOf genUnchecked
-    shrinkUnchecked (v :| vs) = [ e :| es | (e, es) <- shrinkUnchecked (v, vs)]
-
-instance GenValid a => GenValid (NonEmpty a) where
-    genValid = genNonEmptyOf genValid
-    shrinkValid (v :| vs) = [ e :| es | (e, es) <- shrinkValid (v, vs)]
-
-instance (GenUnchecked a, GenInvalid a) => GenInvalid (NonEmpty a) where
-    genInvalid = genNonEmptyOf genInvalid
-#endif
+instance (GenValid a) => GenValid (Maybe a) where
+  genValid = genMaybe genValid
+  shrinkValid = shrinkMaybe shrinkValid
 
-instance GenUnchecked a => GenUnchecked [a] where
-  genUnchecked = genListOf genUnchecked
-  shrinkUnchecked = shrinkList shrinkUnchecked
+instance (GenValid a) => GenValid (NonEmpty a) where
+  genValid = genNonEmptyOf genValid
+  shrinkValid = shrinkNonEmpty shrinkValid
 
--- | If we can generate values of a certain type, we can also generate lists of
--- them.
-instance GenValid a => GenValid [a] where
+instance (GenValid a) => GenValid [a] where
   genValid = genListOf genValid
   shrinkValid = shrinkList shrinkValid
 
--- | This instance ensures that the generated list contains at least one element
--- that satisfies 'isInvalid'. The rest is unchecked.
-instance (GenUnchecked a, GenInvalid a) => GenInvalid [a] where
-  genInvalid =
-    sized $ \n -> do
-      (x, y, z) <- genSplit3 n
-      before <- resize x $ genListOf genUnchecked
-      middle <- resize y genInvalid
-      after <- resize z $ genListOf genUnchecked
-      return $ before ++ [middle] ++ after
-
-instance GenUnchecked () where
-  genUnchecked = arbitrary
-  shrinkUnchecked = shrink
-
 instance GenValid () where
-  genValid = genUnchecked
-  shrinkValid = shrinkUnchecked
-
-instance GenUnchecked Bool where
-  genUnchecked = arbitrary
-  shrinkUnchecked = shrink
+  genValid = pure ()
+  shrinkValid () = []
 
 instance GenValid Bool where
-  genValid = genUnchecked
-  shrinkValid = shrinkUnchecked
-
-instance GenUnchecked Ordering where
-  genUnchecked = arbitrary
-  shrinkUnchecked = shrink
+  genValid = arbitrary
+  shrinkValid = shrink
 
 instance GenValid Ordering where
-  genValid = genUnchecked
-  shrinkValid = shrinkUnchecked
+  genValid = arbitrary
+  shrinkValid = shrink
 
-instance GenUnchecked Char where
-  genUnchecked = frequency [(9, choose (minBound, maxBound)), (1, genUtf16SurrogateCodePoint)]
-  shrinkUnchecked = shrink
+instance GenValid Char where
+  genValid =
+    frequency
+      [ (9, choose (minBound, maxBound)),
+        (1, genUtf16SurrogateCodePoint)
+      ]
+  shrinkValid = shrink
 
 genUtf16SurrogateCodePoint :: Gen Char
 genUtf16SurrogateCodePoint = chr <$> oneof [choose (0xD800, 0xDBFF), choose (0xDC00, 0xDFFF)]
 
-instance GenValid Char where
-  genValid = genUnchecked
-  shrinkValid = shrinkUnchecked
-
 genLineSeparator :: Gen Char
 genLineSeparator = elements ['\n', '\r']
 
@@ -654,138 +260,67 @@
 genSingleLineString :: Gen String
 genSingleLineString = genListOf genNonLineSeparator
 
-instance GenUnchecked Int where
-  genUnchecked = genIntX
-  shrinkUnchecked = shrink
-
 instance GenValid Int where
-  genValid = genUnchecked
-  shrinkValid = shrinkUnchecked
-
-instance GenUnchecked Int8 where
-  genUnchecked = genUncheckedInt (I8#)
-  shrinkUnchecked = shrinkUncheckedInt (\(I# i#) -> I8# i#) (\(I8# i#) -> I# i#)
+  genValid = genIntX
+  shrinkValid = shrink
 
 instance GenValid Int8 where
   genValid = genIntX
   shrinkValid = shrink
 
-instance GenUnchecked Int16 where
-  genUnchecked = genUncheckedInt (I16#)
-  shrinkUnchecked = shrinkUncheckedInt (\(I# i#) -> I16# i#) (\(I16# i#) -> I# i#)
-
 instance GenValid Int16 where
   genValid = genIntX
   shrinkValid = shrink
 
-instance GenUnchecked Int32 where
-  genUnchecked = genUncheckedInt (I32#)
-  shrinkUnchecked = shrinkUncheckedInt (\(I# i#) -> I32# i#) (\(I32# i#) -> I# i#)
-
 instance GenValid Int32 where
   genValid = genIntX
   shrinkValid = shrink
 
-instance GenUnchecked Int64 where
-  genUnchecked = genIntX
-  shrinkUnchecked = shrink
-
 instance GenValid Int64 where
-  genValid = genUnchecked
-  shrinkValid = shrinkUnchecked
-
-instance GenUnchecked Word where
-  genUnchecked = genWordX
-  shrinkUnchecked = shrink
+  genValid = genIntX
+  shrinkValid = shrink
 
 instance GenValid Word where
-  genValid = genUnchecked
-  shrinkValid = shrinkUnchecked
-
-instance GenUnchecked Word8 where
-  genUnchecked = genUncheckedWord (W8#)
-  shrinkUnchecked = shrinkUncheckedWord (\(W# w#) -> W8# w#) (\(W8# w#) -> W# w#)
+  genValid = genWordX
+  shrinkValid = shrink
 
 instance GenValid Word8 where
   genValid = genWordX
   shrinkValid = shrink
 
-instance GenUnchecked Word16 where
-  genUnchecked = genUncheckedWord (W16#)
-  shrinkUnchecked = shrinkUncheckedWord (\(W# w#) -> W16# w#) (\(W16# w#) -> W# w#)
-
 instance GenValid Word16 where
   genValid = genWordX
   shrinkValid = shrink
 
-instance GenUnchecked Word32 where
-  genUnchecked = genUncheckedWord (W32#)
-  shrinkUnchecked = shrinkUncheckedWord (\(W# w#) -> W32# w#) (\(W32# w#) -> W# w#)
-
 instance GenValid Word32 where
   genValid = genWordX
   shrinkValid = shrink
 
-instance GenUnchecked Word64 where
-  genUnchecked = genWordX
-  shrinkUnchecked = shrink
-
 instance GenValid Word64 where
-  genValid = genUnchecked
-  shrinkValid = shrinkUnchecked
+  genValid = genWordX
+  shrinkValid = shrink
 
-#if MIN_VERSION_QuickCheck(2,9,2)
-instance GenUnchecked Float where
-  genUnchecked = genFloat
-  shrinkUnchecked f
+instance GenValid Float where
+  genValid = genFloat
+  shrinkValid f
     | isInfinite f = []
     | isNaN f = []
     | otherwise = shrink f
-#else
-instance GenUnchecked Float where
-  genUnchecked = genFloat
-  shrinkUnchecked _ = []
-#endif
 
-instance GenValid Float where
-  genValid = genUnchecked
-  shrinkValid = shrinkUnchecked
-
-#if MIN_VERSION_QuickCheck(2,9,2)
-instance GenUnchecked Double where
-  genUnchecked = genDouble
-  shrinkUnchecked d
-      | isInfinite d = []
-      | isNaN d = []
-      | otherwise = shrink d
-#else
-instance GenUnchecked Double where
-  genUnchecked = genDouble
-  shrinkUnchecked _ = []
-#endif
-
 instance GenValid Double where
-  genValid = genUnchecked
-  shrinkValid = shrinkUnchecked
-
-instance GenUnchecked Integer where
-  genUnchecked = genInteger
-  shrinkUnchecked = shrink
-
-instance GenValid Integer
+  genValid = genDouble
+  shrinkValid d
+    | isInfinite d = []
+    | isNaN d = []
+    | otherwise = shrink d
 
-#if MIN_VERSION_base(4,8,0)
-instance GenUnchecked Natural where
-    genUnchecked = fromInteger . abs <$> genUnchecked
-    shrinkUnchecked = fmap (fromInteger . abs) . shrinkUnchecked . toInteger
+instance GenValid Integer where
+  genValid = genInteger
+  shrinkValid = shrink
 
 instance GenValid Natural where
-    genValid = fromInteger . abs <$> genValid
-#endif
-
-instance (Integral a, GenUnchecked a) => GenUnchecked (Ratio a) where
-  genUnchecked = (:%) <$> genUnchecked <*> genUnchecked
-  shrinkUnchecked (n :% d) = [n' :% d' | (n', d') <- shrinkUnchecked (n, d)]
+  genValid = fromInteger . abs <$> genValid
+  shrinkValid = fmap (fromInteger . abs) . shrinkValid . toInteger
 
 instance (Integral a, Num a, Ord a, GenValid a) => GenValid (Ratio a) where
   genValid =
@@ -796,127 +331,14 @@
     )
       `suchThat` isValid
   shrinkValid (n :% d) = do
-    (n', d') <- shrinkValid (n, d)
-    guard $ d' > 0
+    (n', d') <- shrinkTuple shrinkValid (filter (> 0) . shrinkValid) (n, d)
     let candidate = n' :% d'
     guard $ isValid candidate
     pure $ n' % d'
 
-instance (Integral a, Num a, Ord a, Validity a, GenUnchecked a) => GenInvalid (Ratio a)
-
-instance HasResolution a => GenUnchecked (Fixed a) where
-  genUnchecked = MkFixed <$> genUnchecked
-  shrinkUnchecked (MkFixed i) = MkFixed <$> shrinkUnchecked i
-
-instance HasResolution a => GenValid (Fixed a)
-
-genericGenUnchecked :: (Generic a, GGenUnchecked (Rep a)) => Gen a
-genericGenUnchecked = to <$> gGenUnchecked
-
-class GGenUnchecked f where
-  gGenUnchecked :: Gen (f a)
-
-instance GGenUnchecked U1 where
-  gGenUnchecked = pure U1
-
-instance (GGenUnchecked a, GGenUnchecked b) => GGenUnchecked (a :*: b) where
-  gGenUnchecked = (:*:) <$> gGenUnchecked <*> gGenUnchecked
-
-instance (GGenUnchecked a, GGenUnchecked b) => GGenUnchecked (a :+: b) where
-  gGenUnchecked = oneof [L1 <$> gGenUnchecked, R1 <$> gGenUnchecked]
-
-instance (GGenUnchecked a) => GGenUnchecked (M1 i c a) where
-  gGenUnchecked = M1 <$> gGenUnchecked
-
-instance (GenUnchecked a) => GGenUnchecked (K1 i a) where
-  gGenUnchecked = K1 <$> genUnchecked
-
--- | Shrink a term to any of its immediate subterms,
--- and also recursively shrink all subterms.
-genericShrinkUnchecked :: (Generic a, GUncheckedRecursivelyShrink (Rep a), GUncheckedSubterms (Rep a) a) => a -> [a]
-genericShrinkUnchecked x = uncheckedSubterms x ++ uncheckedRecursivelyShrink x
-
--- | Recursively shrink all immediate uncheckedSubterms.
-uncheckedRecursivelyShrink :: (Generic a, GUncheckedRecursivelyShrink (Rep a)) => a -> [a]
-uncheckedRecursivelyShrink = map to . gUncheckedRecursivelyShrink . from
-
-class GUncheckedRecursivelyShrink f where
-  gUncheckedRecursivelyShrink :: f a -> [f a]
-
-instance (GUncheckedRecursivelyShrink f, GUncheckedRecursivelyShrink g) => GUncheckedRecursivelyShrink (f :*: g) where
-  gUncheckedRecursivelyShrink (x :*: y) =
-    ((:*:) <$> gUncheckedRecursivelyShrink x <*> gUncheckedRecursivelyShrink y)
-      ++ [x' :*: y | x' <- gUncheckedRecursivelyShrink x]
-      ++ [x :*: y' | y' <- gUncheckedRecursivelyShrink y]
-
-instance (GUncheckedRecursivelyShrink f, GUncheckedRecursivelyShrink g) => GUncheckedRecursivelyShrink (f :+: g) where
-  gUncheckedRecursivelyShrink (L1 x) = map L1 (gUncheckedRecursivelyShrink x)
-  gUncheckedRecursivelyShrink (R1 x) = map R1 (gUncheckedRecursivelyShrink x)
-
-instance GUncheckedRecursivelyShrink f => GUncheckedRecursivelyShrink (M1 i c f) where
-  gUncheckedRecursivelyShrink (M1 x) = map M1 (gUncheckedRecursivelyShrink x)
-
-instance GenUnchecked a => GUncheckedRecursivelyShrink (K1 i a) where
-  gUncheckedRecursivelyShrink (K1 x) = map K1 (shrinkUnchecked x)
-
-instance GUncheckedRecursivelyShrink U1 where
-  gUncheckedRecursivelyShrink U1 = []
-
-instance GUncheckedRecursivelyShrink V1 where
-  -- The empty type can't be shrunk to anything.
-  gUncheckedRecursivelyShrink _ = []
-
--- | All immediate uncheckedSubterms of a term.
-uncheckedSubterms :: (Generic a, GUncheckedSubterms (Rep a) a) => a -> [a]
-uncheckedSubterms = gUncheckedSubterms . from
-
-class GUncheckedSubterms f a where
-  gUncheckedSubterms :: f a -> [a]
-
-instance GUncheckedSubterms V1 a where
-  gUncheckedSubterms _ = []
-
-instance GUncheckedSubterms U1 a where
-  gUncheckedSubterms U1 = []
-
-instance (GUncheckedSubtermsIncl f a, GUncheckedSubtermsIncl g a) => GUncheckedSubterms (f :*: g) a where
-  gUncheckedSubterms (l :*: r) = gUncheckedSubtermsIncl l ++ gUncheckedSubtermsIncl r
-
-instance (GUncheckedSubtermsIncl f a, GUncheckedSubtermsIncl g a) => GUncheckedSubterms (f :+: g) a where
-  gUncheckedSubterms (L1 x) = gUncheckedSubtermsIncl x
-  gUncheckedSubterms (R1 x) = gUncheckedSubtermsIncl x
-
-instance GUncheckedSubterms f a => GUncheckedSubterms (M1 i c f) a where
-  gUncheckedSubterms (M1 x) = gUncheckedSubterms x
-
-instance GUncheckedSubterms (K1 i a) b where
-  gUncheckedSubterms (K1 _) = []
-
-class GUncheckedSubtermsIncl f a where
-  gUncheckedSubtermsIncl :: f a -> [a]
-
-instance GUncheckedSubtermsIncl V1 a where
-  gUncheckedSubtermsIncl _ = []
-
-instance GUncheckedSubtermsIncl U1 a where
-  gUncheckedSubtermsIncl U1 = []
-
-instance (GUncheckedSubtermsIncl f a, GUncheckedSubtermsIncl g a) => GUncheckedSubtermsIncl (f :*: g) a where
-  gUncheckedSubtermsIncl (l :*: r) = gUncheckedSubtermsIncl l ++ gUncheckedSubtermsIncl r
-
-instance (GUncheckedSubtermsIncl f a, GUncheckedSubtermsIncl g a) => GUncheckedSubtermsIncl (f :+: g) a where
-  gUncheckedSubtermsIncl (L1 x) = gUncheckedSubtermsIncl x
-  gUncheckedSubtermsIncl (R1 x) = gUncheckedSubtermsIncl x
-
-instance GUncheckedSubtermsIncl f a => GUncheckedSubtermsIncl (M1 i c f) a where
-  gUncheckedSubtermsIncl (M1 x) = gUncheckedSubtermsIncl x
-
--- This is the important case: We've found a term of the same type.
-instance OVERLAPPING_ GUncheckedSubtermsIncl (K1 i a) a where
-  gUncheckedSubtermsIncl (K1 x) = [x]
-
-instance OVERLAPPING_ GUncheckedSubtermsIncl (K1 i a) b where
-  gUncheckedSubtermsIncl (K1 _) = []
+instance (HasResolution a) => GenValid (Fixed a) where
+  genValid = MkFixed <$> genValid
+  shrinkValid (MkFixed i) = MkFixed <$> shrinkValid i
 
 -- | Generate a valid value by generating all the sub parts using the 'Generic' instance,
 -- and trying that until a valid value has been generated
@@ -957,6 +379,22 @@
 instance (GenValid a) => GGenValid (K1 i a) where
   gGenValid = K1 <$> genValid
 
+deriving newtype instance (GenValid a) => GenValid (Identity a)
+
+deriving newtype instance (GenValid (f a)) => GenValid (Alt f a)
+
+deriving newtype instance (GenValid a) => GenValid (Dual a)
+
+deriving newtype instance (GenValid a) => GenValid (Semigroup.First a)
+
+deriving newtype instance (GenValid a) => GenValid (Semigroup.Last a)
+
+deriving newtype instance (GenValid a) => GenValid (Monoid.First a)
+
+deriving newtype instance (GenValid a) => GenValid (Monoid.Last a)
+
+deriving newtype instance (GenValid a) => GenValid (Const a b)
+
 -- | Shrink a term to any of its immediate valid subterms,
 -- and also recursively shrink all subterms, and then filtering out the results that are not valid.
 --
@@ -994,10 +432,10 @@
   gValidRecursivelyShrink (L1 x) = map L1 (gValidRecursivelyShrink x)
   gValidRecursivelyShrink (R1 x) = map R1 (gValidRecursivelyShrink x)
 
-instance GValidRecursivelyShrink f => GValidRecursivelyShrink (M1 i c f) where
+instance (GValidRecursivelyShrink f) => GValidRecursivelyShrink (M1 i c f) where
   gValidRecursivelyShrink (M1 x) = map M1 (gValidRecursivelyShrink x)
 
-instance GenValid a => GValidRecursivelyShrink (K1 i a) where
+instance (GenValid a) => GValidRecursivelyShrink (K1 i a) where
   gValidRecursivelyShrink (K1 x) = map K1 (shrinkValid x)
 
 instance GValidRecursivelyShrink U1 where
@@ -1027,7 +465,7 @@
   gValidSubterms (L1 x) = gValidSubtermsIncl x
   gValidSubterms (R1 x) = gValidSubtermsIncl x
 
-instance GValidSubterms f a => GValidSubterms (M1 i c f) a where
+instance (GValidSubterms f a) => GValidSubterms (M1 i c f) a where
   gValidSubterms (M1 x) = gValidSubterms x
 
 instance GValidSubterms (K1 i a) b where
@@ -1049,12 +487,12 @@
   gValidSubtermsIncl (L1 x) = gValidSubtermsIncl x
   gValidSubtermsIncl (R1 x) = gValidSubtermsIncl x
 
-instance GValidSubtermsIncl f a => GValidSubtermsIncl (M1 i c f) a where
+instance (GValidSubtermsIncl f a) => GValidSubtermsIncl (M1 i c f) a where
   gValidSubtermsIncl (M1 x) = gValidSubtermsIncl x
 
 -- This is the important case: We've found a term of the same type.
-instance OVERLAPPING_ GValidSubtermsIncl (K1 i a) a where
+instance {-# OVERLAPPING #-} GValidSubtermsIncl (K1 i a) a where
   gValidSubtermsIncl (K1 x) = [x]
 
-instance OVERLAPPING_ GValidSubtermsIncl (K1 i a) b where
+instance {-# OVERLAPPING #-} GValidSubtermsIncl (K1 i a) b where
   gValidSubtermsIncl (K1 _) = []
diff --git a/src/Data/GenValidity/Utils.hs b/src/Data/GenValidity/Utils.hs
--- a/src/Data/GenValidity/Utils.hs
+++ b/src/Data/GenValidity/Utils.hs
@@ -1,19 +1,9 @@
-{-# LANGUAGE CPP #-}
 {-# LANGUAGE FlexibleContexts #-}
 {-# LANGUAGE FlexibleInstances #-}
-{-# LANGUAGE MagicHash #-}
-{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE LambdaCase #-}
 {-# LANGUAGE ScopedTypeVariables #-}
-
-#if __GLASGOW_HASKELL__ >= 710
-#define OVERLAPPING_ {-# OVERLAPPING #-}
-#else
-{-# LANGUAGE OverlappingInstances #-}
-#define OVERLAPPING_
-#endif
-#if MIN_VERSION_base(4,9,0)
 {-# OPTIONS_GHC -fno-warn-redundant-constraints #-}
-#endif
+
 module Data.GenValidity.Utils
   ( -- ** Helper functions for implementing generators
     upTo,
@@ -27,53 +17,41 @@
     arbPartition,
     shuffle,
     genListLength,
+    genStringBy,
+    genStringBy1,
     genListOf,
-#if MIN_VERSION_base(4,9,0)
+    genListOf1,
+    genMaybe,
     genNonEmptyOf,
-#endif
-
-    -- ** Helper functions for implementing shrinking functions
-    shrinkTuple,
-    shrinkT2,
-    shrinkT3,
-    shrinkT4,
     genIntX,
     genWordX,
     genFloat,
     genDouble,
     genFloatX,
     genInteger,
-    genUncheckedInt,
-    shrinkUncheckedInt,
-    genUncheckedWord,
-    shrinkUncheckedWord,
+
+    -- ** Helper functions for implementing shrinking functions
+    shrinkMaybe,
+    shrinkTuple,
+    shrinkTriple,
+    shrinkQuadruple,
+    shrinkT2,
+    shrinkT3,
+    shrinkT4,
+    shrinkList,
+    shrinkNonEmpty,
   )
 where
 
+import Control.Monad (forM, replicateM)
+import Data.List.NonEmpty (NonEmpty (..))
+import qualified Data.List.NonEmpty as NE
+import Data.Maybe
 import Data.Ratio
-import GHC.Exts (Int#, Word#)
 import GHC.Float (castWord32ToFloat, castWord64ToDouble)
-import GHC.Int (Int (..))
-import GHC.Word (Word (..))
 import System.Random
 import Test.QuickCheck hiding (Fixed)
 
-#if !MIN_VERSION_QuickCheck(2,8,0)
-import Data.List (sortBy)
-import Data.Ord (comparing)
-#endif
-#if MIN_VERSION_base(4,9,0)
-import Data.List.NonEmpty(NonEmpty(..))
-import qualified Data.List.NonEmpty as NE
-#endif
-
-#if MIN_VERSION_base(4,8,0)
-import Control.Monad (forM, replicateM)
-#else
-import Control.Applicative ((<$>), (<*>), pure)
-import Control.Monad (forM, replicateM)
-#endif
-
 -- | 'upTo' generates an integer between 0 (inclusive) and 'n'.
 upTo :: Int -> Gen Int
 upTo n
@@ -85,68 +63,68 @@
 genSplit n
   | n < 0 = pure (0, 0)
   | otherwise = do
-    i <- choose (0, n)
-    let j = n - i
-    pure (i, j)
+      i <- choose (0, n)
+      let j = n - i
+      pure (i, j)
 
 -- | 'genSplit3 a' generates a triple '(b, c, d)' such that 'b + c + d' equals 'a'.
 genSplit3 :: Int -> Gen (Int, Int, Int)
 genSplit3 n
   | n < 0 = pure (0, 0, 0)
   | otherwise = do
-    (a, z) <- genSplit n
-    (b, c) <- genSplit z
-    return (a, b, c)
+      (a, z) <- genSplit n
+      (b, c) <- genSplit z
+      return (a, b, c)
 
 -- | 'genSplit4 a' generates a quadruple '(b, c, d, e)' such that 'b + c + d + e' equals 'a'.
 genSplit4 :: Int -> Gen (Int, Int, Int, Int)
 genSplit4 n
   | n < 0 = pure (0, 0, 0, 0)
   | otherwise = do
-    (y, z) <- genSplit n
-    (a, b) <- genSplit y
-    (c, d) <- genSplit z
-    return (a, b, c, d)
+      (y, z) <- genSplit n
+      (a, b) <- genSplit y
+      (c, d) <- genSplit z
+      return (a, b, c, d)
 
 -- | 'genSplit5 a' generates a quintuple '(b, c, d, e, f)' such that 'b + c + d + e + f' equals 'a'.
 genSplit5 :: Int -> Gen (Int, Int, Int, Int, Int)
 genSplit5 n
   | n < 0 = pure (0, 0, 0, 0, 0)
   | otherwise = do
-    (y, z) <- genSplit n
-    (a, b, c) <- genSplit3 y
-    (d, e) <- genSplit z
-    return (a, b, c, d, e)
+      (y, z) <- genSplit n
+      (a, b, c) <- genSplit3 y
+      (d, e) <- genSplit z
+      return (a, b, c, d, e)
 
 -- | 'genSplit6 a' generates a sextuple '(b, c, d, e, f, g)' such that 'b + c + d + e + f + g' equals 'a'.
 genSplit6 :: Int -> Gen (Int, Int, Int, Int, Int, Int)
 genSplit6 n
   | n < 0 = pure (0, 0, 0, 0, 0, 0)
   | otherwise = do
-    (y, z) <- genSplit n
-    (a, b, c) <- genSplit3 y
-    (d, e, f) <- genSplit3 z
-    return (a, b, c, d, e, f)
+      (y, z) <- genSplit n
+      (a, b, c) <- genSplit3 y
+      (d, e, f) <- genSplit3 z
+      return (a, b, c, d, e, f)
 
 -- | 'genSplit7 a' generates a septtuple '(b, c, d, e, f, g)' such that 'b + c + d + e + f + g' equals 'a'.
 genSplit7 :: Int -> Gen (Int, Int, Int, Int, Int, Int, Int)
 genSplit7 n
   | n < 0 = pure (0, 0, 0, 0, 0, 0, 0)
   | otherwise = do
-    (y, z) <- genSplit n
-    (a, b, c) <- genSplit3 y
-    (d, e, f, g) <- genSplit4 z
-    return (a, b, c, d, e, f, g)
+      (y, z) <- genSplit n
+      (a, b, c) <- genSplit3 y
+      (d, e, f, g) <- genSplit4 z
+      return (a, b, c, d, e, f, g)
 
 -- | 'genSplit8 a' generates a octtuple '(b, c, d, e, f, g, h)' such that 'b + c + d + e + f + g + h' equals 'a'.
 genSplit8 :: Int -> Gen (Int, Int, Int, Int, Int, Int, Int, Int)
 genSplit8 n
   | n < 0 = pure (0, 0, 0, 0, 0, 0, 0, 0)
   | otherwise = do
-    (y, z) <- genSplit n
-    (a, b, c, d) <- genSplit4 y
-    (e, f, g, h) <- genSplit4 z
-    return (a, b, c, d, e, f, g, h)
+      (y, z) <- genSplit n
+      (a, b, c, d) <- genSplit4 y
+      (e, f, g, h) <- genSplit4 z
+      return (a, b, c, d, e, f, g, h)
 
 -- | 'arbPartition n' generates a list 'ls' such that 'sum ls' equals 'n', approximately.
 arbPartition :: Int -> Gen [Int]
@@ -163,24 +141,17 @@
     -- Use an exponential distribution for generating the
     -- sizes in the partition.
     invE :: Double -> Double -> Double
-    invE lambda u = (- log (1 - u)) / lambda
+    invE lambda u = (-log (1 - u)) / lambda
 
-#if !MIN_VERSION_QuickCheck(2,8,0)
--- | Generates a random permutation of the given list.
-shuffle :: [a] -> Gen [a]
-shuffle xs = do
-    ns <- vectorOf (length xs) (choose (minBound :: Int, maxBound))
-    return (map snd (sortBy (comparing fst) (zip ns xs)))
-#endif
+genMaybe :: Gen a -> Gen (Maybe a)
+genMaybe gen = oneof [pure Nothing, Just <$> gen]
 
-#if MIN_VERSION_base(4,9,0)
 genNonEmptyOf :: Gen a -> Gen (NonEmpty a)
 genNonEmptyOf gen = do
   l <- genListOf gen
   case NE.nonEmpty l of
-    Nothing -> scale (+1) $ genNonEmptyOf gen
+    Nothing -> scale (+ 1) $ genNonEmptyOf gen
     Just ne -> pure ne
-#endif
 
 -- Uses 'genListLengthWithSize' with the size parameter
 genListLength :: Gen Int
@@ -204,6 +175,14 @@
             then a + sqrt (u * (b - a) * (c - a))
             else b - sqrt ((1 - u) * (b - a) * (b - c))
 
+-- Generate a String using a generator of 'Char's
+genStringBy :: Gen Char -> Gen String
+genStringBy = genListOf
+
+-- Generate a String using a generator of 'Char's
+genStringBy1 :: Gen Char -> Gen String
+genStringBy1 = genListOf1
+
 -- | A version of @listOf@ that takes size into account more accurately.
 --
 -- This generator distributes the size that is is given among the values
@@ -214,24 +193,62 @@
     pars <- arbPartition n
     forM pars $ \i -> resize i func
 
+-- | A version of 'genNonEmptyOf' that returns a list instead of a 'NonEmpty'.
+genListOf1 :: Gen a -> Gen [a]
+genListOf1 gen = NE.toList <$> genNonEmptyOf gen
+
+-- | Lift a shrinker function into a maybe
+shrinkMaybe :: (a -> [a]) -> Maybe a -> [Maybe a]
+shrinkMaybe shrinker = \case
+  Nothing -> []
+  Just a -> Nothing : (Just <$> shrinker a)
+
+-- | Combine two shrinking functions to shrink a tuple.
 shrinkTuple :: (a -> [a]) -> (b -> [b]) -> (a, b) -> [(a, b)]
 shrinkTuple sa sb (a, b) =
   ((,) <$> sa a <*> sb b)
     ++ [(a', b) | a' <- sa a]
     ++ [(a, b') | b' <- sb b]
 
+-- | Like 'shrinkTuple', but for triples
+shrinkTriple ::
+  (a -> [a]) ->
+  (b -> [b]) ->
+  (c -> [c]) ->
+  (a, b, c) ->
+  [(a, b, c)]
+shrinkTriple sa sb sc (a, b, c) = do
+  (a', (b', c')) <- shrinkTuple sa (shrinkTuple sb sc) (a, (b, c))
+  pure (a', b', c')
+
+-- | Like 'shrinkTuple', but for quadruples
+shrinkQuadruple ::
+  (a -> [a]) ->
+  (b -> [b]) ->
+  (c -> [c]) ->
+  (d -> [d]) ->
+  (a, b, c, d) ->
+  [(a, b, c, d)]
+shrinkQuadruple sa sb sc sd (a, b, c, d) = do
+  ((a', b'), (c', d')) <- shrinkTuple (shrinkTuple sa sb) (shrinkTuple sc sd) ((a, b), (c, d))
+  pure (a', b', c', d')
+
 -- | Turn a shrinking function into a function that shrinks tuples.
 shrinkT2 :: (a -> [a]) -> (a, a) -> [(a, a)]
-shrinkT2 s (a, b) = (,) <$> s a <*> s b
+shrinkT2 s = shrinkTuple s s
 
 -- | Turn a shrinking function into a function that shrinks triples.
 shrinkT3 :: (a -> [a]) -> (a, a, a) -> [(a, a, a)]
-shrinkT3 s (a, b, c) = (,,) <$> s a <*> s b <*> s c
+shrinkT3 s = shrinkTriple s s s
 
 -- | Turn a shrinking function into a function that shrinks quadruples.
 shrinkT4 :: (a -> [a]) -> (a, a, a, a) -> [(a, a, a, a)]
-shrinkT4 s (a, b, c, d) = (,,,) <$> s a <*> s b <*> s c <*> s d
+shrinkT4 s = shrinkQuadruple s s s s
 
+-- Shrink a nonempty list given a shrinker for values.
+shrinkNonEmpty :: (a -> [a]) -> NonEmpty a -> [NonEmpty a]
+shrinkNonEmpty shrinker = mapMaybe NE.nonEmpty . shrinkList shrinker . NE.toList
+
 -- | Generate Int, Int8, Int16, Int32 and Int64 values smartly.
 --
 -- * Some at the border
@@ -242,7 +259,7 @@
   frequency
     [ (1, extreme),
       (1, small),
-      (8, uniform)
+      (8, uniformInt)
     ]
   where
     extreme :: Gen a
@@ -252,9 +269,9 @@
           choose (minBound, minBound + fromIntegral s)
         ]
     small :: Gen a
-    small = sized $ \s -> choose (- fromIntegral s, fromIntegral s)
-    uniform :: Gen a
-    uniform = choose (minBound, maxBound)
+    small = sized $ \s -> choose (-fromIntegral s, fromIntegral s)
+    uniformInt :: Gen a
+    uniformInt = choose (minBound, maxBound)
 
 -- | Generate Word, Word8, Word16, Word32 and Word64 values smartly.
 --
@@ -266,7 +283,7 @@
   frequency
     [ (1, extreme),
       (1, small),
-      (8, uniform)
+      (8, uniformWord)
     ]
   where
     extreme :: Gen a
@@ -274,8 +291,8 @@
       choose (maxBound - fromIntegral s, maxBound)
     small :: Gen a
     small = sized $ \s -> choose (0, fromIntegral s)
-    uniform :: Gen a
-    uniform = choose (minBound, maxBound)
+    uniformWord :: Gen a
+    uniformWord = choose (minBound, maxBound)
 
 -- | See 'genFloatX'
 genFloat :: Gen Float
@@ -325,12 +342,12 @@
       let n' = toInteger n
       let precision = 9999999999999 :: Integer
       b <- choose (1, precision)
-      a <- choose ((- n') * b, n' * b)
+      a <- choose ((-n') * b, n' * b)
       pure (fromRational (a % b))
     upperSignificand :: Integer
     upperSignificand = floatRadix (0.0 :: a) ^ floatDigits (0.0 :: a)
     lowerSignificand :: Integer
-    lowerSignificand = (- upperSignificand)
+    lowerSignificand = (-upperSignificand)
     (lowerExponent, upperExponent) = floatRange (0.0 :: a)
     aroundBounds :: Gen a
     aroundBounds = do
@@ -362,7 +379,7 @@
         small
       ]
   where
-    small = sized $ \s -> choose (- toInteger s, toInteger s)
+    small = sized $ \s -> choose (-toInteger s, toInteger s)
     genIntSizedInteger = toInteger <$> (genIntX :: Gen Int)
     genBiggerInteger = sized $ \s -> do
       (a, b, c) <- genSplit3 s
@@ -370,19 +387,3 @@
       bi <- resize b genInteger
       ci <- resize c genIntSizedInteger
       pure $ ai * bi + ci
-
-genUncheckedInt :: (Int# -> a) -> Gen a
-genUncheckedInt func = do
-  (I# i#) <- genIntX
-  pure $ func i#
-
-shrinkUncheckedInt :: (Int -> a) -> (a -> Int) -> a -> [a]
-shrinkUncheckedInt fromInt toInt = fmap fromInt . shrink . toInt
-
-genUncheckedWord :: (Word# -> a) -> Gen a
-genUncheckedWord func = do
-  (W# w#) <- genWordX
-  pure $ func w#
-
-shrinkUncheckedWord :: (Word -> a) -> (a -> Word) -> a -> [a]
-shrinkUncheckedWord fromWord toWord = fmap fromWord . shrink . toWord
diff --git a/test/Data/GenValidity/GenericSpec.hs b/test/Data/GenValidity/GenericSpec.hs
--- a/test/Data/GenValidity/GenericSpec.hs
+++ b/test/Data/GenValidity/GenericSpec.hs
@@ -82,14 +82,16 @@
     ( unwords
         ["never shrinks to itself for valid", "\"" ++ nameOf proxy ++ "\"s"]
     )
-    $ forAll (genValid :: Gen a) $ \a ->
+    $ forAll (genValid :: Gen a)
+    $ \a ->
       forM_ (shrinkValidStructurally a) $ \subA ->
         when (subA == a) $
-          expectationFailure $ unlines [show a, "was shrunk to itself."]
+          expectationFailure $
+            unlines [show a, "was shrunk to itself."]
 
 nameOf ::
   forall a.
-  Typeable a =>
+  (Typeable a) =>
   Proxy a ->
   String
 nameOf = show . typeRep
@@ -99,7 +101,5 @@
   deriving (Show, Eq, Generic, Typeable)
 
 instance Validity MyType
-
-instance GenUnchecked MyType
 
 instance GenValid MyType
diff --git a/test/Data/GenValidity/ShrinkGenericSpec.hs b/test/Data/GenValidity/ShrinkGenericSpec.hs
--- a/test/Data/GenValidity/ShrinkGenericSpec.hs
+++ b/test/Data/GenValidity/ShrinkGenericSpec.hs
@@ -8,13 +8,6 @@
 
 spec :: Spec
 spec = do
-  describe "genericShrinkUnchecked" $ do
-    it "shrinks tuples correctly" $
-      genericShrinkUnchecked ((A2, B3))
-        `shouldBe` [(A1, B1), (A1, B2), (A1, B3), (A2, B1), (A2, B2)]
-    it "figures out the right shrinking function for Ex" $
-      genericShrinkUnchecked (Ex A2 B3)
-        `shouldBe` [Ex A1 B1, Ex A1 B2, Ex A1 B3, Ex A2 B1, Ex A2 B2]
   describe "default shrinkValid" $ do
     it "figures out the right shrinking function for A" $
       shrinkValid A2 `shouldBe` [A1]
@@ -40,8 +33,6 @@
 
 instance Validity Ex
 
-instance GenUnchecked Ex
-
 instance GenValid Ex
 
 data A
@@ -51,11 +42,9 @@
 
 instance Validity A
 
-instance GenUnchecked A where
-  shrinkUnchecked A1 = []
-  shrinkUnchecked A2 = [A1]
-
-instance GenValid A
+instance GenValid A where
+  shrinkValid A1 = []
+  shrinkValid A2 = [A1]
 
 data B
   = B1
@@ -68,9 +57,7 @@
   validate B2 = invalid "for test"
   validate B3 = valid
 
-instance GenUnchecked B where
-  shrinkUnchecked B1 = []
-  shrinkUnchecked B2 = [B1]
-  shrinkUnchecked B3 = [B1, B2]
-
-instance GenValid B
+instance GenValid B where
+  shrinkValid B1 = []
+  shrinkValid B2 = [B1]
+  shrinkValid B3 = [B1]
diff --git a/test/Data/GenValiditySpec.hs b/test/Data/GenValiditySpec.hs
--- a/test/Data/GenValiditySpec.hs
+++ b/test/Data/GenValiditySpec.hs
@@ -23,7 +23,8 @@
       forAll genSingleLineString (`shouldSatisfy` isSingleLine)
   describe "upTo" $ do
     it "returns only positive integers" $
-      forAll arbitrary $ \n -> forAll (upTo n) (`shouldSatisfy` (>= 0))
+      forAll arbitrary $
+        \n -> forAll (upTo n) (`shouldSatisfy` (>= 0))
     it "returns only integers smaller than or equal to the given number" $
       forAll arbitrary $ \n ->
         forAll (upTo n) (`shouldSatisfy` (<= (max n 0)))
diff --git a/test/Data/InstanceSpec.hs b/test/Data/InstanceSpec.hs
--- a/test/Data/InstanceSpec.hs
+++ b/test/Data/InstanceSpec.hs
@@ -1,4 +1,4 @@
-{-# LANGUAGE CPP #-}
+{-# LANGUAGE NegativeLiterals #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 
 module Data.InstanceSpec
@@ -6,72 +6,63 @@
   )
 where
 
-#if !MIN_VERSION_base(4,8,0)
-import Control.Applicative ((<*>), pure)
-import Data.Functor ((<$>))
-#endif
-
-#if MIN_VERSION_base(4,9,0)
-import Data.List.NonEmpty (NonEmpty)
-#endif
-
 import Control.Monad
 import Data.Data
 import Data.Fixed
-#if MIN_VERSION_base(4,8,0)
-import GHC.Natural
-#endif
 import Data.GenValidity
 import Data.Int
+import Data.List.NonEmpty (NonEmpty)
 import Data.Ratio
 import Data.Word
+import Numeric.Natural
 import Test.Hspec
 import Test.Hspec.Core.QuickCheck (modifyMaxSize, modifyMaxSuccess)
 import Test.QuickCheck
 
 spec :: Spec
 spec = do
-  twoTests (Proxy :: Proxy ())
-  twoTests (Proxy :: Proxy Bool)
-  twoTests (Proxy :: Proxy Ordering)
-  twoTests (Proxy :: Proxy Char)
-  twoTests (Proxy :: Proxy Word)
-  twoTests (Proxy :: Proxy Word8)
-  twoTests (Proxy :: Proxy Word16)
-  twoTests (Proxy :: Proxy Word32)
-  twoTests (Proxy :: Proxy Word64)
-  twoTests (Proxy :: Proxy Int)
-  twoTests (Proxy :: Proxy Int8)
-  twoTests (Proxy :: Proxy Int16)
-  twoTests (Proxy :: Proxy Int32)
-  twoTests (Proxy :: Proxy Int64)
-  twoTests (Proxy :: Proxy Integer)
-  twoTests (Proxy :: Proxy Float)
-  twoTupleTests (Proxy :: Proxy Float)
+  genValidTest (Proxy :: Proxy ())
+  genValidTest (Proxy :: Proxy Bool)
+  genValidTest (Proxy :: Proxy Ordering)
+  genValidTest (Proxy :: Proxy Char)
+  genValidTest (Proxy :: Proxy Word)
+  genValidTest (Proxy :: Proxy Word8)
+  genValidTest (Proxy :: Proxy Word16)
+  genValidTest (Proxy :: Proxy Word32)
+  genValidTest (Proxy :: Proxy Word64)
+  genValidTest (Proxy :: Proxy Int)
+  genValidTest (Proxy :: Proxy Int8)
+  genValidTest (Proxy :: Proxy Int16)
+  genValidTest (Proxy :: Proxy Int32)
+  genValidTest (Proxy :: Proxy Int64)
+  genValidTest (Proxy :: Proxy Integer)
+  genValidTest (Proxy :: Proxy Float)
+  tupleTest (Proxy :: Proxy Float)
   -- Regression tests
-  describe "shrinkUnchecked Float" $ do
+  describe "shrinkValid Float" $ do
     let sf :: Float -> Spec
-        sf f = it (unwords ["Does not shrink", show f, "to itself"]) $ f `shouldNotSatisfy` (`elem` shrinkUnchecked f)
+        sf f = it (unwords ["Does not shrink", show f, "to itself"]) $ f `shouldNotSatisfy` (`elem` shrinkValid f)
 
     sf (-2.1393704e20)
     sf 1.2223988e-12
     sf 2.7896812e10
-  describe "shrinkUnchecked Double" $ do
+  describe "shrinkValid Double" $ do
     let sd :: Double -> Spec
-        sd d = it (unwords ["Does not shrink", show d, "to itself"]) $ d `shouldNotSatisfy` (`elem` shrinkUnchecked d)
+        sd d = it (unwords ["Does not shrink", show d, "to itself"]) $ d `shouldNotSatisfy` (`elem` shrinkValid d)
     sd (-1.032730679986007e18)
-  twoTests (Proxy :: Proxy Double)
-  twoTupleTests (Proxy :: Proxy Double)
-  threeTests (Proxy :: Proxy (Ratio Int))
+  genValidTest (Proxy :: Proxy Double)
+  tupleTest (Proxy :: Proxy Double)
+  genValidTest (Proxy :: Proxy (Ratio Int))
   modifyMaxSuccess (`quot` 2) $
-    modifyMaxSize (`quot` 2) $ twoTests (Proxy :: Proxy (Either Bool Ordering))
-  twoTests (Proxy :: Proxy (Maybe Ordering))
-  twoTests (Proxy :: Proxy (Maybe (Maybe (Ordering))))
-  threeTests (Proxy :: Proxy (Ratio Integer))
+    modifyMaxSize (`quot` 2) $
+      genValidTest (Proxy :: Proxy (Either Bool Ordering))
+  genValidTest (Proxy :: Proxy (Maybe Ordering))
+  genValidTest (Proxy :: Proxy (Maybe (Maybe (Ordering))))
+  genValidTest (Proxy :: Proxy (Ratio Integer))
   -- threeTupleTests (Proxy :: Proxy (Ratio Integer))
-  threeTests (Proxy :: Proxy (Ratio Int))
+  genValidTest (Proxy :: Proxy (Ratio Int))
   -- threeTupleTests (Proxy :: Proxy (Ratio Int))
-  threeTests (Proxy :: Proxy (Ratio Int8))
+  genValidTest (Proxy :: Proxy (Ratio Int8))
   describe "shrinking (Ratio Int)" $
     it "can shrink this example" $
       let v = ((-9223372036854775808) % 9223372036854775761) :: Ratio Int
@@ -80,88 +71,31 @@
     it "can shrink this example" $
       let v = ((-128) % 113) :: Ratio Int8
        in v `notElem` shrinkValid v
-  twoTests (Proxy :: Proxy Uni)
-  twoTupleTests (Proxy :: Proxy Uni)
-  twoTests (Proxy :: Proxy Deci)
-  twoTupleTests (Proxy :: Proxy Deci)
-  twoTests (Proxy :: Proxy Centi)
-  twoTupleTests (Proxy :: Proxy Centi)
-  twoTests (Proxy :: Proxy Milli)
-  twoTupleTests (Proxy :: Proxy Milli)
-  twoTests (Proxy :: Proxy Micro)
-  twoTupleTests (Proxy :: Proxy Micro)
-  twoTests (Proxy :: Proxy Nano)
-  twoTupleTests (Proxy :: Proxy Nano)
-  twoTests (Proxy :: Proxy Pico)
-  twoTupleTests (Proxy :: Proxy Pico)
-
-#if MIN_VERSION_base(4,8,0)
-  twoTests (Proxy :: Proxy Natural)
-
-  twoTupleTests (Proxy :: Proxy Natural)
-#endif
-#if MIN_VERSION_base(4,9,0)
-  twoTests (Proxy :: Proxy (NonEmpty Ordering))
-#endif
-twoTupleTests ::
-  forall a.
-  (Show a, Eq a, Typeable a, GenUnchecked a, GenValid a) =>
-  Proxy a ->
-  Spec
-twoTupleTests proxy = do
-  modifyMaxSuccess (`quot` 2) $ modifyMaxSize (`quot` 2) $ twoTests $ (,) <$> proxy <*> proxy
-
-twoTests ::
-  forall a.
-  (Show a, Eq a, Typeable a, GenUnchecked a, GenValid a) =>
-  Proxy a ->
-  Spec
-twoTests proxy =
-  describe (nameOf proxy) $ do
-    genUncheckedTest proxy
-    genValidTest proxy
-
-threeTests ::
-  forall a.
-  (Show a, Eq a, Typeable a, GenUnchecked a, GenValid a, GenInvalid a) =>
-  Proxy a ->
-  Spec
-threeTests proxy =
-  describe (nameOf proxy) $ do
-    genUncheckedTest proxy
-    genValidTest proxy
-    genInvalidTest proxy
+  genValidTest (Proxy :: Proxy Uni)
+  tupleTest (Proxy :: Proxy Uni)
+  genValidTest (Proxy :: Proxy Deci)
+  tupleTest (Proxy :: Proxy Deci)
+  genValidTest (Proxy :: Proxy Centi)
+  tupleTest (Proxy :: Proxy Centi)
+  genValidTest (Proxy :: Proxy Milli)
+  tupleTest (Proxy :: Proxy Milli)
+  genValidTest (Proxy :: Proxy Micro)
+  tupleTest (Proxy :: Proxy Micro)
+  genValidTest (Proxy :: Proxy Nano)
+  tupleTest (Proxy :: Proxy Nano)
+  genValidTest (Proxy :: Proxy Pico)
+  tupleTest (Proxy :: Proxy Pico)
+  genValidTest (Proxy :: Proxy Natural)
+  tupleTest (Proxy :: Proxy Natural)
+  genValidTest (Proxy :: Proxy (NonEmpty Ordering))
 
-genUncheckedTest ::
+tupleTest ::
   forall a.
-  (Show a, Eq a, Typeable a, GenUnchecked a, GenValid a) =>
+  (Show a, Eq a, Typeable a, GenValid a) =>
   Proxy a ->
   Spec
-genUncheckedTest proxy = do
-  it (unwords ["genUnchecked of", nameOf proxy, "does not crash while validating"]) $
-    forAll genUnchecked $ \a ->
-      case prettyValidate (a :: a) of
-        Right v -> seq v True
-        Left err -> seq err True
-  modifyMaxSuccess (`quot` 5) $
-    it
-      ( unwords
-          [ "shrinkUnchecked of",
-            nameOf proxy,
-            "only produces values that do not crash while validating"
-          ]
-      )
-      $ forAll genUnchecked $ \a ->
-        forM_ (shrinkUnchecked a) $ \v ->
-          case prettyValidate (v :: a) of
-            Right v_ -> seq v_ $ pure () :: IO ()
-            Left err -> seq err $ pure ()
-  modifyMaxSuccess (`quot` 5) $
-    it (unwords ["shrinkUnchecked of", nameOf proxy, "does not shrink to itself"]) $
-      forAll genValid $ \a ->
-        forM_ (shrinkUnchecked a) $ \a' ->
-          unless (a /= a') $
-            expectationFailure $ unlines ["The value", show (a :: a), "was shrunk to itself"]
+tupleTest proxy = do
+  modifyMaxSuccess (`quot` 2) $ modifyMaxSize (`quot` 2) $ genValidTest $ (,) <$> proxy <*> proxy
 
 genValidTest ::
   forall a.
@@ -185,61 +119,28 @@
             Left err ->
               expectationFailure $
                 unlines ["'validate' reported this value to be invalid:", show v, err, ""]
-  modifyMaxSuccess (`quot` 5) $
-    it
+  modifyMaxSuccess (`quot` 5)
+    $ it
       ( unwords
           ["shrinkValid of", nameOf proxy, "only produces values that do not crash while validating"]
       )
-      $ forAll genValid $ \a ->
-        forM_ (shrinkValid a) $ \v ->
-          case prettyValidate (v :: a) of
-            Right v_ -> seq v_ $ pure () :: IO ()
-            Left err -> seq err $ pure ()
+    $ forAll genValid
+    $ \a ->
+      forM_ (shrinkValid a) $ \v ->
+        case prettyValidate (v :: a) of
+          Right v_ -> seq v_ $ pure () :: IO ()
+          Left err -> seq err $ pure ()
   modifyMaxSuccess (`quot` 5) $
     it (unwords ["shrinkValid of", nameOf proxy, "does not shrink to itself"]) $
       forAll genValid $ \a ->
         forM_ (shrinkValid a) $ \a' ->
           unless (a /= a') $
-            expectationFailure $ unlines ["The value", show (a :: a), "was shrunk to itself"]
-
-genInvalidTest ::
-  forall a.
-  (Show a, Typeable a, GenInvalid a) =>
-  Proxy a ->
-  Spec
-genInvalidTest proxy = do
-  it (unwords ["genInvalid of", nameOf proxy, "generates only invalid values"]) $
-    forAll genInvalid $ \a ->
-      case prettyValidate (a :: a) of
-        Right _ ->
-          expectationFailure $ unlines ["'validate' reported this value to be valid: ", show a]
-        Left e -> seq e $ pure ()
-  modifyMaxSuccess (`quot` 5) $
-    it (unwords ["shrinkInvalid of", nameOf proxy, "shrinks to only invalid values"]) $
-      forAll genInvalid $ \a ->
-        forM_ (shrinkInvalid a) $ \v ->
-          case prettyValidate (v :: a) of
-            Right _ ->
-              expectationFailure $ unlines ["'validate' reported this value to be valid: ", show v]
-            Left e -> seq e $ pure ()
-  modifyMaxSuccess (`quot` 5) $
-    it
-      ( unwords
-          [ "shrinkInvalid of",
-            nameOf proxy,
-            "only produces values that do not crash while validating"
-          ]
-      )
-      $ forAll genInvalid $ \a ->
-        forM_ (shrinkInvalid a) $ \v ->
-          case prettyValidate (v :: a) of
-            Right _ ->
-              expectationFailure $ unlines ["'validate' reported this value to be valid: ", show v]
-            Left e -> seq e $ pure ()
+            expectationFailure $
+              unlines ["The value", show (a :: a), "was shrunk to itself"]
 
 nameOf ::
   forall a.
-  Typeable a =>
+  (Typeable a) =>
   Proxy a ->
   String
 nameOf = show . typeRep
