diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -1,1 +1,94 @@
 # quickcheck-classes
+
+This library provides sets of properties that should hold for common typeclasses,
+along with three (3) simple functions that you can use to test them.
+
+### `lawsCheck`:
+
+A convenience function for testing properties in GHCi.
+For example, at GHCi:
+
+```bash
+>>> lawsCheck (monoidLaws (Proxy :: Proxy Ordering))
+Monoid: Associative +++ OK, passed 100 tests.
+Monoid: Left Identity +++ OK, passed 100 tests.
+Monoid: Right Identity +++ OK, passed 100 tests.
+```
+
+Assuming that the `Arbitrary` instance for `Ordering` is good, we now
+have confidence that the `Monoid` instance for `Ordering` satisfies
+the monoid laws.
+
+### `lawsCheckMany`:
+
+A convenience function for checking multiple typeclass instances
+of multiple types. Consider the following Haskell source file:
+
+```haskell
+import Data.Proxy (Proxy(..))
+import Data.Map (Map)
+import Data.Set (Set)
+
+-- A 'Proxy' for 'Set' 'Int'. 
+setInt :: Proxy (Set Int)
+setInt = Proxy
+
+-- A 'Proxy' for 'Map' 'Int' 'Int'.
+mapInt :: Proxy (Map Int Int)
+mapInt = Proxy
+
+myLaws :: Proxy a -> [Laws]
+myLaws p = [eqLaws p, monoidLaws p]
+
+namedTests :: [(String, [Laws])]
+namedTests =
+  [ ("Set Int", myLaws setInt)
+  , ("Map Int Int", myLaws mapInt)
+  ]
+```
+
+Now, in GHCi:
+
+```bash
+>>> lawsCheckMany namedTests
+
+Testing properties for common typeclasses
+-------------
+-- Set Int --
+-------------
+
+Eq: Transitive +++ OK, passed 100 tests.
+Eq: Symmetric +++ OK, passed 100 tests.
+Eq: Reflexive +++ OK, passed 100 tests.
+Monoid: Associative +++ OK, passed 100 tests.
+Monoid: Left Identity +++ OK, passed 100 tests.
+Monoid: Right Identity +++ OK, passed 100 tests.
+Monoid: Concatenation +++ OK, passed 100 tests.
+
+-----------------
+-- Map Int Int --
+-----------------
+
+Eq: Transitive +++ OK, passed 100 tests.
+Eq: Symmetric +++ OK, passed 100 tests.
+Eq: Reflexive +++ OK, passed 100 tests.
+Monoid: Associative +++ OK, passed 100 tests.
+Monoid: Left Identity +++ OK, passed 100 tests.
+Monoid: Right Identity +++ OK, passed 100 tests.
+Monoid: Concatenation +++ OK, passed 100 tests.
+
+```
+
+### `lawsCheckOne`
+
+A convenience function that allows one to check many typeclass
+instances of the same type.
+
+For example, in GHCi:
+
+```bash
+>>> lawsCheckOne (Proxy :: Proxy Word) [jsonLaws, showReadLaws]
+ToJSON/FromJSON: Encoding Equals Value +++ OK, passed 100 tests.
+ToJSON/FromJSON: Partial Isomorphism +++ OK, passed 100 tests.
+Show/Read: Partial Isomorphism +++ OK, passed 100 tests.
+```
diff --git a/Setup.hs b/Setup.hs
deleted file mode 100644
--- a/Setup.hs
+++ /dev/null
@@ -1,2 +0,0 @@
-import Distribution.Simple
-main = defaultMain
diff --git a/changelog.md b/changelog.md
new file mode 100644
--- /dev/null
+++ b/changelog.md
@@ -0,0 +1,228 @@
+# Changelog
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
+and this project adheres to the [Haskell Package Versioning Policy](https://pvp.haskell.org/).
+
+Note that since `quickcheck-classes` reexports larges parts of
+`quickcheck-classes-base`, changelog entries that deal with any of the
+classes from `base` are duplicated across the two changelogs.
+
+## [0.6.5.0] - 2021-04-12
+### Added
+- Laws for `abs` and `signum`
+- Storable Set-Set Law (resolves issue 101).
+- Add laws for `quotRem` and `divMod`.
+- Use non-commutative monoid for bifoldable tests (resolves issue 98)
+- `substitutiveEqLaws`, which tests for Eq substitutivity.
+- Negation law check for `Eq`.
+- Document that users can provide their own `Laws`.
+
+## [0.6.4.0] - 2019-09-13
+### Changed
+- Use newer semirings
+
+## [0.6.3.0] - 2019-08-08
+### Added
+- `gcdDomainLaws`
+- `euclideanLaws`
+### Changed
+- Replaces 0.6.2.2. That release should have been a minor version
+  bump since it added new features.
+- Support `primitive-0.6.4.0`.
+- Extend `semiringLaws` to cover `fromNatural`
+- Factor out a subset of laws tests into `quickcheck-classes-base`
+  and depend on this library.
+
+## [0.6.2.2] - 2019-06-18
+### Added
+- `numLaws`
+- `bitraversableLaws`
+
+## [0.6.2.1] - 2019-05-23
+### Fixed
+- Removal of BadList test that was causing the test suite to fail
+
+## [0.6.2.0] - 2019-05-23
+### Added
+- `ixLaws`
+- `contravariantLaws`
+- `semigroupMonoidLaws`
+### Changed
+- extend `mvectorLaws`
+- extend `applyLaws` to include associativity
+### Fixed
+- bug in `foldableLaws` which could fail to catch implementations of `foldMap` or `fold`
+  that evaluate in the wrong order
+
+## [0.6.1.0] - 2019-01-12
+### Change
+- `genericLaws` and `generic1Laws` were not exported. Now they are.
+### Added
+- Add `muvectorLaws`.
+
+## [0.6.0.0] - 2018-12-24
+### Change
+- Support QuickCheck 2.7 and 2.8. This adds `Arbitrary` orphan instances
+  to the test suite.
+- Fix CPP that caused build failures on GHC 7.10 and some old
+  package versions.
+- Fix compiling the test suite without semigroupoids and compiling with old
+  versions of transformers.
+- Add lower bound for semigroups to make sure the `stimes` method is available.
+- The laws `commutativeSemigroupLaws` and `commutativeMonoidLaws` no longer
+  check any property other than commutativity. They must now be used in conjunction
+  with, rather than in place of, `semigroupLaws` and `monoidLaws`. This is a breaking
+  change.
+- Fix the right distribution law for semirings.
+- The function `lawsCheckMany` now terminates with exit code 1 if a
+  test fails.
+- Extend `showReadLaws` with new properties for `showsPrec`, `readsPrec`,
+  `showList` and `readList`.
+- Prettify JSON partial isomorphism test failure.
+### Added
+- Add `genericLaws` and `generic1Laws`
+- Add property tests for special classes of semigroups. This includes:
+  commutative, idempotent, rectangular band, and exponential.
+- `bifoldableLaws`, `bifoldableFunctorLaws`
+- Add `showLaws`.
+
+## [0.5.0.0] - 2018-09-25
+### Change
+- When compiling with GHC 8.6 and newer, use `QuantifiedConstraints` instead
+  of `Eq1`, `Show1`, `Arbitrary1`, `Eq2`, `Show`, and `Arbitrary2`.
+
+## [0.4.14.3] - 2018-09-21
+### Change
+- Fix a CPP conditional import problem that caused build failures on GHC 7.10
+- Set an explicit lower bound for containers
+
+## [0.4.14.2] - 2018-09-12
+### Change
+- Support QuickCheck-2.12
+- Fix compilation for containers<0.5.9
+- Fix compilation with QuickCheck-2.9
+
+## [0.4.14.1] - 2018-07-24
+### Change
+- Build correctly when dependency on semigroupoids is disabled.
+
+## [0.4.14] - 2018-07-23
+### Added
+- commutativeSemigroupLaws
+- the following typeclasses:
+    `Data.Semigroupoid.Semigroupoid` (semigroupoids)
+    `Data.Functor.Plus.Plus` (semigroupoids)
+
+### Change
+- semiringLaws were never exported, we now export them.
+- make documentation for `MonadPlus` and `Alternative` consistent.
+- bump semirings to 0.2.0.0
+- deprecate `Test.QuickCheck.Classes.specialisedLawsCheckMany`
+  in favour of `Test.QuickCheck.Classes.lawsCheckOne`
+
+## [0.4.13] - 2018-07-18
+### Added
+- Laws for `Enum` typeclass.
+- Laws for `Category` typeclass.
+
+## [0.4.12] - 2018-06-07
+### Added
+- Remaining laws for `Storable` typeclass.
+- Laws for `Prim` typeclass requiring `setByteArray` and `setOffAddr` to
+  match the behavior that would result from manually iterating over the
+  array and writing the value element-by-element.
+### Change
+- Correct the law from the `Bits` typeclass that relates `clearBit`
+  and `zeroBits`.
+- Limit the size of the lists that are used when testing that
+  `mconcat` and `sconcat` have behaviors that match their default
+  implementations. For some data structures, concatenating the
+  elements in a list of several dozen arbitrary values does not
+  finish in a reasonable amount of time. So, the size of these
+  has been limited to 6.
+- Make library build against `primitive-0.6.1.0`.
+
+## [0.4.11.1] - 2018-05-25
+### Change
+- Fix compatibility with older GHCs when `semigroupoids` support
+  is disabled.
+
+## [0.4.11] - 2018-05-24
+### Added
+- Greatly improved documentation
+- `specialisedLawsCheckMany` function, a shorter way for the user
+  to use `lawsCheckMany` on a single type.
+
+### Change
+- Some internal names, making it more clear what it is that they do.
+
+## [0.4.10] - 2018-05-03
+### Added
+- Property tests for `mconcat`, `sconcat`, and `stimes`. It isn't
+  common to override the defaults for these, but when you do, it's
+  nice to check that they agree with what they are supposed to do.
+
+## [0.4.9] - 2018-04-06
+### Change
+- Be more careful with import of `Data.Primitive`. There is a
+  branch of `primitive` that adds `PrimArray`. The implementation
+  of `PrimArray` in this library should eventually be removed, but
+  for now it will be sufficient to ensure that it does not create
+  a conflicting import problem with the one in the branch.
+
+## [0.4.8] - 2018-03-29
+### Change
+- Fix compilation regression for older versions of transformers.
+
+## [0.4.7] - 2018-03-29
+### Change
+- Split up monolithic module into hidden internal modules.
+- Fix compilation regression for older GHCs.
+
+## [0.4.6] - 2018-03-29
+### Added
+- Property test the naturality law for `MonadZip`. There is another law
+  that instances should satisfy (the Information Preservation law), but
+  it's more difficult to write a test for. It has been omitted for now.
+- Property tests for all `MonadPlus` laws.
+- Several additional property tests for list-like containers: mapMaybe,
+  replicate, filter.
+
+## [0.4.5] - 2018-03-26
+### Added
+- Property tests for list-like containers that have `IsList` instances.
+  These are useful for things that are nearly `Foldable` or nearly `Traversable`
+  but are either constrained in their element type or totally monomorphic
+  in it.
+
+## [0.4.4] - 2018-03-23
+### Added
+- Cabal flags for controlling whether or not `aeson` and `semigroupoids`
+  are used. These are mostly provided to accelerate builds `primitive`'s
+  test suite.
+
+## [0.4.3] - 2018-03-23
+### Added
+- Property tests for `foldl1` and `foldr1`.
+- Property tests for `Traversable`.
+
+## [0.4.2] - 2018-03-22
+### Changed
+- Made compatible with `transformers-0.3`. Tests for higher-kinded
+  typeclasses are unavailable when built with a sufficiently old
+  version of both `transformers` and `base`. This is because `Eq1`
+  and `Show1` are unavailable in this situation.
+
+## [0.4.1] - 2018-03-21
+### Changed
+- Made compatible with `transformers-0.4`.
+
+## [0.4.0] - 2018-03-20
+### Added
+- Property tests for `Bifunctor` and `Alternative`.
+### Changed
+- Made compatible with older GHCs all the way back to 7.8.4.
+- Lower dependency footprint. Eliminate the dependency on `prim-array`
+  and inline the relevant functions and types from it into
+  `Test.QuickCheck.Classes`. None of these are exported.
diff --git a/quickcheck-classes.cabal b/quickcheck-classes.cabal
--- a/quickcheck-classes.cabal
+++ b/quickcheck-classes.cabal
@@ -1,51 +1,197 @@
+cabal-version: 2.4
 name: quickcheck-classes
-version: 0.3.1
+version: 0.6.5.0
 synopsis: QuickCheck common typeclasses
 description:
-  This library provides quickcheck properties to
-  ensure that typeclass instances the set of laws that they are
-  supposed to. There are other libraries that do similar
-  things, such as `genvalidity-hspec` and `checkers`. This
-  library differs from other solutions by not introducing
-  any new typeclasses that the user needs to learn. The source
-  code for this library should be easy to understand if you
-  are already familiar with quickcheck. Open an issue
-  if you feel that this is not the case.
+  This library provides QuickCheck properties to ensure
+  that typeclass instances adhere to the set of laws that
+  they are supposed to. There are other libraries that do
+  similar things, such as `genvalidity-hspec` and `checkers`.
+  This library differs from other solutions by not introducing
+  any new typeclasses that the user needs to learn.
+  .
+  /Note:/ on GHC < 8.5, this library uses the higher-kinded typeclasses
+  ('Data.Functor.Classes.Show1', 'Data.Functor.Classes.Eq1', 'Data.Functor.Classes.Ord1', etc.),
+  but on GHC >= 8.5, it uses `-XQuantifiedConstraints` to express these
+  constraints more cleanly.
 homepage: https://github.com/andrewthad/quickcheck-classes#readme
-license: BSD3
+license: BSD-3-Clause
 license-file: LICENSE
-author: Andrew Martin
+author: Andrew Martin, chessai
 maintainer: andrew.thaddeus@gmail.com
-copyright: 2017 Andrew Martin
+copyright: 2018 Andrew Martin
 category: Testing
 build-type: Simple
 extra-source-files: README.md
-cabal-version: >=1.10
+extra-source-files: changelog.md
 
+flag aeson
+  description:
+    You can disable the use of the `aeson` package using `-f-aeson`.
+    .
+    This may be useful for accelerating builds in sandboxes for expert users.
+  default: True
+  manual: True
+
+flag semigroupoids
+  description:
+    You can disable the use of the `semigroupoids` package using `-f-semigroupoids`.
+    .
+    This may be useful for accelerating builds in sandboxes for expert users.
+  default: True
+  manual: True
+
+flag semirings
+  description:
+    You can disable the use of the `semirings` package using `-f-semirings`.
+    .
+    This may be useful for accelerating builds in sandboxes for expert users.
+  default: True
+  manual: True
+
+flag vector
+  description:
+    You can disable the use of the `vector` package using `-f-vector`.
+    .
+    This may be useful for accelerating builds in sandboxes for expert users.
+  default: True
+  manual: True
+
+flag unary-laws
+  description:
+    Include infrastructure for testing class laws of unary type constructors.
+    It is required that this flag match the value that the `unary-laws` flag
+    was given when building `quickcheck-classes-base`.
+  default: True
+  manual: True
+
+flag binary-laws
+  description:
+    Include infrastructure for testing class laws of binary type constructors.
+    It is required that this flag match the value that the `unary-laws` flag
+    was given when building `quickcheck-classes-base`. Disabling `unary-laws`
+    while keeping `binary-laws` enabled is an unsupported configuration.
+  default: True
+  manual: True
+
 library
+  default-language: Haskell2010
   hs-source-dirs: src
   exposed-modules:
     Test.QuickCheck.Classes
+    Test.QuickCheck.Classes.IsList
+  other-modules:
+    Test.QuickCheck.Classes.Alt
+    Test.QuickCheck.Classes.Apply
+    Test.QuickCheck.Classes.Euclidean
+    Test.QuickCheck.Classes.Json
+    Test.QuickCheck.Classes.MVector
+    Test.QuickCheck.Classes.Plus
+    Test.QuickCheck.Classes.Prim
+    Test.QuickCheck.Classes.Semigroupoid
+    Test.QuickCheck.Classes.Semiring
+    Test.QuickCheck.Classes.Ring
   build-depends:
-      base >= 4.7 && < 5
+    , base >= 4.5 && < 5
+    , QuickCheck >= 2.7
+    , transformers >= 0.3 && < 0.6
+    , primitive >= 0.6.4 && < 0.8
+    , primitive-addr >= 0.1.0.2 && < 0.2
+    , containers >= 0.4.2.1
+    , quickcheck-classes-base >=0.6.2 && <0.7
+  if impl(ghc < 8.0)
+    build-depends:
+      , semigroups >= 0.17
+      , fail
+  if impl(ghc < 7.8)
+    build-depends: tagged
+  if impl(ghc > 7.4) && impl(ghc < 7.6)
+    build-depends: ghc-prim
+  if impl(ghc > 8.5)
+    cpp-options: -DHAVE_QUANTIFIED_CONSTRAINTS
+  if flag(unary-laws)
+    build-depends:
+      , transformers >= 0.4.0
+      , QuickCheck >= 2.10.0
+    cpp-options: -DHAVE_UNARY_LAWS
+  if flag(binary-laws)
+    build-depends:
+      , transformers >= 0.5.0
+      , QuickCheck >= 2.10.0
+    cpp-options: -DHAVE_BINARY_LAWS
+  if flag(aeson)
+    build-depends: aeson >= 0.9
+    cpp-options: -DHAVE_AESON
+  if flag(semigroupoids)
+    build-depends: semigroupoids
+    cpp-options: -DHAVE_SEMIGROUPOIDS
+  if flag(semirings)
+    build-depends: semirings >= 0.4.2
+    cpp-options: -DHAVE_SEMIRINGS
+  if flag(vector)
+    build-depends: vector >= 0.12
+    cpp-options: -DHAVE_VECTOR
+
+-- The basic test suite is compatible with all the versions of GHC that
+-- this library supports. It is useful for confirming whether the laws tests
+-- behave correct. Additionally, it helps catch CPP mistakes.
+test-suite basic
+  type: exitcode-stdio-1.0
+  hs-source-dirs: test
+  main-is: Spec.hs
+  other-modules:
+    Spec.ShowRead
+  build-depends:
+    , base
+    , base-orphans >= 0.5
+    , quickcheck-classes
     , QuickCheck
-    , transformers
+    , containers
     , primitive
-    , prim-array
-    , aeson
+    , vector
+    , transformers
+    , tagged
+  if impl(ghc > 8.5)
+    cpp-options: -DHAVE_QUANTIFIED_CONSTRAINTS
+  if flag(unary-laws)
+    cpp-options: -DHAVE_UNARY_LAWS
+  if flag(binary-laws)
+    cpp-options: -DHAVE_BINARY_LAWS
+  if flag(aeson)
+    build-depends: aeson
+    cpp-options: -DHAVE_AESON
+  if flag(semigroupoids)
+    build-depends: semigroupoids
+    cpp-options: -DHAVE_SEMIGROUPOIDS
+  if flag(vector)
+    build-depends: vector >= 0.12
+    cpp-options: -DHAVE_VECTOR
   default-language: Haskell2010
 
-test-suite test
+-- The advanced test suite only builds with the newest version
+-- of GHC. It is intended to be a sort of regression test for GHC and for
+-- base. It check instances for a number of types in base. It also checks
+-- a bunch of derived instances for data types of varying sizes. And it
+-- does some tests on UnboxedSums.
+test-suite advanced
   type: exitcode-stdio-1.0
   hs-source-dirs: test
-  main-is: Spec.hs
+  main-is: Advanced.hs
+  ghc-options: -O2
   build-depends:
-      base
-    , quickcheck-classes
     , QuickCheck
+    , base >= 4.12
+    , base-orphans >= 0.5
+    , containers
     , primitive
-    , aeson
+    , quickcheck-classes
+    , tagged
+    , tasty
+    , tasty-quickcheck
+    , transformers
     , vector
+  if impl(ghc < 8.6)
+    buildable: False
   default-language: Haskell2010
 
 source-repository head
diff --git a/src/Test/QuickCheck/Classes.hs b/src/Test/QuickCheck/Classes.hs
--- a/src/Test/QuickCheck/Classes.hs
+++ b/src/Test/QuickCheck/Classes.hs
@@ -1,871 +1,140 @@
-{-# LANGUAGE BangPatterns #-}
 {-# LANGUAGE CPP #-}
-{-# LANGUAGE DeriveFunctor #-}
-{-# LANGUAGE FlexibleContexts #-}
-{-# LANGUAGE MagicHash #-}
-{-# LANGUAGE RankNTypes #-}
-{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE KindSignatures #-}
 
 {-# OPTIONS_GHC -Wall #-}
 
-{-|
-
-This library provides lists of properties that should hold for common typeclasses.
-All of these take a 'Proxy' argument that is used to nail down the type for which
-the typeclass dictionaries should be tested. For example, at GHCi:
-
->>> lawsCheck (monoidLaws (Proxy :: Proxy Ordering))
-Monoid: Associative +++ OK, passed 100 tests.
-Monoid: Left Identity +++ OK, passed 100 tests.
-Monoid: Right Identity +++ OK, passed 100 tests.
-
-Assuming that the 'Arbitrary' instance for 'Ordering' is good, we now
-have confidence that the 'Monoid' instance for 'Ordering' satisfies
-the monoid laws. We can check multiple typeclasses with:
-
->>> foldMap (lawsCheck . ($ (Proxy :: Proxy Word))) [jsonLaws,showReadLaws]
-ToJSON/FromJSON: Encoding Equals Value +++ OK, passed 100 tests.
-ToJSON/FromJSON: Partial Isomorphism +++ OK, passed 100 tests.
-Show/Read: Partial Isomorphism +++ OK, passed 100 tests.
+{-| This library provides sets of properties that should hold for common
+    typeclasses.
 
+    /Note:/ on GHC < 8.6, this library uses the higher-kinded typeclasses
+    ('Data.Functor.Classes.Show1', 'Data.Functor.Classes.Eq1', 'Data.Functor.Classes.Ord1', etc.),
+    but on GHC >= 8.6, it uses @-XQuantifiedConstraints@ to express these
+    constraints more cleanly.
 -}
 module Test.QuickCheck.Classes
   ( -- * Running
-    lawsCheck
-  , lawsCheckMany
+    QCB.lawsCheck
+  , QCB.lawsCheckMany
+  , QCB.lawsCheckOne
     -- * Properties
-    -- ** Ground Types
-  , semigroupLaws
-  , monoidLaws
-  , commutativeMonoidLaws
-  , eqLaws
-  , ordLaws
-  , showReadLaws
+    -- ** Ground types
+#if MIN_VERSION_base(4,7,0)
+  , QCB.bitsLaws
+#endif
+  , QCB.eqLaws
+  , QCB.substitutiveEqLaws
+  , QCB.numLaws
+  , QCB.integralLaws
+  , QCB.ixLaws
+#if MIN_VERSION_base(4,7,0)
+  , QCB.isListLaws
+#endif
+#if HAVE_AESON
   , jsonLaws
-  , isListLaws
+#endif
+  , QCB.monoidLaws
+  , QCB.commutativeMonoidLaws
+  , QCB.semigroupMonoidLaws
+  , QCB.ordLaws
+  , QCB.enumLaws
+  , QCB.boundedEnumLaws
   , primLaws
-  , storableLaws
-#if MIN_VERSION_QuickCheck(2,10,0)
-    -- ** Higher-Kinded Types
-  , functorLaws
-  , applicativeLaws
-  , monadLaws
-  , foldableLaws
+  , QCB.semigroupLaws
+  , QCB.commutativeSemigroupLaws
+  , QCB.exponentialSemigroupLaws
+  , QCB.idempotentSemigroupLaws
+  , QCB.rectangularBandSemigroupLaws
+#if HAVE_SEMIRINGS
+  , semiringLaws
+  , ringLaws
+  , gcdDomainLaws
+  , euclideanLaws
 #endif
+  , QCB.showLaws
+  , QCB.showReadLaws
+  , QCB.storableLaws
+#if MIN_VERSION_base(4,5,0)
+  , QCB.genericLaws
+  , QCB.generic1Laws
+#endif
+#if HAVE_UNARY_LAWS
+    -- ** Unary type constructors
+  , QCB.alternativeLaws
+#if HAVE_SEMIGROUPOIDS
+  , altLaws
+  , applyLaws
+#endif
+  , QCB.applicativeLaws
+  , QCB.contravariantLaws
+  , QCB.foldableLaws
+  , QCB.functorLaws
+  , QCB.monadLaws
+  , QCB.monadPlusLaws
+  , QCB.monadZipLaws
+#if HAVE_SEMIGROUPOIDS
+  , plusLaws
+  , extendedPlusLaws
+#endif
+  , QCB.traversableLaws
+#endif
+#if HAVE_BINARY_LAWS
+    -- ** Binary type constructors
+  , QCB.bifoldableLaws
+  , QCB.bifunctorLaws
+  , QCB.bitraversableLaws
+  , QCB.categoryLaws
+  , QCB.commutativeCategoryLaws
+#if HAVE_SEMIGROUPOIDS
+  , semigroupoidLaws
+  , commutativeSemigroupoidLaws
+#endif
+#if HAVE_VECTOR
+  , muvectorLaws
+#endif
+#endif
     -- * Types
-  , Laws(..)
+  , QCB.Laws(..)
+  , QCB.Proxy1(..)
+  , QCB.Proxy2(..)
   ) where
 
-import Test.QuickCheck
-import Test.QuickCheck.Monadic (monadicIO)
-import Test.QuickCheck.Property (Property(..))
-import Data.Primitive hiding (sizeOf,newArray,copyArray)
-import Data.Primitive.PrimArray
-import Data.Proxy
-import Control.Monad.ST
-import Control.Monad
-import Data.Monoid (Endo(..),Sum(..),Dual(..))
-import GHC.Ptr (Ptr(..))
-import Data.Primitive.Addr (Addr(..))
-import Foreign.Marshal.Alloc
-import System.IO.Unsafe
-import Data.Semigroup (Semigroup)
-import GHC.Exts (IsList(fromList,toList,fromListN),Item)
-import Foreign.Marshal.Array
-import Foreign.Storable
-import Text.Read (readMaybe)
-import Data.Aeson (FromJSON(..),ToJSON(..))
-import Data.Functor.Classes
-import Control.Applicative
-import Data.Foldable (foldlM,fold,foldMap,foldl',foldr')
-import Control.Exception (ErrorCall,evaluate,try)
-import Control.Monad.Trans.Class (lift)
-import qualified Data.Foldable as F
-import qualified Data.Aeson as AE
-import qualified Data.Primitive as P
-import qualified Data.Semigroup as SG
-import qualified GHC.OldList as L
-
-#if MIN_VERSION_QuickCheck(2,10,0)
-import Test.QuickCheck.Arbitrary (Arbitrary1(..))
-#endif
-
--- | A set of laws associated with a typeclass.
-data Laws = Laws
-  { lawsTypeclass :: String
-    -- ^ Name of the typeclass whose laws are tested
-  , lawsProperties :: [(String,Property)]
-    -- ^ Pairs of law name and property
-  }
-
--- | A convenience function for working testing properties in GHCi.
---   See the test suite of this library for an example of how to
---   integrate multiple properties into larger test suite.
-lawsCheck :: Laws -> IO ()
-lawsCheck (Laws className properties) = do
-  flip foldlMapM properties $ \(name,p) -> do
-    putStr (className ++ ": " ++ name ++ " ")
-    quickCheck p
-
--- | A convenience function for checking multiple typeclass instances
---   of multiple types.
-lawsCheckMany ::
-     [(String,[Laws])] -- ^ Element is type name paired with typeclass laws
-  -> IO ()
-lawsCheckMany xs = do
-  putStrLn "Testing properties for common typeclasses"
-  r <- flip foldlMapM xs $ \(typeName,laws) -> do
-    putStrLn $ "------------"
-    putStrLn $ "-- " ++ typeName
-    putStrLn $ "------------"
-    flip foldlMapM laws $ \(Laws typeClassName properties) -> do
-      flip foldlMapM properties $ \(name,p) -> do
-        putStr (typeClassName ++ ": " ++ name ++ " ")
-        r <- quickCheckResult p
-        return $ case r of
-          Success _ _ _ -> Good
-          _ -> Bad
-  putStrLn ""
-  case r of
-    Good -> putStrLn "All tests succeeded"
-    Bad -> putStrLn "One or more tests failed"
-
-data Status = Bad | Good
-
-instance Monoid Status where
-  mempty = Good
-  mappend Good x = x
-  mappend Bad _ = Bad
-
-foldlMapM :: (Foldable t, Monoid b, Monad m) => (a -> m b) -> t a -> m b
-foldlMapM f = foldlM (\b a -> fmap (mappend b) (f a)) mempty
-
-jsonLaws :: (ToJSON a, FromJSON a, Show a, Arbitrary a, Eq a) => Proxy a -> Laws
-jsonLaws p = Laws "ToJSON/FromJSON"
-  [ ("Encoding Equals Value", jsonEncodingEqualsValue p)
-  , ("Partial Isomorphism", jsonEncodingPartialIsomorphism p)
-  ]
-
--- | Tests the following properties:
 --
--- [/Partial Isomorphism/]
---   @fromList . toList ≡ id@
--- [/Length Preservation/]
---   @fromList xs ≡ fromListN (length xs) xs@
-isListLaws :: (IsList a, Show a, Show (Item a), Arbitrary a, Arbitrary (Item a), Eq a) => Proxy a -> Laws
-isListLaws p = Laws "IsList"
-  [ ("Partial Isomorphism", isListPartialIsomorphism p)
-  , ("Length Preservation", isListLengthPreservation p)
-  ]
-
-showReadLaws :: (Show a, Read a, Eq a, Arbitrary a) => Proxy a -> Laws
-showReadLaws p = Laws "Show/Read"
-  [ ("Partial Isomorphism", showReadPartialIsomorphism p)
-  ]
-
--- | Tests the following properties:
+-- re-exports
 --
--- [/Associative/]
---   @a <> (b <> c) ≡ (a <> b) <> c@
-semigroupLaws :: (Semigroup a, Eq a, Arbitrary a, Show a) => Proxy a -> Laws
-semigroupLaws p = Laws "Semigroup"
-  [ ("Associative", semigroupAssociative p)
-  ]
 
--- | Tests the following properties:
---
--- [/Transitive/]
---   @a == b ∧ b == c ⇒ a == c@
--- [/Symmetric/]
---   @a == b ⇒ b == a@
--- [/Reflexive/]
---   @a == a@
---
--- Some of these properties involve implication. In the case that
--- the left hand side of the implication arrow does not hold, we
--- do not retry. Consequently, these properties only end up being
--- useful when the data type has a small number of inhabitants.
-eqLaws :: (Eq a, Arbitrary a, Show a) => Proxy a -> Laws
-eqLaws p = Laws "Eq"
-  [ ("Transitive", eqTransitive p)
-  , ("Symmetric", eqSymmetric p)
-  , ("Reflexive", eqReflexive p)
-  ]
-
--- | Tests the following properties:
---
--- [/Transitive/]
---   @a ≤ b ∧ b ≤ c ⇒ a ≤ c@
--- [/Comparable/]
---   @a ≤ b ∨ a > b@
-ordLaws :: (Ord a, Arbitrary a, Show a) => Proxy a -> Laws
-ordLaws p = Laws "Ord"
-  [ ("Transitive", ordTransitive p)
-  , ("Comparable", ordComparable p)
-  ]
-
--- | Tests the following properties:
---
--- [/Associative/]
---   @mappend a (mappend b c) ≡ mappend (mappend a b) c@
--- [/Left Identity/]
---   @mappend mempty a ≡ a@
--- [/Right Identity/]
---   @mappend a mempty ≡ a@
-monoidLaws :: (Monoid a, Eq a, Arbitrary a, Show a) => Proxy a -> Laws
-monoidLaws p = Laws "Monoid"
-  [ ("Associative", monoidAssociative p)
-  , ("Left Identity", monoidLeftIdentity p)
-  , ("Right Identity", monoidRightIdentity p)
-  ]
-
--- | Tests everything from 'monoidProps' plus the following:
---
--- [/Commutative/]
---   @mappend a b ≡ mappend b a@
-commutativeMonoidLaws :: (Monoid a, Eq a, Arbitrary a, Show a) => Proxy a -> Laws
-commutativeMonoidLaws p = Laws "Commutative Monoid" $ lawsProperties (monoidLaws p) ++
-  [ ("Commutative", monoidCommutative p)
-  ]
-
--- | Test that a 'Prim' instance obey the several laws.
-primLaws :: (Prim a, Eq a, Arbitrary a, Show a) => Proxy a -> Laws
-primLaws p = Laws "Prim"
-  [ ("ByteArray Set-Get (you get back what you put in)", primSetGetByteArray p)
-  , ("ByteArray Get-Set (putting back what you got out has no effect)", primGetSetByteArray p)
-  , ("ByteArray Set-Set (setting twice is same as setting once)", primSetSetByteArray p)
-  , ("ByteArray List Conversion Roundtrips", primListByteArray p)
-  , ("Addr Set-Get (you get back what you put in)", primSetGetAddr p)
-  , ("Addr Get-Set (putting back what you got out has no effect)", primGetSetAddr p)
-  , ("Addr List Conversion Roundtrips", primListAddr p)
-  ]
-
-storableLaws :: (Storable a, Eq a, Arbitrary a, Show a) => Proxy a -> Laws
-storableLaws p = Laws "Storable"
-  [ ("Set-Get (you get back what you put in)", storableSetGet p)
-  , ("Get-Set (putting back what you got out has no effect)", storableGetSet p)
-  , ("List Conversion Roundtrips", storableList p)
-  ]
-
-isListPartialIsomorphism :: forall a. (IsList a, Show a, Arbitrary a, Eq a) => Proxy a -> Property
-isListPartialIsomorphism _ = myForAllShrink False
-  (\(a :: a) -> ["a = " ++ show a])
-  "fromList (toList a)"
-  (\a -> fromList (toList a))
-  "a"
-  (\a -> a)
-
-isListLengthPreservation :: forall a. (IsList a, Show (Item a), Arbitrary (Item a), Eq a) => Proxy a -> Property
-isListLengthPreservation _ = property $ \(xs :: [Item a]) ->
-  (fromList xs :: a) == fromListN (length xs) xs
-
-showReadPartialIsomorphism :: forall a. (Show a, Read a, Arbitrary a, Eq a) => Proxy a -> Property
-showReadPartialIsomorphism _ = property $ \(a :: a) ->
-  readMaybe (show a) == Just a
-
--- TODO: improve the quality of the error message if
--- something does not pass this test.
-jsonEncodingEqualsValue :: forall a. (ToJSON a, Show a, Arbitrary a) => Proxy a -> Property
-jsonEncodingEqualsValue _ = property $ \(a :: a) ->
-  case AE.decode (AE.encode a) of
-    Nothing -> False
-    Just (v :: AE.Value) -> v == toJSON a
-
-jsonEncodingPartialIsomorphism :: forall a. (ToJSON a, FromJSON a, Show a, Eq a, Arbitrary a) => Proxy a -> Property
-jsonEncodingPartialIsomorphism _ = property $ \(a :: a) ->
-  AE.decode (AE.encode a) == Just a
-
-eqTransitive :: forall a. (Show a, Eq a, Arbitrary a) => Proxy a -> Property
-eqTransitive _ = property $ \(a :: a) b c -> case a == b of
-  True -> case b == c of
-    True -> a == c
-    False -> a /= c
-  False -> case b == c of
-    True -> a /= c
-    False -> True
-
--- Technically, this tests something a little stronger than it is supposed to.
--- But that should be alright since this additional strength is implied by
--- the rest of the Ord laws.
-ordTransitive :: forall a. (Show a, Ord a, Arbitrary a) => Proxy a -> Property
-ordTransitive _ = property $ \(a :: a) b c -> case (compare a b, compare b c) of
-  (LT,LT) -> a < c
-  (LT,EQ) -> a < c
-  (LT,GT) -> True
-  (EQ,LT) -> a < c
-  (EQ,EQ) -> a == c
-  (EQ,GT) -> a > c
-  (GT,LT) -> True
-  (GT,EQ) -> a > c
-  (GT,GT) -> a > c
-
-ordComparable :: forall a. (Show a, Ord a, Arbitrary a) => Proxy a -> Property
-ordComparable _ = property $ \(a :: a) b -> a > b || b >= a
-
-eqSymmetric :: forall a. (Show a, Eq a, Arbitrary a) => Proxy a -> Property
-eqSymmetric _ = property $ \(a :: a) b -> case a == b of
-  True -> b == a
-  False -> b /= a
-
-eqReflexive :: forall a. (Show a, Eq a, Arbitrary a) => Proxy a -> Property
-eqReflexive _ = property $ \(a :: a) -> a == a
-
-semigroupAssociative :: forall a. (Semigroup a, Eq a, Arbitrary a, Show a) => Proxy a -> Property
-semigroupAssociative _ = property $ \(a :: a) b c -> a SG.<> (b SG.<> c) == (a SG.<> b) SG.<> c
-
-monoidAssociative :: forall a. (Monoid a, Eq a, Arbitrary a, Show a) => Proxy a -> Property
-monoidAssociative _ = myForAllShrink True
-  (\(a :: a,b,c) -> ["a = " ++ show a, "b = " ++ show b, "c = " ++ show c])
-  "mappend a (mappend b c)"
-  (\(a,b,c) -> mappend a (mappend b c))
-  "mappend (mappend a b) c"
-  (\(a,b,c) -> mappend (mappend a b) c)
-
-monoidLeftIdentity :: forall a. (Monoid a, Eq a, Arbitrary a, Show a) => Proxy a -> Property
-monoidLeftIdentity _ = myForAllShrink False
-  (\(a :: a) -> ["a = " ++ show a])
-  "mappend mempty a"
-  (\a -> mappend mempty a)
-  "a"
-  (\a -> a)
-
-monoidRightIdentity :: forall a. (Monoid a, Eq a, Arbitrary a, Show a) => Proxy a -> Property
-monoidRightIdentity _ = myForAllShrink False
-  (\(a :: a) -> ["a = " ++ show a])
-  "mappend a mempty"
-  (\a -> mappend a mempty)
-  "a"
-  (\a -> a)
-
-monoidCommutative :: forall a. (Monoid a, Eq a, Arbitrary a, Show a) => Proxy a -> Property
-monoidCommutative _ = property $ \(a :: a) b -> mappend a b == mappend b a
-
-primListByteArray :: forall a. (Prim a, Eq a, Arbitrary a, Show a) => Proxy a -> Property
-primListByteArray _ = property $ \(as :: [a]) ->
-  as == toList (fromList as :: PrimArray a)
-
-primListAddr :: forall a. (Prim a, Eq a, Arbitrary a, Show a) => Proxy a -> Property
-primListAddr _ = property $ \(as :: [a]) -> unsafePerformIO $ do
-  let len = L.length as
-  ptr@(Ptr addr#) :: Ptr a <- mallocBytes (len * P.sizeOf (undefined :: a))
-  let addr = Addr addr#
-  let go :: Int -> [a] -> IO ()
-      go !ix xs = case xs of
-        [] -> return ()
-        (x : xsNext) -> do
-          writeOffAddr addr ix x
-          go (ix + 1) xsNext
-  go 0 as
-  let rebuild :: Int -> IO [a]
-      rebuild !ix = if ix < len
-        then (:) <$> readOffAddr addr ix <*> rebuild (ix + 1)
-        else return []
-  asNew <- rebuild 0
-  free ptr
-  return (as == asNew)
-
-primSetGetByteArray :: forall a. (Prim a, Eq a, Arbitrary a, Show a) => Proxy a -> Property
-primSetGetByteArray _ = property $ \(a :: a) len -> (len > 0) ==> do
-  ix <- choose (0,len - 1)
-  return $ runST $ do
-    arr <- newPrimArray len
-    writePrimArray arr ix a
-    a' <- readPrimArray arr ix
-    return (a == a')
-
-primGetSetByteArray :: forall a. (Prim a, Eq a, Arbitrary a, Show a) => Proxy a -> Property
-primGetSetByteArray _ = property $ \(as :: [a]) -> (not (L.null as)) ==> do
-  let arr1 = fromList as :: PrimArray a
-      len = L.length as
-  ix <- choose (0,len - 1)
-  arr2 <- return $ runST $ do
-    marr <- newPrimArray len
-    copyPrimArray marr 0 arr1 0 len
-    a <- readPrimArray marr ix
-    writePrimArray marr ix a
-    unsafeFreezePrimArray marr
-  return (arr1 == arr2)
-
-primSetSetByteArray :: forall a. (Prim a, Eq a, Arbitrary a, Show a) => Proxy a -> Property
-primSetSetByteArray _ = property $ \(a :: a) (as :: [a]) -> (not (L.null as)) ==> do
-  let arr1 = fromList as :: PrimArray a
-      len = L.length as
-  ix <- choose (0,len - 1)
-  (arr2,arr3) <- return $ runST $ do
-    marr2 <- newPrimArray len
-    copyPrimArray marr2 0 arr1 0 len
-    writePrimArray marr2 ix a
-    marr3 <- newPrimArray len
-    copyMutablePrimArray marr3 0 marr2 0 len
-    arr2 <- unsafeFreezePrimArray marr2
-    writePrimArray marr3 ix a
-    arr3 <- unsafeFreezePrimArray marr3
-    return (arr2,arr3)
-  return (arr2 == arr3)
-
-primSetGetAddr :: forall a. (Prim a, Eq a, Arbitrary a, Show a) => Proxy a -> Property
-primSetGetAddr _ = property $ \(a :: a) len -> (len > 0) ==> do
-  ix <- choose (0,len - 1)
-  return $ unsafePerformIO $ do
-    ptr@(Ptr addr#) :: Ptr a <- mallocBytes (len * P.sizeOf (undefined :: a))
-    let addr = Addr addr#
-    writeOffAddr addr ix a
-    a' <- readOffAddr addr ix
-    free ptr
-    return (a == a')
-
-primGetSetAddr :: forall a. (Prim a, Eq a, Arbitrary a, Show a) => Proxy a -> Property
-primGetSetAddr _ = property $ \(as :: [a]) -> (not (L.null as)) ==> do
-  let arr1 = fromList as :: PrimArray a
-      len = L.length as
-  ix <- choose (0,len - 1)
-  arr2 <- return $ unsafePerformIO $ do
-    ptr@(Ptr addr#) :: Ptr a <- mallocBytes (len * P.sizeOf (undefined :: a))
-    let addr = Addr addr#
-    copyPrimArrayToPtr ptr arr1 0 len
-    a :: a <- readOffAddr addr ix
-    writeOffAddr addr ix a
-    marr <- newPrimArray len
-    copyPtrToMutablePrimArray marr 0 ptr len
-    free ptr
-    unsafeFreezePrimArray marr
-  return (arr1 == arr2)
-
-storableSetGet :: forall a. (Storable a, Eq a, Arbitrary a, Show a) => Proxy a -> Property
-storableSetGet _ = property $ \(a :: a) len -> (len > 0) ==> do
-  ix <- choose (0,len - 1)
-  return $ unsafePerformIO $ do
-    ptr :: Ptr a <- mallocArray len
-    pokeElemOff ptr ix a
-    a' <- peekElemOff ptr ix
-    free ptr
-    return (a == a')
-
-storableGetSet :: forall a. (Storable a, Eq a, Arbitrary a, Show a) => Proxy a -> Property
-storableGetSet _ = property $ \(as :: [a]) -> (not (L.null as)) ==> do
-  let len = L.length as
-  ix <- choose (0,len - 1)
-  return $ unsafePerformIO $ do
-    ptrA <- newArray as
-    ptrB <- mallocArray len
-    copyArray ptrB ptrA len
-    a <- peekElemOff ptrA ix
-    pokeElemOff ptrA ix a
-    res <- arrayEq ptrA ptrB len
-    free ptrA
-    free ptrB
-    return res
-
-storableList :: forall a. (Storable a, Eq a, Arbitrary a, Show a) => Proxy a -> Property
-storableList _ = property $ \(as :: [a]) -> unsafePerformIO $ do
-  let len = L.length as
-  ptr <- newArray as
-  let rebuild :: Int -> IO [a]
-      rebuild !ix = if ix < len
-        then (:) <$> peekElemOff ptr ix <*> rebuild (ix + 1)
-        else return []
-  asNew <- rebuild 0
-  free ptr
-  return (as == asNew)
-
-arrayEq :: forall a. (Storable a, Eq a) => Ptr a -> Ptr a -> Int -> IO Bool
-arrayEq ptrA ptrB len = go 0 where
-  go !i = if i < len
-    then do
-      a <- peekElemOff ptrA i
-      b <- peekElemOff ptrB i
-      if a == b
-        then go (i + 1)
-        else return False
-    else return True
-
-#if MIN_VERSION_QuickCheck(2,10,0)
--- | Tests the following applicative properties:
---
--- [/Identity/]
---   @'fmap' 'id' ≡ 'id'@
--- [/Composition/]
---   @fmap (f . g) ≡ 'fmap' f . 'fmap' g@
--- [/Const/]
---   @(<$) ≡ 'fmap' 'const'@
-functorLaws :: (Functor f, Eq1 f, Show1 f, Arbitrary1 f) => Proxy f -> Laws
-functorLaws p = Laws "Functor"
-  [ ("Identity", functorIdentity p)
-  , ("Composition", functorComposition p)
-  , ("Const", functorConst p)
-  ]
-
--- | Tests the following applicative properties:
---
--- [/Identity/]
---   @'pure' 'id' '<*>' v ≡ v@
--- [/Composition/]
---   @'pure' (.) '<*>' u '<*>' v '<*>' w ≡ u '<*>' (v '<*>' w)@
--- [/Homomorphism/]
---   @'pure' f '<*>' 'pure' x ≡ 'pure' (f x)@
--- [/Interchange/]
---   @u '<*>' 'pure' y ≡ 'pure' ('$' y) '<*>' u@
--- [/LiftA2 (1)/]
---   @('<*>') ≡ 'liftA2' 'id'@
-applicativeLaws :: (Applicative f, Eq1 f, Show1 f, Arbitrary1 f) => Proxy f -> Laws
-applicativeLaws p = Laws "Applicative"
-  [ ("Identity", applicativeIdentity p)
-  , ("Composition", applicativeComposition p)
-  , ("Homomorphism", applicativeHomomorphism p)
-  , ("Interchange", applicativeInterchange p)
-  , ("LiftA2 Part 1", applicativeLiftA2_1 p)
-    -- todo: liftA2 part 2, we need an equation of two variables for this
-  ]
-
-
--- | Tests the following monadic properties:
---
--- [/Left Identity/]
---   @'return' a '>>=' k ≡ k a@
--- [/Right Identity/]
---   @m '>>=' 'return' ≡ m@
--- [/Associativity/]
---   @m '>>=' (\\x -> k x '>>=' h) ≡ (m '>>=' k) '>>=' h@
--- [/Return/]
---   @'pure' ≡ 'return'@
--- [/Ap/]
---   @('<*>') ≡ 'ap'@
-monadLaws :: (Monad f, Eq1 f, Show1 f, Arbitrary1 f) => Proxy f -> Laws
-monadLaws p = Laws "Monad"
-  [ ("Left Identity", monadLeftIdentity p)
-  , ("Right Identity", monadRightIdentity p)
-  , ("Associativity", monadAssociativity p)
-  , ("Return", monadReturn p)
-  , ("Ap", monadAp p)
-  ]
-
--- | Tests the following 'Foldable' properties:
---
--- [/fold/]
---   @'fold' ≡ 'foldMap' 'id'@
--- [/foldMap/]
---   @'foldMap' f ≡ 'foldr' ('mappend' . f) 'mempty'@
--- [/foldr/]
---   @'foldr' f z t ≡ 'appEndo' ('foldMap' ('Endo' . f) t ) z@
--- [/foldr'/]
---   @'foldr'' f z0 xs = let f\' k x z = k '$!' f x z in 'foldl' f\' 'id' xs z0@
--- [/foldl/]
---   @'foldl' f z t ≡ 'appEndo' ('getDual' ('foldMap' ('Dual' . 'Endo' . 'flip' f) t)) z@
--- [/foldl'/]
---   @'foldl'' f z0 xs = let f' x k z = k '$!' f z x in 'foldr' f\' 'id' xs z0@
--- [/toList/]
---   @'F.toList' ≡ 'foldr' (:) []@
--- [/null/]
---   @'null' ≡ 'foldr' ('const' ('const' 'False')) 'True'@
--- [/length/]
---   @'length' ≡ getSum . foldMap ('const' ('Sum' 1))@
---
--- Note that this checks to ensure that @foldl\'@ and @foldr\'@
--- are suitably strict.
-foldableLaws :: (Foldable f, Eq1 f, Show1 f, Arbitrary1 f) => Proxy f -> Laws
-foldableLaws = foldableLawsInternal
-
-foldableLawsInternal :: forall f. (Foldable f, Eq1 f, Show1 f, Arbitrary1 f) => Proxy f -> Laws
-foldableLawsInternal p = Laws "Foldable"
-  [ (,) "fold" $ property $ \(Apply (a :: f (Sum Integer))) ->
-      fold a == foldMap id a
-  , (,) "foldMap" $ property $ \(Apply (a :: f Integer)) (e :: Equation) ->
-      let f = Sum . runEquation e
-       in foldMap f a == foldr (mappend . f) mempty a
-  , (,) "foldr" $ property $ \(e :: EquationTwo) (z :: Integer) (Apply (t :: f Integer)) ->
-      let f = runEquationTwo e
-       in foldr f z t == appEndo (foldMap (Endo . f) t) z
-  , (,) "foldr'" (foldableFoldr' p)
-  , (,) "foldl" $ property $ \(e :: EquationTwo) (z :: Integer) (Apply (t :: f Integer)) ->
-      let f = runEquationTwo e
-       in foldl f z t == appEndo (getDual (foldMap (Dual . Endo . flip f) t)) z
-  , (,) "foldl'" (foldableFoldl' p)
-  , (,) "toList" $ property $ \(Apply (t :: f Integer)) ->
-      eq1 (F.toList t) (foldr (:) [] t)
-  , (,) "null" $ property $ \(Apply (t :: f Integer)) ->
-      null t == foldr (const (const False)) True t
-  , (,) "length" $ property $ \(Apply (t :: f Integer)) ->
-      length t == getSum (foldMap (const (Sum 1)) t)
-  ]
-
-foldableFoldl' :: forall f. (Foldable f, Eq1 f, Show1 f, Arbitrary1 f) => Proxy f -> Property
-foldableFoldl' _ = property $ \(_ :: ChooseSecond) (_ :: LastNothing) (Apply (xs :: f (Bottom Integer))) ->
-  monadicIO $ do
-    let f :: Integer -> Bottom Integer -> Integer
-        f a b = case b of
-          BottomUndefined -> error "foldableFoldl' example"
-          BottomValue v -> if even v
-            then a
-            else v
-        z0 = 0
-    r1 <- lift $ do
-      let f' x k z = k $! f z x
-      e <- try (evaluate (foldr f' id xs z0))
-      case e of
-        Left (_ :: ErrorCall) -> return Nothing
-        Right i -> return (Just i)
-    r2 <- lift $ do
-      e <- try (evaluate (foldl' f z0 xs))
-      case e of
-        Left (_ :: ErrorCall) -> return Nothing
-        Right i -> return (Just i)
-    return (r1 == r2)
-
-foldableFoldr' :: forall f. (Foldable f, Eq1 f, Show1 f, Arbitrary1 f) => Proxy f -> Property
-foldableFoldr' _ = property $ \(_ :: ChooseFirst) (_ :: LastNothing) (Apply (xs :: f (Bottom Integer))) ->
-  monadicIO $ do
-    let f :: Bottom Integer -> Integer -> Integer
-        f a b = case a of
-          BottomUndefined -> error "foldableFoldl' example"
-          BottomValue v -> if even v
-            then v
-            else b
-        z0 = 0
-    r1 <- lift $ do
-      let f' k x z = k $! f x z
-      e <- try (evaluate (foldl f' id xs z0))
-      case e of
-        Left (_ :: ErrorCall) -> return Nothing
-        Right i -> return (Just i)
-    r2 <- lift $ do
-      e <- try (evaluate (foldr' f z0 xs))
-      case e of
-        Left (_ :: ErrorCall) -> return Nothing
-        Right i -> return (Just i)
-    return (r1 == r2)
-
-data ChooseSecond = ChooseSecond
-  deriving (Eq)
-
-data ChooseFirst = ChooseFirst
-  deriving (Eq)
-
-data LastNothing = LastNothing
-  deriving (Eq)
-
-data Bottom a = BottomUndefined | BottomValue a
-  deriving (Eq)
-
-instance Show ChooseFirst where
-  show ChooseFirst = "\\a b -> if even a then a else b"
-
-instance Show ChooseSecond where
-  show ChooseSecond = "\\a b -> if even b then a else b"
-
-instance Show LastNothing where
-  show LastNothing = "0"
-
-instance Show a => Show (Bottom a) where
-  show x = case x of
-    BottomUndefined -> "undefined"
-    BottomValue a -> show a
-
-instance Arbitrary ChooseSecond where
-  arbitrary = pure ChooseSecond
-
-instance Arbitrary ChooseFirst where
-  arbitrary = pure ChooseFirst
-
-instance Arbitrary LastNothing where
-  arbitrary = pure LastNothing
-
-instance Arbitrary a => Arbitrary (Bottom a) where
-  arbitrary = fmap maybeToBottom arbitrary
-  shrink x = map maybeToBottom (shrink (bottomToMaybe x))
-
-bottomToMaybe :: Bottom a -> Maybe a
-bottomToMaybe BottomUndefined = Nothing
-bottomToMaybe (BottomValue a) = Just a
-
-maybeToBottom :: Maybe a -> Bottom a
-maybeToBottom Nothing = BottomUndefined
-maybeToBottom (Just a) = BottomValue a
-
-data Apply f a = Apply { getApply :: f a }
-
-instance (Eq1 f, Eq a) => Eq (Apply f a) where
-  Apply a == Apply b = eq1 a b
-
-data LinearEquation = LinearEquation
-  { _linearEquationLinear :: Integer
-  , _linearEquationConstant :: Integer
-  } deriving (Eq)
-
-data LinearEquationM m = LinearEquationM (m LinearEquation) (m LinearEquation)
-
-runLinearEquation :: Integer -> LinearEquation -> Integer
-runLinearEquation x (LinearEquation a b) = a * x + b
-
-runLinearEquationM :: Functor m => LinearEquationM m -> Integer -> m Integer
-runLinearEquationM (LinearEquationM e1 e2) i = if odd i
-  then fmap (runLinearEquation i) e1
-  else fmap (runLinearEquation i) e2
-
-instance Eq1 m => Eq (LinearEquationM m) where
-  LinearEquationM a1 b1 == LinearEquationM a2 b2 = eq1 a1 a2 && eq1 b1 b2
-
-showLinear :: Int -> LinearEquation -> ShowS
-showLinear _ (LinearEquation a b) = shows a . showString " * x + " . shows b
-
-showLinearList :: [LinearEquation] -> ShowS
-showLinearList xs = appEndo $ mconcat
-   $ [Endo (showChar '[')]
-  ++ L.intersperse (Endo (showChar ',')) (map (Endo . showLinear 0) xs)
-  ++ [Endo (showChar ']')]
-
-instance Show1 m => Show (LinearEquationM m) where
-  show (LinearEquationM a b) = (\f -> f "")
-    $ showString "\\x -> if odd x then "
-    . liftShowsPrec showLinear showLinearList 0 a
-    . showString " else "
-    . liftShowsPrec showLinear showLinearList 0 b
-
-instance Arbitrary1 m => Arbitrary (LinearEquationM m) where
-  arbitrary = liftA2 LinearEquationM arbitrary1 arbitrary1
-  shrink (LinearEquationM a b) = concat
-    [ map (\x -> LinearEquationM x b) (shrink1 a)
-    , map (\x -> LinearEquationM a x) (shrink1 b)
-    ]
-
-instance Arbitrary LinearEquation where
-  arbitrary = do
-    (a,b) <- arbitrary
-    return (LinearEquation (abs a) (abs b))
-  shrink (LinearEquation a b) =
-    let xs = shrink (a,b)
-     in map (\(x,y) -> LinearEquation (abs x) (abs y)) xs
-
--- this is a quadratic equation
-data Equation = Equation Integer Integer Integer
-  deriving (Eq)
-
--- This show instance is does not actually provide a
--- way to create an equation. Instead, it makes it look
--- like a lambda.
-instance Show Equation where
-  show (Equation a b c) = "\\x -> " ++ show a ++ " * x ^ 2 + " ++ show b ++ " * x + " ++ show c
-
-instance Arbitrary Equation where
-  arbitrary = do
-    (a,b,c) <- arbitrary
-    return (Equation (abs a) (abs b) (abs c))
-  shrink (Equation a b c) =
-    let xs = shrink (a,b,c)
-     in map (\(x,y,z) -> Equation (abs x) (abs y) (abs z)) xs
-
-runEquation :: Equation -> Integer -> Integer
-runEquation (Equation a b c) x = a * x ^ (2 :: Integer) + b * x + c
-
--- linear equation of two variables
-data EquationTwo = EquationTwo Integer Integer
-  deriving (Eq)
-
--- This show instance is does not actually provide a
--- way to create an EquationTwo. Instead, it makes it look
--- like a lambda that takes two variables.
-instance Show EquationTwo where
-  show (EquationTwo a b) = "\\x y -> " ++ show a ++ " * x + " ++ show b ++ " * y"
-
-instance Arbitrary EquationTwo where
-  arbitrary = do
-    (a,b) <- arbitrary
-    return (EquationTwo (abs a) (abs b))
-  shrink (EquationTwo a b) =
-    let xs = shrink (a,b)
-     in map (\(x,y) -> EquationTwo (abs x) (abs y)) xs
-
-runEquationTwo :: EquationTwo -> Integer -> Integer -> Integer
-runEquationTwo (EquationTwo a b) x y = a * x + b * y
-
--- This show instance is intentionally a little bit wrong.
--- We don't wrap the result in Apply since the end user
--- should not be made aware of the Apply wrapper anyway.
-instance (Show1 f, Show a) => Show (Apply f a) where
-  showsPrec p = showsPrec1 p . getApply
-
-instance (Arbitrary1 f, Arbitrary a) => Arbitrary (Apply f a) where
-  arbitrary = fmap Apply arbitrary1
-  shrink = map Apply . shrink1 . getApply
-
-functorIdentity :: forall f. (Functor f, Eq1 f, Show1 f, Arbitrary1 f) => Proxy f -> Property
-functorIdentity _ = property $ \(Apply (a :: f Integer)) -> eq1 (fmap id a) a
-
-func1 :: Integer -> (Integer,Integer)
-func1 i = (div (i + 5) 3, i * i - 2 * i + 1)
-
-func2 :: (Integer,Integer) -> (Bool,Either Ordering Integer)
-func2 (a,b) = (odd a, if even a then Left (compare a b) else Right (b + 2))
-
-functorComposition :: forall f. (Functor f, Eq1 f, Show1 f, Arbitrary1 f) => Proxy f -> Property
-functorComposition _ = property $ \(Apply (a :: f Integer)) ->
-  eq1 (fmap func2 (fmap func1 a)) (fmap (func2 . func1) a)
-
-functorConst :: forall f. (Functor f, Eq1 f, Show1 f, Arbitrary1 f) => Proxy f -> Property
-functorConst _ = property $ \(Apply (a :: f Integer)) ->
-  eq1 (fmap (const 'X') a) ('X' <$ a)
-
-applicativeIdentity :: forall f. (Applicative f, Eq1 f, Show1 f, Arbitrary1 f) => Proxy f -> Property
-applicativeIdentity _ = property $ \(Apply (a :: f Integer)) -> eq1 (pure id <*> a) a
-
-applicativeComposition :: forall f. (Applicative f, Eq1 f, Show1 f, Arbitrary1 f) => Proxy f -> Property
-applicativeComposition _ = property $ \(Apply (u' :: f Equation)) (Apply (v' :: f Equation)) (Apply (w :: f Integer)) ->
-  let u = fmap runEquation u'
-      v = fmap runEquation v'
-   in eq1 (pure (.) <*> u <*> v <*> w) (u <*> (v <*> w))
-
-applicativeHomomorphism :: forall f. (Applicative f, Eq1 f, Show1 f) => Proxy f -> Property
-applicativeHomomorphism _ = property $ \(e :: Equation) (a :: Integer) ->
-  let f = runEquation e
-   in eq1 (pure f <*> pure a) (pure (f a) :: f Integer)
-
-applicativeInterchange :: forall f. (Applicative f, Eq1 f, Show1 f, Arbitrary1 f) => Proxy f -> Property
-applicativeInterchange _ = property $ \(Apply (u' :: f Equation)) (y :: Integer) ->
-  let u = fmap runEquation u'
-   in eq1 (u <*> pure y) (pure ($ y) <*> u)
-
-applicativeLiftA2_1 :: forall f. (Applicative f, Eq1 f, Show1 f, Arbitrary1 f) => Proxy f -> Property
-applicativeLiftA2_1 _ = property $ \(Apply (f' :: f Equation)) (Apply (x :: f Integer)) -> 
-  let f = fmap runEquation f'
-   in eq1 (liftA2 id f x) (f <*> x)
-
-monadLeftIdentity :: forall f. (Monad f, Eq1 f, Show1 f, Arbitrary1 f) => Proxy f -> Property
-monadLeftIdentity _ = property $ \(k' :: LinearEquationM f) (a :: Integer) -> 
-  let k = runLinearEquationM k'
-   in eq1 (return a >>= k) (k a)
-
-monadRightIdentity :: forall f. (Monad f, Eq1 f, Show1 f, Arbitrary1 f) => Proxy f -> Property
-monadRightIdentity _ = property $ \(Apply (m :: f Integer)) -> 
-  eq1 (m >>= return) m
-
-monadAssociativity :: forall f. (Monad f, Eq1 f, Show1 f, Arbitrary1 f) => Proxy f -> Property
-monadAssociativity _ = property $ \(Apply (m :: f Integer)) (k' :: LinearEquationM f) (h' :: LinearEquationM f) -> 
-  let k = runLinearEquationM k'
-      h = runLinearEquationM h'
-   in eq1 (m >>= (\x -> k x >>= h)) ((m >>= k) >>= h)
-
-monadReturn :: forall f. (Monad f, Eq1 f, Show1 f, Arbitrary1 f) => Proxy f -> Property
-monadReturn _ = property $ \(x :: Integer) ->
-  eq1 (return x) (pure x :: f Integer)
-
-monadAp :: forall f. (Monad f, Eq1 f, Show1 f, Arbitrary1 f) => Proxy f -> Property
-monadAp _ = property $ \(Apply (f' :: f Equation)) (Apply (x :: f Integer)) -> 
-  let f = fmap runEquation f'
-   in eq1 (ap f x) (f <*> x)
+-- Ground Types
+#if MIN_VERSION_base(4,7,0)
+import Test.QuickCheck.Classes.IsList
+#endif
+#if HAVE_AESON
+import Test.QuickCheck.Classes.Json
+#endif
+import Test.QuickCheck.Classes.Prim
+#if HAVE_SEMIRINGS
+import Test.QuickCheck.Classes.Euclidean
+import Test.QuickCheck.Classes.Semiring
+import Test.QuickCheck.Classes.Ring
+#endif
+-- Unary type constructors
+#if HAVE_UNARY_LAWS
+#if HAVE_SEMIGROUPOIDS
+import Test.QuickCheck.Classes.Alt
+import Test.QuickCheck.Classes.Apply
+#endif
+#if HAVE_SEMIGROUPOIDS
+import Test.QuickCheck.Classes.Plus
+#endif
+#endif
 
+-- Binary type constructors
+#if HAVE_BINARY_LAWS
+#if HAVE_SEMIGROUPOIDS
+import Test.QuickCheck.Classes.Semigroupoid
 #endif
+#endif
 
-myForAllShrink :: (Arbitrary a, Show b, Eq b) => Bool -> (a -> [String]) -> String -> (a -> b) -> String -> (a -> b) -> Property
-myForAllShrink displayRhs showInputs name1 calc1 name2 calc2 =
-  again $
-  MkProperty $
-  arbitrary >>= \x ->
-    unProperty $
-    shrinking shrink x $ \x' ->
-      let b1 = calc1 x'
-          b2 = calc2 x'
-          sb1 = show b1
-          sb2 = show b2
-          description = "  Description: " ++ name1 ++ " = " ++ name2
-          err = description ++ "\n" ++ unlines (map ("  " ++) (showInputs x)) ++ "  " ++ name1 ++ " = " ++ sb1 ++ (if displayRhs then "\n  " ++ name2 ++ " = " ++ sb2 else "")
-       in counterexample err (b1 == b2)
+#if HAVE_VECTOR
+import Test.QuickCheck.Classes.MVector
+#endif
 
+import qualified Test.QuickCheck.Classes.Base as QCB
diff --git a/src/Test/QuickCheck/Classes/Alt.hs b/src/Test/QuickCheck/Classes/Alt.hs
new file mode 100644
--- /dev/null
+++ b/src/Test/QuickCheck/Classes/Alt.hs
@@ -0,0 +1,63 @@
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+
+#if HAVE_QUANTIFIED_CONSTRAINTS
+{-# LANGUAGE QuantifiedConstraints #-}
+#endif
+
+{-# OPTIONS_GHC -Wall #-}
+
+module Test.QuickCheck.Classes.Alt
+  (
+#if defined(HAVE_SEMIGROUPOIDS) && defined(HAVE_UNARY_LAWS)
+    altLaws
+#endif
+) where
+
+#if defined(HAVE_SEMIGROUPOIDS) && defined(HAVE_UNARY_LAWS)
+import Data.Functor
+import Data.Functor.Alt (Alt)
+import qualified Data.Functor.Alt as Alt
+import Test.QuickCheck hiding ((.&.))
+import Test.QuickCheck.Arbitrary (Arbitrary1(..))
+import Data.Functor.Classes (Eq1,Show1)
+import Test.QuickCheck.Property (Property)
+
+import Test.QuickCheck.Classes.Internal
+
+-- | Tests the following alt properties:
+--
+-- [/Associativity/]
+--   @(a 'Alt.<!>' b) 'Alt.<!>' c ≡ a 'Alt.<!>' (b 'Alt.<!>' c)@
+-- [/Left Distributivity/]
+--   @f '<$>' (a 'Alt.<!>' b) ≡ (f '<$>' a) 'Alt.<!>' (f '<$>' b)@
+altLaws :: forall proxy f.
+#if HAVE_QUANTIFIED_CONSTRAINTS
+  (Alt f, forall a. Eq a => Eq (f a), forall a. Show a => Show (f a), forall a. Arbitrary a => Arbitrary (f a))
+#else
+  (Alt f, Eq1 f, Show1 f, Arbitrary1 f)
+#endif
+  => proxy f -> Laws
+altLaws p = Laws "Alt"
+  [ ("Associativity", altAssociative p)
+  , ("Left Distributivity", altLeftDistributive p)
+  ]
+
+altAssociative :: forall proxy f.
+#if HAVE_QUANTIFIED_CONSTRAINTS
+  (Alt f, forall a. Eq a => Eq (f a), forall a. Show a => Show (f a), forall a. Arbitrary a => Arbitrary (f a))
+#else
+  (Alt f, Eq1 f, Show1 f, Arbitrary1 f)
+#endif
+  => proxy f -> Property
+altAssociative _ = property $ \(Apply (a :: f Integer)) (Apply (b :: f Integer)) (Apply (c :: f Integer)) -> eq1 ((a Alt.<!> b) Alt.<!> c) (a Alt.<!> (b Alt.<!> c))
+
+altLeftDistributive :: forall proxy f.
+#if HAVE_QUANTIFIED_CONSTRAINTS
+  (Alt f, forall a. Eq a => Eq (f a), forall a. Show a => Show (f a), forall a. Arbitrary a => Arbitrary (f a))
+#else
+  (Alt f, Eq1 f, Show1 f, Arbitrary1 f)
+#endif
+  => proxy f -> Property
+altLeftDistributive _ = property $ \(Apply (a :: f Integer)) (Apply (b :: f Integer)) -> eq1 (id <$> (a Alt.<!> b)) ((id <$> a) Alt.<!> (id <$> b))
+#endif
diff --git a/src/Test/QuickCheck/Classes/Apply.hs b/src/Test/QuickCheck/Classes/Apply.hs
new file mode 100644
--- /dev/null
+++ b/src/Test/QuickCheck/Classes/Apply.hs
@@ -0,0 +1,65 @@
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE RankNTypes #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+
+#if HAVE_QUANTIFIED_CONSTRAINTS
+{-# LANGUAGE QuantifiedConstraints #-}
+#endif
+
+{-# OPTIONS_GHC -Wall #-}
+
+module Test.QuickCheck.Classes.Apply
+  (
+#if defined(HAVE_SEMIGROUPOIDS) && defined(HAVE_UNARY_LAWS)
+    applyLaws
+#endif
+) where
+
+#if defined(HAVE_SEMIGROUPOIDS) && defined(HAVE_UNARY_LAWS)
+import Data.Functor
+import qualified Data.Functor.Apply as FunctorApply
+import Test.QuickCheck hiding ((.&.))
+import Test.QuickCheck.Arbitrary (Arbitrary1(..))
+import Data.Functor.Classes (Eq1,Show1)
+import Test.QuickCheck.Property (Property)
+
+import Test.QuickCheck.Classes.Internal
+
+type ApplyProp proxy f =
+#if HAVE_QUANTIFIED_CONSTRAINTS
+  (FunctorApply.Apply f, forall x. Eq x => Eq (f x), forall x. Show x => Show (f x), forall x. Arbitrary x => Arbitrary (f x)) 
+#else
+  (FunctorApply.Apply f, Eq1 f, Show1 f, Arbitrary1 f)
+#endif
+  => proxy f -> Property
+
+-- | Tests the following alt properties:
+--
+-- [/LiftF2 (1)/]
+--   @('FunctorApply.<.>') ≡ 'FunctorApply.liftF2' 'id'@
+-- [/Associativity/]
+--   @'fmap' ('.') u 'FunctorApply.<.>' v 'FunctorApply.<.>' w ≡ u 'FunctorApply.<.>' (v 'FunctorApply.<.>' w)@
+applyLaws ::
+#if HAVE_QUANTIFIED_CONSTRAINTS
+  (FunctorApply.Apply f, forall a. Eq a => Eq (f a), forall a. Show a => Show (f a), forall a. Arbitrary a => Arbitrary (f a))
+#else
+  (FunctorApply.Apply f, Eq1 f, Show1 f, Arbitrary1 f)
+#endif
+  => proxy f -> Laws
+applyLaws p = Laws "Apply"
+  [ ("LiftF2 part 1", applyLiftF2_1 p)
+  , ("Associativity", applyAssociativity p)
+  ]
+
+applyLiftF2_1 :: forall proxy f. ApplyProp proxy f
+applyLiftF2_1 _ = property $ \(Apply (f' :: f QuadraticEquation)) (Apply (x :: f Integer)) ->
+  let f = fmap runQuadraticEquation f'
+  in eq1 (FunctorApply.liftF2 id f x) (f FunctorApply.<.> x)
+
+applyAssociativity :: forall proxy f. ApplyProp proxy f
+applyAssociativity _ = property $ \(Apply (u' :: f QuadraticEquation)) (Apply (v' :: f QuadraticEquation)) (Apply (w :: f Integer)) ->
+  let u = fmap runQuadraticEquation u'
+      v = fmap runQuadraticEquation v'
+   in eq1 (fmap (.) u FunctorApply.<.> v FunctorApply.<.> w) (u FunctorApply.<.> (v FunctorApply.<.> w))
+
+#endif
diff --git a/src/Test/QuickCheck/Classes/Euclidean.hs b/src/Test/QuickCheck/Classes/Euclidean.hs
new file mode 100644
--- /dev/null
+++ b/src/Test/QuickCheck/Classes/Euclidean.hs
@@ -0,0 +1,122 @@
+-- |
+-- Module:      Test.QuickCheck.Classes.Euclidean
+-- Copyright:   (c) 2019 Andrew Lelechenko
+-- Licence:     BSD3
+--
+
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+
+{-# OPTIONS_GHC -Wall #-}
+
+#if !HAVE_SEMIRINGS
+module Test.QuickCheck.Classes.Euclidean where
+#else
+
+module Test.QuickCheck.Classes.Euclidean
+  ( gcdDomainLaws
+  , euclideanLaws
+  ) where
+
+import Prelude hiding (quotRem, quot, rem, gcd, lcm)
+import Data.Maybe
+import Data.Proxy (Proxy)
+import Data.Euclidean
+import Data.Semiring (Semiring(..))
+
+import Test.QuickCheck hiding ((.&.))
+import Test.QuickCheck.Property (Property)
+
+import Test.QuickCheck.Classes.Internal (Laws(..))
+
+-- | Test that a 'GcdDomain' instance obey several laws.
+--
+-- Check that 'divide' is an inverse of times:
+--
+-- * @y \/= 0 => (x * y) \`divide\` y == Just x@,
+-- * @y \/= 0, x \`divide\` y == Just z => x == z * y@.
+--
+-- Check that 'gcd' is a common divisor and is a multiple of any common divisor:
+--
+-- * @x \/= 0, y \/= 0 => isJust (x \`divide\` gcd x y) && isJust (y \`divide\` gcd x y)@,
+-- * @z \/= 0 => isJust (gcd (x * z) (y * z) \`divide\` z)@.
+--
+-- Check that 'lcm' is a common multiple and is a factor of any common multiple:
+--
+-- * @x \/= 0, y \/= 0 => isJust (lcm x y \`divide\` x) && isJust (lcm x y \`divide\` y)@,
+-- * @x \/= 0, y \/= 0, isJust (z \`divide\` x), isJust (z \`divide\` y) => isJust (z \`divide\` lcm x y)@.
+--
+-- Check that 'gcd' of 'coprime' numbers is a unit of the semiring (has an inverse):
+--
+-- * @y \/= 0, coprime x y => isJust (1 \`divide\` gcd x y)@.
+gcdDomainLaws :: (Eq a, GcdDomain a, Arbitrary a, Show a) => Proxy a -> Laws
+gcdDomainLaws p = Laws "GcdDomain"
+  [ ("divide1", divideLaw1 p)
+  , ("divide2", divideLaw2 p)
+  , ("gcd1", gcdLaw1 p)
+  , ("gcd2", gcdLaw2 p)
+  , ("lcm1", lcmLaw1 p)
+  , ("lcm2", lcmLaw2 p)
+  , ("coprime", coprimeLaw p)
+  ]
+
+divideLaw1 :: forall a. (Eq a, GcdDomain a, Arbitrary a, Show a) => Proxy a -> Property
+divideLaw1 _ = property $ \(x :: a) y ->
+  y /= zero ==> (x `times` y) `divide` y === Just x
+
+divideLaw2 :: forall a. (Eq a, GcdDomain a, Arbitrary a, Show a) => Proxy a -> Property
+divideLaw2 _ = property $ \(x :: a) y ->
+  y /= zero ==> maybe (property True) (\z -> x === z `times` y) (x `divide` y)
+
+gcdLaw1 :: forall a. (Eq a, GcdDomain a, Arbitrary a, Show a) => Proxy a -> Property
+gcdLaw1 _ = property $ \(x :: a) y ->
+  x /= zero || y /= zero ==> isJust (x `divide` gcd x y) .&&. isJust (y `divide` gcd x y)
+
+gcdLaw2 :: forall a. (Eq a, GcdDomain a, Arbitrary a, Show a) => Proxy a -> Property
+gcdLaw2 _ = property $ \(x :: a) y z ->
+  z /= zero ==> isJust (gcd (x `times` z) (y `times` z) `divide` z)
+
+lcmLaw1 :: forall a. (Eq a, GcdDomain a, Arbitrary a, Show a) => Proxy a -> Property
+lcmLaw1 _ = property $ \(x :: a) y ->
+  x /= zero && y /= zero ==> isJust (lcm x y `divide` x) .&&. isJust (lcm x y `divide` y)
+
+lcmLaw2 :: forall a. (Eq a, GcdDomain a, Arbitrary a, Show a) => Proxy a -> Property
+lcmLaw2 _ = property $ \(x :: a) y z ->
+  x /= zero && y /= zero ==> isNothing (z `divide` x) .||. isNothing (z `divide` y) .||. isJust (z `divide` lcm x y)
+
+coprimeLaw :: forall a. (Eq a, GcdDomain a, Arbitrary a, Show a) => Proxy a -> Property
+coprimeLaw _ = property $ \(x :: a) y ->
+  y /= zero ==> coprime x y === isJust (one `divide` gcd x y)
+
+-- | Test that a 'Euclidean' instance obey laws of a Euclidean domain.
+--
+-- * @y \/= 0, r == x \`rem\` y => r == 0 || degree r < degree y@,
+-- * @y \/= 0, (q, r) == x \`quotRem\` y => x == q * y + r@,
+-- * @y \/= 0 => x \`quot\` x y == fst (x \`quotRem\` y)@,
+-- * @y \/= 0 => x \`rem\` x y == snd (x \`quotRem\` y)@.
+euclideanLaws :: (Eq a, Euclidean a, Arbitrary a, Show a) => Proxy a -> Laws
+euclideanLaws p = Laws "Euclidean"
+  [ ("degree", degreeLaw p)
+  , ("quotRem", quotRemLaw p)
+  , ("quot", quotLaw p)
+  , ("rem", remLaw p)
+  ]
+
+degreeLaw :: forall a. (Eq a, Euclidean a, Arbitrary a, Show a) => Proxy a -> Property
+degreeLaw _ = property $ \(x :: a) y ->
+  y /= zero ==> let (_, r) = x `quotRem` y in (r === zero .||. degree r < degree y)
+
+quotRemLaw :: forall a. (Eq a, Euclidean a, Arbitrary a, Show a) => Proxy a -> Property
+quotRemLaw _ = property $ \(x :: a) y ->
+  y /= zero ==> let (q, r) = x `quotRem` y in x === (q `times` y) `plus` r
+
+quotLaw :: forall a. (Eq a, Euclidean a, Arbitrary a, Show a) => Proxy a -> Property
+quotLaw _ = property $ \(x :: a) y ->
+  y /= zero ==> quot x y === fst (quotRem x y)
+
+remLaw :: forall a. (Eq a, Euclidean a, Arbitrary a, Show a) => Proxy a -> Property
+remLaw _ = property $ \(x :: a) y ->
+  y /= zero ==> rem x y === snd (quotRem x y)
+
+#endif
diff --git a/src/Test/QuickCheck/Classes/IsList.hs b/src/Test/QuickCheck/Classes/IsList.hs
new file mode 100644
--- /dev/null
+++ b/src/Test/QuickCheck/Classes/IsList.hs
@@ -0,0 +1,8 @@
+module Test.QuickCheck.Classes.IsList
+  ( module Test.QuickCheck.Classes.Base.IsList
+  ) where
+
+-- It would be better to do this with Cabal's module reexport feature,
+-- but that would break compatibility with older GHCs.
+
+import Test.QuickCheck.Classes.Base.IsList
diff --git a/src/Test/QuickCheck/Classes/Json.hs b/src/Test/QuickCheck/Classes/Json.hs
new file mode 100644
--- /dev/null
+++ b/src/Test/QuickCheck/Classes/Json.hs
@@ -0,0 +1,68 @@
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+
+{-# OPTIONS_GHC -Wall #-}
+
+module Test.QuickCheck.Classes.Json
+  (
+#if HAVE_AESON
+    jsonLaws
+#endif  
+  ) where
+
+import Data.Proxy (Proxy)
+import Test.QuickCheck hiding ((.&.))
+import Test.QuickCheck.Property (Property(..))
+
+#if HAVE_AESON
+import Data.Aeson (FromJSON(..), ToJSON(..))
+import qualified Data.Aeson as AE
+#endif
+
+import Test.QuickCheck.Classes.Internal (Laws(..))
+
+-- | Tests the following properties:
+--
+-- [/Partial Isomorphism/]
+--   @decode . encode ≡ Just@
+-- [/Encoding Equals Value/]
+--   @decode . encode ≡ Just . toJSON@
+--
+-- Note that in the second property, the type of decode is @ByteString -> Value@,
+-- not @ByteString -> a@
+#if HAVE_AESON
+jsonLaws :: (ToJSON a, FromJSON a, Show a, Arbitrary a, Eq a) => Proxy a -> Laws
+jsonLaws p = Laws "ToJSON/FromJSON"
+  [ ("Partial Isomorphism", jsonEncodingPartialIsomorphism p)
+  , ("Encoding Equals Value", jsonEncodingEqualsValue p)
+  ]
+
+-- TODO: improve the quality of the error message if
+-- something does not pass this test.
+jsonEncodingEqualsValue :: forall a. (ToJSON a, Show a, Arbitrary a) => Proxy a -> Property
+jsonEncodingEqualsValue _ = property $ \(a :: a) ->
+  case AE.decode (AE.encode a) of
+    Nothing -> False
+    Just (v :: AE.Value) -> v == toJSON a
+
+jsonEncodingPartialIsomorphism :: forall a. (ToJSON a, FromJSON a, Show a, Eq a, Arbitrary a) => Proxy a -> Property
+jsonEncodingPartialIsomorphism _ =
+#if MIN_VERSION_QuickCheck(2,9,0)
+  again $
+#endif
+  MkProperty $
+    arbitrary >>= \(x :: a) ->
+      unProperty $
+      shrinking shrink x $ \x' ->
+        let desc1 = "Just"
+            desc2 = "Data.Aeson.decode . Data.Aeson.encode"
+            name1 = "Data.Aeson.encode a"
+            name2 = "Data.Aeson.decode (Data.Aeson.encode a)"
+            b1  = AE.encode x'
+            b2  = AE.decode (AE.encode x')
+            sb1 = show b1
+            sb2 = show b2
+            description = "  Description: " ++ desc1 ++ " == " ++ desc2
+            err = description ++ "\n" ++ unlines (map ("  " ++) (["a = " ++ show x'])) ++ "  " ++ name1 ++ " = " ++ sb1 ++ "\n  " ++ name2 ++ " = " ++ sb2
+        in counterexample err (Just x' == b2)
+#endif
diff --git a/src/Test/QuickCheck/Classes/MVector.hs b/src/Test/QuickCheck/Classes/MVector.hs
new file mode 100644
--- /dev/null
+++ b/src/Test/QuickCheck/Classes/MVector.hs
@@ -0,0 +1,354 @@
+-- |
+-- Module:      Test.QuickCheck.Classes.MVector
+-- Copyright:   (c) 2019 Andrew Lelechenko
+-- Licence:     BSD3
+--
+
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+
+{-# OPTIONS_GHC -Wall #-}
+
+#if !HAVE_VECTOR
+module Test.QuickCheck.Classes.MVector where
+#else
+
+module Test.QuickCheck.Classes.MVector
+  ( muvectorLaws
+  ) where
+
+import Control.Applicative
+import Control.Monad (when)
+import Control.Monad.ST
+import Data.Functor
+import Data.Proxy (Proxy)
+import qualified Data.Vector.Generic.Mutable as MU (basicInitialize)
+import qualified Data.Vector.Unboxed.Mutable as MU
+
+import Test.QuickCheck hiding ((.&.))
+import Test.QuickCheck.Property (Property)
+
+import Test.QuickCheck.Classes.Internal (Laws(..))
+
+-- | Test that a 'Vector.Unboxed.MVector' instance obey several laws.
+muvectorLaws :: (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Laws
+muvectorLaws p = Laws "Vector.Unboxed.MVector"
+  [ ("New-Length", newLength p)
+  , ("Replicate-Length", replicateLength p)
+  , ("Slice-Length", sliceLength p)
+  , ("Grow-Length", growLength p)
+
+  , ("Write-Read", writeRead p)
+  , ("Set-Read", setRead p)
+  , ("Sliced-Set-Read", slicedSetRead p)
+  , ("Replicate-Read", replicateRead p)
+
+  , ("Slice-Overlaps", sliceOverlaps p)
+  , ("Slice-Copy", sliceCopy p)
+  , ("Slice-Move", sliceMove p)
+
+  , ("Write-Copy-Read", writeCopyRead p)
+  , ("Write-Move-Read", writeMoveRead p)
+  , ("Write-Grow-Read", writeGrowRead p)
+  , ("Sliced-Write-Copy-Read", slicedWriteCopyRead p)
+  , ("Sliced-Write-Move-Read", slicedWriteMoveRead p)
+  , ("Sliced-Write-Grow-Read", slicedWriteGrowRead p)
+
+  , ("Write-InitializeAround-Read", writeInitializeAroundRead p)
+  , ("Write-ClearAround-Read", writeClearAroundRead p)
+  , ("Write-SetAround-Read", writeSetAroundRead p)
+  , ("Write-WriteAround-Read", writeWriteAroundRead p)
+  , ("Write-CopyAround-Read", writeCopyAroundRead p)
+  , ("Write-MoveAround-Read", writeMoveAroundRead p)
+
+  , ("Write-InitializeBetween-Read", writeInitializeBetweenRead p)
+  , ("Write-ClearBetween-Read", writeClearBetweenRead p)
+  , ("Write-SetBetween-Read", writeSetBetweenRead p)
+  , ("Write-CopyBetween-Read", writeCopyBetweenRead p)
+  , ("Write-MoveBetween-Read", writeMoveBetweenRead p)
+  ]
+
+-------------------------------------------------------------------------------
+-- Length
+
+newLength :: forall a. (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Property
+newLength _ = property $ \(NonNegative len) -> do
+  (=== len) (runST $ MU.length <$> (MU.new len :: ST s (MU.MVector s a)))
+
+replicateLength :: forall a. (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Property
+replicateLength _ = property $ \(a :: a) (NonNegative len) -> do
+  (=== len) (runST $ MU.length <$> MU.replicate len a)
+
+sliceLength :: forall a. (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Property
+sliceLength _ = property $ \(NonNegative ix) (NonNegative subLen) (Positive excess) -> do
+  (=== subLen) (runST $ MU.length . MU.slice ix subLen <$> (MU.new (ix + subLen + excess) :: ST s (MU.MVector s a)))
+
+growLength :: forall a. (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Property
+growLength _ = property $ \(Positive len) (Positive by) -> do
+  (=== len + by) $ runST $ do
+    arr <- MU.new len :: ST s (MU.MVector s a)
+    MU.length <$> MU.grow arr by
+
+-------------------------------------------------------------------------------
+-- Read
+
+writeRead :: forall a. (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Property
+writeRead _ = property $ \(a :: a) (NonNegative ix) (Positive excess) -> do
+  (=== a) $ runST $ do
+    arr <- MU.new (ix + excess)
+    MU.write arr ix a
+    MU.read arr ix
+
+setRead :: forall a. (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Property
+setRead _ = property $ \(a :: a) (NonNegative ix) (Positive excess) -> do
+  (=== a) $ runST $ do
+    arr <- MU.new (ix + excess)
+    MU.set arr a
+    MU.read arr ix
+
+slicedSetRead :: forall a. (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Property
+slicedSetRead _ = property $ \(a :: a) (NonNegative ix) (Positive excess) before after -> do
+  (=== a) $ runST $ do
+    arr <- newSlice before after (ix + excess)
+    MU.set arr a
+    MU.read arr ix
+
+replicateRead :: forall a. (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Property
+replicateRead _ = property $ \(a :: a) (NonNegative ix) (Positive excess) -> do
+  (=== a) $ runST $ do
+    arr <- MU.replicate (ix + excess) a
+    MU.read arr ix
+
+-------------------------------------------------------------------------------
+-- Overlaps
+
+sliceOverlaps :: forall a. (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Property
+sliceOverlaps _ = property $ \(NonNegative i) (NonNegative ij) (NonNegative jk) (NonNegative kl) (NonNegative lm) -> do
+  let j = i + ij
+      k = j + jk
+      l = k + kl
+      m = l + lm
+  property $ runST $ do
+    arr <- MU.new (m + 1) :: ST s (MU.MVector s a)
+    let slice1 = MU.slice i (k - i + 1) arr
+        slice2 = MU.slice j (l - j + 1) arr
+    pure $ MU.overlaps slice1 slice2
+
+sliceCopy :: forall a. (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Property
+sliceCopy _ = property $ \(a :: a) (NonNegative i) (NonNegative ix) (Positive excess) (NonNegative ij) (NonNegative jk) -> do
+  let j = i + ix + excess + ij
+      k = j + ix + excess + jk
+  runST $ do
+    arr <- MU.new k :: ST s (MU.MVector s a)
+    let src = MU.slice i (ix + excess) arr
+        dst = MU.slice j (ix + excess) arr
+    if MU.overlaps src dst then pure (property True) else do
+      MU.write src ix a
+      MU.copy dst src
+      valSrc <- MU.read src ix
+      valDst <- MU.read dst ix
+      pure (valSrc === a .&&. valDst === a)
+
+sliceMove :: forall a. (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Property
+sliceMove _ = property $ \(a :: a) (NonNegative i) (NonNegative ix) (Positive excess) (NonNegative ij) (NonNegative jk) -> do
+  let j = i + ix + excess + ij
+      k = j + ix + excess + jk
+  (=== a) $ runST $ do
+    arr <- MU.new k :: ST s (MU.MVector s a)
+    let src = MU.slice i (ix + excess) arr
+        dst = MU.slice j (ix + excess) arr
+    MU.write src ix a
+    MU.move dst src
+    MU.read dst ix
+
+-------------------------------------------------------------------------------
+-- Write + copy/move/grow
+
+writeCopyRead :: forall a. (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Property
+writeCopyRead _ = property $ \(a :: a) (NonNegative ix) (Positive excess) -> do
+  (=== a) $ runST $ do
+    src <- MU.new (ix + excess)
+    MU.write src ix a
+    dst <- MU.new (ix + excess)
+    MU.copy dst src
+    MU.clear src
+    MU.read dst ix
+
+writeMoveRead :: forall a. (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Property
+writeMoveRead _ = property $ \(a :: a) (NonNegative ix) (Positive excess) -> do
+  (=== a) $ runST $ do
+    src <- MU.new (ix + excess)
+    MU.write src ix a
+    dst <- MU.new (ix + excess)
+    MU.move dst src
+    MU.clear src
+    MU.read dst ix
+
+writeGrowRead :: forall a. (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Property
+writeGrowRead _ = property $ \(a :: a) (NonNegative ix) (Positive excess) (Positive by) -> do
+  (=== a) $ runST $ do
+    src <- MU.new (ix + excess)
+    MU.write src ix a
+    dst <- MU.grow src by
+    MU.clear src
+    MU.read dst ix
+
+slicedWriteCopyRead :: forall a. (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Property
+slicedWriteCopyRead _ = property $ \(a :: a) (NonNegative ix) (Positive excess) beforeSrc afterSrc beforeDst afterDst -> do
+  (=== a) $ runST $ do
+    src <- newSlice beforeSrc afterSrc (ix + excess)
+    MU.write src ix a
+    dst <- newSlice beforeDst afterDst (ix + excess)
+    MU.copy dst src
+    MU.clear src
+    MU.read dst ix
+
+slicedWriteMoveRead :: forall a. (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Property
+slicedWriteMoveRead _ = property $ \(a :: a) (NonNegative ix) (Positive excess) beforeSrc afterSrc beforeDst afterDst -> do
+  (=== a) $ runST $ do
+    src <- newSlice beforeSrc afterSrc (ix + excess)
+    MU.write src ix a
+    dst <- newSlice beforeDst afterDst (ix + excess)
+    MU.move dst src
+    MU.clear src
+    MU.read dst ix
+
+slicedWriteGrowRead :: forall a. (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Property
+slicedWriteGrowRead _ = property $ \(a :: a) (NonNegative ix) (Positive excess) (Positive by) beforeSrc afterSrc -> do
+  (=== a) $ runST $ do
+    src <- newSlice beforeSrc afterSrc (ix + excess)
+    MU.write src ix a
+    dst <- MU.grow src by
+    MU.clear src
+    MU.read dst ix
+
+-------------------------------------------------------------------------------
+-- Write + overwrite around
+
+writeInitializeAroundRead :: forall a. (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Property
+writeInitializeAroundRead _ = property $ \(a :: a) (NonNegative ix) (Positive excess) -> do
+  (=== a) $ runST $ do
+    arr <- MU.new (ix + excess)
+    MU.write arr ix a
+    when (ix > 0) $
+      MU.basicInitialize (MU.slice 0 ix arr)
+    when (excess > 1) $
+      MU.basicInitialize (MU.slice (ix + 1) (excess - 1) arr)
+    MU.read arr ix
+
+writeClearAroundRead :: forall a. (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Property
+writeClearAroundRead _ = property $ \(a :: a) (NonNegative ix) (Positive excess) -> do
+  (=== a) $ runST $ do
+    arr <- MU.new (ix + excess)
+    MU.write arr ix a
+    when (ix > 0) $
+      MU.clear (MU.slice 0 ix arr)
+    when (excess > 1) $
+      MU.clear (MU.slice (ix + 1) (excess - 1) arr)
+    MU.read arr ix
+
+writeSetAroundRead :: forall a. (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Property
+writeSetAroundRead _ = property $ \(a :: a) (b :: a) (NonNegative ix) (Positive excess) -> do
+  (=== a) $ runST $ do
+    arr <- MU.new (ix + excess)
+    MU.write arr ix a
+    when (ix > 0) $
+      MU.set (MU.slice 0 ix arr) b
+    when (excess > 1) $
+      MU.set (MU.slice (ix + 1) (excess - 1) arr) b
+    MU.read arr ix
+
+writeWriteAroundRead :: forall a. (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Property
+writeWriteAroundRead _ = property $ \(a :: a) (b :: a) (NonNegative ix) (Positive excess) -> do
+  (=== a) $ runST $ do
+    arr <- MU.new (ix + excess)
+    MU.write arr ix a
+    when (ix > 0) $
+      MU.write arr (ix - 1) b
+    when (excess > 1) $
+      MU.write arr (ix + 1) b
+    MU.read arr ix
+
+writeCopyAroundRead :: forall a. (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Property
+writeCopyAroundRead _ = property $ \(a :: a) (NonNegative ix) (Positive excess) -> do
+  (=== a) $ runST $ do
+    src <- MU.new (ix + excess)
+    dst <- MU.new (ix + excess)
+    MU.write dst ix a
+    when (ix > 0) $
+      MU.copy (MU.slice 0 ix dst) (MU.slice 0 ix src)
+    when (excess > 1) $
+      MU.copy (MU.slice (ix + 1) (excess - 1) dst) (MU.slice (ix + 1) (excess - 1) src)
+    MU.read dst ix
+
+writeMoveAroundRead :: forall a. (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Property
+writeMoveAroundRead _ = property $ \(a :: a) (NonNegative ix) (Positive excess) -> do
+  (=== a) $ runST $ do
+    src <- MU.new (ix + excess)
+    dst <- MU.new (ix + excess)
+    MU.write dst ix a
+    when (ix > 0) $
+      MU.move (MU.slice 0 ix dst) (MU.slice 0 ix src)
+    when (excess > 1) $
+      MU.move (MU.slice (ix + 1) (excess - 1) dst) (MU.slice (ix + 1) (excess - 1) src)
+    MU.read dst ix
+
+-------------------------------------------------------------------------------
+-- Two writes + overwrite in between
+
+writeInitializeBetweenRead :: forall a. (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Property
+writeInitializeBetweenRead _ = property $ \(a :: a) (b :: a) (NonNegative ix) (Positive dix) (Positive excess) -> do
+  (=== (a, b)) $ runST $ do
+    arr <- MU.new (ix + dix + excess)
+    MU.write arr ix a
+    MU.write arr (ix + dix) b
+    MU.basicInitialize (MU.slice (ix + 1) (dix - 1) arr)
+    (,) <$> MU.read arr ix <*> MU.read arr (ix + dix)
+
+writeClearBetweenRead :: forall a. (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Property
+writeClearBetweenRead _ = property $ \(a :: a) (b :: a) (NonNegative ix) (Positive dix) (Positive excess) -> do
+  (=== (a, b)) $ runST $ do
+    arr <- MU.new (ix + dix + excess)
+    MU.write arr ix a
+    MU.write arr (ix + dix) b
+    MU.clear (MU.slice (ix + 1) (dix - 1) arr)
+    (,) <$> MU.read arr ix <*> MU.read arr (ix + dix)
+
+writeSetBetweenRead :: forall a. (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Property
+writeSetBetweenRead _ = property $ \(a :: a) (b :: a) (c :: a) (NonNegative ix) (Positive dix) (Positive excess) -> do
+  (=== (a, b)) $ runST $ do
+    arr <- MU.new (ix + dix + excess)
+    MU.write arr ix a
+    MU.write arr (ix + dix) b
+    MU.set (MU.slice (ix + 1) (dix - 1) arr) c
+    (,) <$> MU.read arr ix <*> MU.read arr (ix + dix)
+
+writeCopyBetweenRead :: forall a. (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Property
+writeCopyBetweenRead _ = property $ \(a :: a) (b :: a) (NonNegative ix) (Positive dix) (Positive excess) -> do
+  (=== (a, b)) $ runST $ do
+    src <- MU.new (ix + dix + excess)
+    dst <- MU.new (ix + dix + excess)
+    MU.write dst ix a
+    MU.write dst (ix + dix) b
+    MU.copy (MU.slice (ix + 1) (dix - 1) dst) (MU.slice (ix + 1) (dix - 1) src)
+    (,) <$> MU.read dst ix <*> MU.read dst (ix + dix)
+
+writeMoveBetweenRead :: forall a. (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Property
+writeMoveBetweenRead _ = property $ \(a :: a) (b :: a) (NonNegative ix) (Positive dix) (Positive excess) -> do
+  (=== (a, b)) $ runST $ do
+    src <- MU.new (ix + dix + excess)
+    dst <- MU.new (ix + dix + excess)
+    MU.write dst ix a
+    MU.write dst (ix + dix) b
+    MU.move (MU.slice (ix + 1) (dix - 1) dst) (MU.slice (ix + 1) (dix - 1) src)
+    (,) <$> MU.read dst ix <*> MU.read dst (ix + dix)
+
+-------------------------------------------------------------------------------
+-- Utils
+
+newSlice :: MU.Unbox a => NonNegative Int -> NonNegative Int -> Int -> ST s (MU.MVector s a)
+newSlice (NonNegative before) (NonNegative after) len = do
+  arr <- MU.new (before + len + after)
+  pure $ MU.slice before len arr
+
+#endif
diff --git a/src/Test/QuickCheck/Classes/Plus.hs b/src/Test/QuickCheck/Classes/Plus.hs
new file mode 100644
--- /dev/null
+++ b/src/Test/QuickCheck/Classes/Plus.hs
@@ -0,0 +1,93 @@
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+
+#if HAVE_QUANTIFIED_CONSTRAINTS
+{-# LANGUAGE QuantifiedConstraints #-}
+#endif
+
+{-# OPTIONS_GHC -Wall #-}
+
+module Test.QuickCheck.Classes.Plus
+  (
+#if defined(HAVE_SEMIGROUPOIDS) && defined(HAVE_UNARY_LAWS)
+    plusLaws
+  , extendedPlusLaws
+#endif
+  ) where
+
+#if defined(HAVE_SEMIGROUPOIDS) && defined(HAVE_UNARY_LAWS)
+import Data.Functor
+import Data.Functor.Alt (Alt)
+import Data.Functor.Plus (Plus)
+import qualified Data.Functor.Alt as Alt
+import qualified Data.Functor.Plus as Plus
+
+import Test.QuickCheck hiding ((.&.))
+import Test.QuickCheck.Arbitrary (Arbitrary1(..))
+import Data.Functor.Classes (Eq1,Show1)
+import qualified Control.Applicative as Alternative
+import Test.QuickCheck.Property (Property)
+
+import Test.QuickCheck.Classes.Internal
+
+-- | Tests the following alt properties:
+--
+-- [/Left Identity/]
+--   @'Plus.zero' 'Alt.<!>' m ≡ m@
+-- [/Right Identity/]
+--   @m 'Alt.<!>' 'Plus.zero' ≡ m@
+plusLaws :: forall proxy f.
+#if HAVE_QUANTIFIED_CONSTRAINTS
+  (Plus f, forall a. Eq a => Eq (f a), forall a. Show a => Show (f a), forall a. Arbitrary a => Arbitrary (f a))
+#else
+  (Plus f, Eq1 f, Show1 f, Arbitrary1 f)
+#endif
+  => proxy f -> Laws
+plusLaws p = Laws "Plus"
+  [ ("Left Identity", plusLeftIdentity p)
+  , ("Right Identity", plusRightIdentity p)
+  ]
+
+-- | Tests everything from 'altLaws', plus the following:
+--
+-- [/Congruency/]
+--   @'Plus.zero' ≡ 'Alternative.empty'@
+extendedPlusLaws :: forall proxy f.
+#if HAVE_QUANTIFIED_CONSTRAINTS
+  (Plus f, Alternative.Alternative f, forall a. Eq a => Eq (f a), forall a. Show a => Show (f a), forall a. Arbitrary a => Arbitrary (f a))
+#else
+  (Plus f, Alternative.Alternative f, Eq1 f, Show1 f, Arbitrary1 f)
+#endif
+  => proxy f -> Laws
+extendedPlusLaws p = Laws "Plus extended to Alternative" $ lawsProperties (plusLaws p) ++
+  [ ("Congruency", extendedPlusLaw p)
+  ]
+
+extendedPlusLaw :: forall proxy f.
+#if HAVE_QUANTIFIED_CONSTRAINTS
+  (Plus f, Alternative.Alternative f, forall a. Eq a => Eq (f a), forall a. Show a => Show (f a), forall a. Arbitrary a => Arbitrary (f a))
+#else
+  (Plus f, Alternative.Alternative f, Eq1 f, Show1 f, Arbitrary1 f)
+#endif
+  => proxy f -> Property
+extendedPlusLaw _ = property $ eq1 (Plus.zero :: f Integer) (Alternative.empty :: f Integer)
+
+plusLeftIdentity :: forall proxy f.
+#if HAVE_QUANTIFIED_CONSTRAINTS
+  (Plus f, forall a. Eq a => Eq (f a), forall a. Show a => Show (f a), forall a. Arbitrary a => Arbitrary (f a))
+#else
+  (Plus f, Eq1 f, Show1 f, Arbitrary1 f)
+#endif
+  => proxy f -> Property
+plusLeftIdentity _ = property $ \(Apply (m :: f Integer)) -> eq1 (Plus.zero Alt.<!> m) m
+
+plusRightIdentity :: forall proxy f.
+#if HAVE_QUANTIFIED_CONSTRAINTS
+  (Plus f, forall a. Eq a => Eq (f a), forall a. Show a => Show (f a), forall a. Arbitrary a => Arbitrary (f a))
+#else
+  (Plus f, Eq1 f, Show1 f, Arbitrary1 f)
+#endif
+  => proxy f -> Property
+plusRightIdentity _ = property $ \(Apply (m :: f Integer)) -> eq1 (m Alt.<!> Plus.zero) m
+
+#endif
diff --git a/src/Test/QuickCheck/Classes/Prim.hs b/src/Test/QuickCheck/Classes/Prim.hs
new file mode 100644
--- /dev/null
+++ b/src/Test/QuickCheck/Classes/Prim.hs
@@ -0,0 +1,390 @@
+{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE MagicHash #-}
+{-# LANGUAGE PackageImports #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE UnboxedTuples #-}
+
+{-# OPTIONS_GHC -Wall #-}
+
+module Test.QuickCheck.Classes.Prim
+  ( primLaws
+  ) where
+
+import Control.Applicative
+import Control.Monad.Primitive (PrimMonad, PrimState,primitive,primitive_)
+import Control.Monad.ST
+import Data.Proxy (Proxy)
+import Data.Primitive.ByteArray
+import Data.Primitive.Types (Prim(..))
+import "primitive-addr" Data.Primitive.Addr
+import Foreign.Marshal.Alloc
+import GHC.Exts
+  (State#,Int#,Addr#,Int(I#),(*#),(+#),(<#),newByteArray#,unsafeFreezeByteArray#,
+   copyMutableByteArray#,copyByteArray#,quotInt#,sizeofByteArray#)
+
+#if MIN_VERSION_base(4,7,0)
+import GHC.Exts (IsList(fromList,toList,fromListN),Item,
+  copyByteArrayToAddr#,copyAddrToByteArray#)
+#endif
+
+import GHC.Ptr (Ptr(..))
+import System.IO.Unsafe
+import Test.QuickCheck hiding ((.&.))
+import Test.QuickCheck.Property (Property)
+
+import qualified Data.List as L
+import qualified Data.Primitive as P
+
+import Test.QuickCheck.Classes.Internal (Laws(..),isTrue#)
+
+-- | Test that a 'Prim' instance obey the several laws.
+primLaws :: (Prim a, Eq a, Arbitrary a, Show a) => Proxy a -> Laws
+primLaws p = Laws "Prim"
+  [ ("ByteArray Put-Get (you get back what you put in)", primPutGetByteArray p)
+  , ("ByteArray Get-Put (putting back what you got out has no effect)", primGetPutByteArray p)
+  , ("ByteArray Put-Put (putting twice is same as putting once)", primPutPutByteArray p)
+  , ("ByteArray Set Range", primSetByteArray p)
+#if MIN_VERSION_base(4,7,0)
+  , ("ByteArray List Conversion Roundtrips", primListByteArray p)
+#endif
+  , ("Addr Put-Get (you get back what you put in)", primPutGetAddr p)
+  , ("Addr Get-Put (putting back what you got out has no effect)", primGetPutAddr p)
+  , ("Addr Set Range", primSetOffAddr p)
+  , ("Addr List Conversion Roundtrips", primListAddr p)
+  ]
+
+primListAddr :: forall a. (Prim a, Eq a, Arbitrary a, Show a) => Proxy a -> Property
+primListAddr _ = property $ \(as :: [a]) -> unsafePerformIO $ do
+  let len = L.length as
+  ptr@(Ptr addr#) :: Ptr a <- mallocBytes (len * P.sizeOf (undefined :: a))
+  let addr = Addr addr#
+  let go :: Int -> [a] -> IO ()
+      go !ix xs = case xs of
+        [] -> return ()
+        (x : xsNext) -> do
+          writeOffAddr addr ix x
+          go (ix + 1) xsNext
+  go 0 as
+  let rebuild :: Int -> IO [a]
+      rebuild !ix = if ix < len
+        then (:) <$> readOffAddr addr ix <*> rebuild (ix + 1)
+        else return []
+  asNew <- rebuild 0
+  free ptr
+  return (as == asNew)
+
+primPutGetByteArray :: forall a. (Prim a, Eq a, Arbitrary a, Show a) => Proxy a -> Property
+primPutGetByteArray _ = property $ \(a :: a) len -> (len > 0) ==> do
+  ix <- choose (0,len - 1)
+  return $ runST $ do
+    arr <- newPrimArray len
+    writePrimArray arr ix a
+    a' <- readPrimArray arr ix
+    return (a == a')
+
+primGetPutByteArray :: forall a. (Prim a, Eq a, Arbitrary a, Show a) => Proxy a -> Property
+primGetPutByteArray _ = property $ \(as :: [a]) -> (not (L.null as)) ==> do
+  let arr1 = primArrayFromList as :: PrimArray a
+      len = L.length as
+  ix <- choose (0,len - 1)
+  arr2 <- return $ runST $ do
+    marr <- newPrimArray len
+    copyPrimArray marr 0 arr1 0 len
+    a <- readPrimArray marr ix
+    writePrimArray marr ix a
+    unsafeFreezePrimArray marr
+  return (arr1 == arr2)
+
+primPutPutByteArray :: forall a. (Prim a, Eq a, Arbitrary a, Show a) => Proxy a -> Property
+primPutPutByteArray _ = property $ \(a :: a) (as :: [a]) -> (not (L.null as)) ==> do
+  let arr1 = primArrayFromList as :: PrimArray a
+      len = L.length as
+  ix <- choose (0,len - 1)
+  (arr2,arr3) <- return $ runST $ do
+    marr2 <- newPrimArray len
+    copyPrimArray marr2 0 arr1 0 len
+    writePrimArray marr2 ix a
+    marr3 <- newPrimArray len
+    copyMutablePrimArray marr3 0 marr2 0 len
+    arr2 <- unsafeFreezePrimArray marr2
+    writePrimArray marr3 ix a
+    arr3 <- unsafeFreezePrimArray marr3
+    return (arr2,arr3)
+  return (arr2 == arr3)
+
+primPutGetAddr :: forall a. (Prim a, Eq a, Arbitrary a, Show a) => Proxy a -> Property
+primPutGetAddr _ = property $ \(a :: a) len -> (len > 0) ==> do
+  ix <- choose (0,len - 1)
+  return $ unsafePerformIO $ do
+    ptr@(Ptr addr#) :: Ptr a <- mallocBytes (len * P.sizeOf (undefined :: a))
+    let addr = Addr addr#
+    writeOffAddr addr ix a
+    a' <- readOffAddr addr ix
+    free ptr
+    return (a == a')
+
+primGetPutAddr :: forall a. (Prim a, Eq a, Arbitrary a, Show a) => Proxy a -> Property
+primGetPutAddr _ = property $ \(as :: [a]) -> (not (L.null as)) ==> do
+  let arr1 = primArrayFromList as :: PrimArray a
+      len = L.length as
+  ix <- choose (0,len - 1)
+  arr2 <- return $ unsafePerformIO $ do
+    ptr@(Ptr addr#) :: Ptr a <- mallocBytes (len * P.sizeOf (undefined :: a))
+    let addr = Addr addr#
+    copyPrimArrayToPtr ptr arr1 0 len
+    a :: a <- readOffAddr addr ix
+    writeOffAddr addr ix a
+    marr <- newPrimArray len
+    copyPtrToMutablePrimArray marr 0 ptr len
+    free ptr
+    unsafeFreezePrimArray marr
+  return (arr1 == arr2)
+
+primSetByteArray :: forall a. (Prim a, Eq a, Arbitrary a, Show a) => Proxy a -> Property
+primSetByteArray _ = property $ \(as :: [a]) (z :: a) -> do
+  let arr1 = primArrayFromList as :: PrimArray a
+      len = L.length as
+  x <- choose (0,len)
+  y <- choose (0,len)
+  let lo = min x y
+      hi = max x y
+  return $ runST $ do
+    marr2 <- newPrimArray len
+    copyPrimArray marr2 0 arr1 0 len
+    marr3 <- newPrimArray len
+    copyPrimArray marr3 0 arr1 0 len
+    setPrimArray marr2 lo (hi - lo) z
+    internalDefaultSetPrimArray marr3 lo (hi - lo) z
+    arr2 <- unsafeFreezePrimArray marr2
+    arr3 <- unsafeFreezePrimArray marr3
+    return (arr2 == arr3)
+
+primSetOffAddr :: forall a. (Prim a, Eq a, Arbitrary a, Show a) => Proxy a -> Property
+primSetOffAddr _ = property $ \(as :: [a]) (z :: a) -> do
+  let arr1 = primArrayFromList as :: PrimArray a
+      len = L.length as
+  x <- choose (0,len)
+  y <- choose (0,len)
+  let lo = min x y
+      hi = max x y
+  return $ unsafePerformIO $ do
+    ptrA@(Ptr addrA#) :: Ptr a <- mallocBytes (len * P.sizeOf (undefined :: a))
+    let addrA = Addr addrA#
+    copyPrimArrayToPtr ptrA arr1 0 len
+    ptrB@(Ptr addrB#) :: Ptr a <- mallocBytes (len * P.sizeOf (undefined :: a))
+    let addrB = Addr addrB#
+    copyPrimArrayToPtr ptrB arr1 0 len
+    setOffAddr addrA lo (hi - lo) z
+    internalDefaultSetOffAddr addrB lo (hi - lo) z
+    marrA <- newPrimArray len
+    copyPtrToMutablePrimArray marrA 0 ptrA len
+    free ptrA
+    marrB <- newPrimArray len
+    copyPtrToMutablePrimArray marrB 0 ptrB len
+    free ptrB
+    arrA <- unsafeFreezePrimArray marrA
+    arrB <- unsafeFreezePrimArray marrB
+    return (arrA == arrB)
+
+-- byte array with phantom variable that specifies element type
+data PrimArray a = PrimArray ByteArray#
+data MutablePrimArray s a = MutablePrimArray (MutableByteArray# s)
+
+instance (Eq a, Prim a) => Eq (PrimArray a) where
+  a1 == a2 = sizeofPrimArray a1 == sizeofPrimArray a2 && loop (sizeofPrimArray a1 - 1)
+    where
+    loop !i | i < 0 = True
+            | otherwise = indexPrimArray a1 i == indexPrimArray a2 i && loop (i-1)
+
+#if MIN_VERSION_base(4,7,0)
+instance Prim a => IsList (PrimArray a) where
+  type Item (PrimArray a) = a
+  fromList = primArrayFromList
+  fromListN = primArrayFromListN
+  toList = primArrayToList
+#endif
+
+indexPrimArray :: forall a. Prim a => PrimArray a -> Int -> a
+indexPrimArray (PrimArray arr#) (I# i#) = indexByteArray# arr# i#
+
+sizeofPrimArray :: forall a. Prim a => PrimArray a -> Int
+sizeofPrimArray (PrimArray arr#) = I# (quotInt# (sizeofByteArray# arr#) (P.sizeOf# (undefined :: a)))
+
+newPrimArray :: forall m a. (PrimMonad m, Prim a) => Int -> m (MutablePrimArray (PrimState m) a)
+newPrimArray (I# n#)
+  = primitive (\s# ->
+      case newByteArray# (n# *# sizeOf# (undefined :: a)) s# of
+        (# s'#, arr# #) -> (# s'#, MutablePrimArray arr# #)
+    )
+
+readPrimArray :: (Prim a, PrimMonad m) => MutablePrimArray (PrimState m) a -> Int -> m a
+readPrimArray (MutablePrimArray arr#) (I# i#)
+  = primitive (readByteArray# arr# i#)
+
+writePrimArray ::
+     (Prim a, PrimMonad m)
+  => MutablePrimArray (PrimState m) a
+  -> Int
+  -> a
+  -> m ()
+writePrimArray (MutablePrimArray arr#) (I# i#) x
+  = primitive_ (writeByteArray# arr# i# x)
+
+unsafeFreezePrimArray
+  :: PrimMonad m => MutablePrimArray (PrimState m) a -> m (PrimArray a)
+unsafeFreezePrimArray (MutablePrimArray arr#)
+  = primitive (\s# -> case unsafeFreezeByteArray# arr# s# of
+                        (# s'#, arr'# #) -> (# s'#, PrimArray arr'# #))
+
+#if !MIN_VERSION_base(4,7,0)
+ptrToAddr :: Ptr a -> Addr
+ptrToAddr (Ptr x) = Addr x
+
+generateM_ :: Monad m => Int -> (Int -> m a) -> m ()
+generateM_ n f = go 0 where
+  go !ix = if ix < n
+    then f ix >> go (ix + 1)
+    else return ()
+#endif
+
+copyPrimArrayToPtr :: forall m a. (PrimMonad m, Prim a)
+  => Ptr a       -- ^ destination pointer
+  -> PrimArray a -- ^ source array
+  -> Int         -- ^ offset into source array
+  -> Int         -- ^ number of prims to copy
+  -> m ()
+#if MIN_VERSION_base(4,7,0)
+copyPrimArrayToPtr (Ptr addr#) (PrimArray ba#) (I# soff#) (I# n#) =
+  primitive (\ s# ->
+      let s'# = copyByteArrayToAddr# ba# (soff# *# siz#) addr# (n# *# siz#) s#
+      in (# s'#, () #))
+  where siz# = sizeOf# (undefined :: a)
+#else
+copyPrimArrayToPtr addr ba soff n =
+  generateM_ n $ \ix -> writeOffAddr (ptrToAddr addr) ix (indexPrimArray ba (ix + soff))
+#endif
+
+copyPtrToMutablePrimArray :: forall m a. (PrimMonad m, Prim a)
+  => MutablePrimArray (PrimState m) a
+  -> Int
+  -> Ptr a
+  -> Int
+  -> m ()
+#if MIN_VERSION_base(4,7,0)
+copyPtrToMutablePrimArray (MutablePrimArray ba#) (I# doff#) (Ptr addr#) (I# n#) =
+  primitive (\ s# ->
+      let s'# = copyAddrToByteArray# addr# ba# (doff# *# siz#) (n# *# siz#) s#
+      in (# s'#, () #))
+  where siz# = sizeOf# (undefined :: a)
+#else
+copyPtrToMutablePrimArray ba doff addr n =
+  generateM_ n $ \ix -> do
+    x <- readOffAddr (ptrToAddr addr) ix
+    writePrimArray ba (doff + ix) x
+#endif
+
+copyMutablePrimArray :: forall m a.
+     (PrimMonad m, Prim a)
+  => MutablePrimArray (PrimState m) a -- ^ destination array
+  -> Int -- ^ offset into destination array
+  -> MutablePrimArray (PrimState m) a -- ^ source array
+  -> Int -- ^ offset into source array
+  -> Int -- ^ number of bytes to copy
+  -> m ()
+copyMutablePrimArray (MutablePrimArray dst#) (I# doff#) (MutablePrimArray src#) (I# soff#) (I# n#)
+  = primitive_ (copyMutableByteArray#
+      src#
+      (soff# *# (sizeOf# (undefined :: a)))
+      dst#
+      (doff# *# (sizeOf# (undefined :: a)))
+      (n# *# (sizeOf# (undefined :: a)))
+    )
+
+copyPrimArray :: forall m a.
+     (PrimMonad m, Prim a)
+  => MutablePrimArray (PrimState m) a -- ^ destination array
+  -> Int -- ^ offset into destination array
+  -> PrimArray a -- ^ source array
+  -> Int -- ^ offset into source array
+  -> Int -- ^ number of bytes to copy
+  -> m ()
+copyPrimArray (MutablePrimArray dst#) (I# doff#) (PrimArray src#) (I# soff#) (I# n#)
+  = primitive_ (copyByteArray#
+      src#
+      (soff# *# (sizeOf# (undefined :: a)))
+      dst#
+      (doff# *# (sizeOf# (undefined :: a)))
+      (n# *# (sizeOf# (undefined :: a)))
+    )
+
+setPrimArray
+  :: (Prim a, PrimMonad m)
+  => MutablePrimArray (PrimState m) a -- ^ array to fill
+  -> Int -- ^ offset into array
+  -> Int -- ^ number of values to fill
+  -> a -- ^ value to fill with
+  -> m ()
+setPrimArray (MutablePrimArray dst#) (I# doff#) (I# sz#) x
+  = primitive_ (P.setByteArray# dst# doff# sz# x)
+
+primArrayFromList :: Prim a => [a] -> PrimArray a
+primArrayFromList xs = primArrayFromListN (L.length xs) xs
+
+primArrayFromListN :: forall a. Prim a => Int -> [a] -> PrimArray a
+primArrayFromListN len vs = runST run where
+  run :: forall s. ST s (PrimArray a)
+  run = do
+    arr <- newPrimArray len
+    let go :: [a] -> Int -> ST s ()
+        go !xs !ix = case xs of
+          [] -> return ()
+          a : as -> do
+            writePrimArray arr ix a
+            go as (ix + 1)
+    go vs 0
+    unsafeFreezePrimArray arr
+
+primArrayToList :: forall a. Prim a => PrimArray a -> [a]
+primArrayToList arr = go 0 where
+  !len = sizeofPrimArray arr
+  go :: Int -> [a]
+  go !ix = if ix < len
+    then indexPrimArray arr ix : go (ix + 1)
+    else []
+
+#if MIN_VERSION_base(4,7,0)
+primListByteArray :: forall a. (Prim a, Eq a, Arbitrary a, Show a) => Proxy a -> Property
+primListByteArray _ = property $ \(as :: [a]) ->
+  as == toList (fromList as :: PrimArray a)
+#endif
+
+setOffAddr :: forall a. Prim a => Addr -> Int -> Int -> a -> IO ()
+setOffAddr addr ix len a = setAddr (plusAddr addr (P.sizeOf (undefined :: a) * ix)) len a
+
+internalDefaultSetPrimArray :: Prim a
+  => MutablePrimArray s a -> Int -> Int -> a -> ST s ()
+internalDefaultSetPrimArray (MutablePrimArray arr) (I# i) (I# len) ident =
+  primitive_ (internalDefaultSetByteArray# arr i len ident)
+
+internalDefaultSetByteArray# :: Prim a
+  => MutableByteArray# s -> Int# -> Int# -> a -> State# s -> State# s
+internalDefaultSetByteArray# arr# i# len# ident = go 0#
+  where
+  go ix# s0 = if isTrue# (ix# <# len#)
+    then case writeByteArray# arr# (i# +# ix#) ident s0 of
+      s1 -> go (ix# +# 1#) s1
+    else s0
+
+internalDefaultSetOffAddr :: Prim a => Addr -> Int -> Int -> a -> IO ()
+internalDefaultSetOffAddr (Addr addr) (I# ix) (I# len) a = primitive_
+  (internalDefaultSetOffAddr# addr ix len a)
+
+internalDefaultSetOffAddr# :: Prim a => Addr# -> Int# -> Int# -> a -> State# s -> State# s
+internalDefaultSetOffAddr# addr# i# len# ident = go 0#
+  where
+  go ix# s0 = if isTrue# (ix# <# len#)
+    then case writeOffAddr# addr# (i# +# ix#) ident s0 of
+      s1 -> go (ix# +# 1#) s1
+    else s0
diff --git a/src/Test/QuickCheck/Classes/Ring.hs b/src/Test/QuickCheck/Classes/Ring.hs
new file mode 100644
--- /dev/null
+++ b/src/Test/QuickCheck/Classes/Ring.hs
@@ -0,0 +1,43 @@
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+
+{-# OPTIONS_GHC -Wall #-}
+
+module Test.QuickCheck.Classes.Ring
+  ( 
+#if HAVE_SEMIRINGS
+    ringLaws
+#endif
+  ) where
+
+#if HAVE_SEMIRINGS
+import Data.Semiring
+import Prelude hiding (Num(..))
+#endif
+
+import Data.Proxy (Proxy)
+import Test.QuickCheck hiding ((.&.))
+import Test.QuickCheck.Property (Property)
+
+import Test.QuickCheck.Classes.Internal (Laws(..), myForAllShrink)
+
+#if HAVE_SEMIRINGS
+-- | Tests the following properties:
+--
+-- [/Additive Inverse/]
+--   @'negate' a '+' a ≡ 0@
+--
+-- Note that this does not test any of the laws tested by 'Test.QuickCheck.Classes.Semiring.semiringLaws'.
+ringLaws :: (Ring a, Eq a, Arbitrary a, Show a) => Proxy a -> Laws
+ringLaws p = Laws "Ring"
+  [ ("Additive Inverse", ringAdditiveInverse p)
+  ]
+
+ringAdditiveInverse :: forall a. (Ring a, Eq a, Arbitrary a, Show a) => Proxy a -> Property
+ringAdditiveInverse _ = myForAllShrink True (const True)
+  (\(a :: a) -> ["a = " ++ show a])
+  "negate a + a"
+  (\a -> negate a + a)
+  "0"
+  (const zero)
+#endif
diff --git a/src/Test/QuickCheck/Classes/Semigroupoid.hs b/src/Test/QuickCheck/Classes/Semigroupoid.hs
new file mode 100644
--- /dev/null
+++ b/src/Test/QuickCheck/Classes/Semigroupoid.hs
@@ -0,0 +1,81 @@
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+
+#if HAVE_QUANTIFIED_CONSTRAINTS
+{-# LANGUAGE QuantifiedConstraints #-}
+#endif
+
+{-# OPTIONS_GHC -Wall #-}
+
+module Test.QuickCheck.Classes.Semigroupoid
+  (
+#if defined(HAVE_SEMIGROUPOIDS) && defined(HAVE_BINARY_LAWS)
+    semigroupoidLaws
+  , commutativeSemigroupoidLaws
+#endif
+  ) where
+
+#if defined(HAVE_SEMIGROUPOIDS) && defined(HAVE_BINARY_LAWS)
+import Prelude hiding (id, (.))
+import Data.Semigroupoid (Semigroupoid(..))
+import Test.QuickCheck hiding ((.&.))
+import Data.Functor.Classes (Eq2,Show2)
+import Test.QuickCheck.Property (Property)
+
+import Test.QuickCheck.Classes.Internal
+
+-- | Tests the following 'Semigroupoid' properties:
+--
+-- [/Associativity/]
+--   @f `'o'` (g `'o'` h) ≡ (f `'o'` g) `'o'` h@
+--
+-- /Note/: This property test is only available when this package is built with
+-- @base-4.9+@ or @transformers-0.5+@.
+semigroupoidLaws :: forall proxy s.
+#if HAVE_QUANTIFIED_CONSTRAINTS
+  (Semigroupoid s, forall a b. (Eq a, Eq b) => Eq (s a b), forall a b. (Show a, Show b) => Show (s a b), forall a b. (Arbitrary a, Arbitrary b) => Arbitrary (s a b))
+#else
+  (Semigroupoid s, Eq2 s, Show2 s, Arbitrary2 s)
+#endif
+  => proxy s -> Laws
+semigroupoidLaws p = Laws "Semigroupoid"
+  [ ("Associativity", semigroupoidAssociativity p)
+  ]
+
+-- | Tests everything from 'semigroupoidLaws' plus the following:
+--
+-- [/Commutative/]
+--   @f `'o'` g ≡ g `'o'` f@
+--
+-- /Note/: This property test is only available when this package is built with
+-- @base-4.9+@ or @transformers-0.5+@.
+commutativeSemigroupoidLaws :: forall proxy s.
+#if HAVE_QUANTIFIED_CONSTRAINTS
+  (Semigroupoid s, forall a b. (Eq a, Eq b) => Eq (s a b), forall a b. (Show a, Show b) => Show (s a b), forall a b. (Arbitrary a, Arbitrary b) => Arbitrary (s a b))
+#else
+  (Semigroupoid s, Eq2 s, Show2 s, Arbitrary2 s)
+#endif
+  => proxy s -> Laws
+commutativeSemigroupoidLaws p = Laws "Commutative Semigroupoid" $ lawsProperties (semigroupoidLaws p) ++
+  [ ("Commutative", semigroupoidCommutativity p)
+  ]
+
+semigroupoidAssociativity :: forall proxy s.
+#if HAVE_QUANTIFIED_CONSTRAINTS
+  (Semigroupoid s, forall a b. (Eq a, Eq b) => Eq (s a b), forall a b. (Show a, Show b) => Show (s a b), forall a b. (Arbitrary a, Arbitrary b) => Arbitrary (s a b))
+#else
+  (Semigroupoid s, Eq2 s, Show2 s, Arbitrary2 s)
+#endif
+  => proxy s -> Property
+semigroupoidAssociativity _ = property $ \(Apply2 (f :: s Integer Integer)) (Apply2 (g :: s Integer Integer)) (Apply2 (h :: s Integer Integer)) -> eq2 (f `o` (g `o` h)) ((f `o` g) `o` h)
+
+semigroupoidCommutativity :: forall proxy s.
+#if HAVE_QUANTIFIED_CONSTRAINTS
+  (Semigroupoid s, forall a b. (Eq a, Eq b) => Eq (s a b), forall a b. (Show a, Show b) => Show (s a b), forall a b. (Arbitrary a, Arbitrary b) => Arbitrary (s a b))
+#else
+  (Semigroupoid s, Eq2 s, Show2 s, Arbitrary2 s)
+#endif
+  => proxy s -> Property
+semigroupoidCommutativity _ = property $ \(Apply2 (f :: s Integer Integer)) (Apply2 (g :: s Integer Integer)) -> eq2 (f `o` g) (g `o` f)
+
+#endif
diff --git a/src/Test/QuickCheck/Classes/Semiring.hs b/src/Test/QuickCheck/Classes/Semiring.hs
new file mode 100644
--- /dev/null
+++ b/src/Test/QuickCheck/Classes/Semiring.hs
@@ -0,0 +1,191 @@
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+
+{-# OPTIONS_GHC -Wall #-}
+
+module Test.QuickCheck.Classes.Semiring
+  (
+#if HAVE_SEMIRINGS
+    semiringLaws
+#endif
+  ) where
+
+#if HAVE_SEMIRINGS
+import Data.Semiring hiding (fromInteger)
+import Prelude hiding (Num(..))
+import Prelude (fromInteger)
+#endif
+
+import Data.Proxy (Proxy)
+import Test.QuickCheck hiding ((.&.))
+import Test.QuickCheck.Property (Property)
+
+import Test.QuickCheck.Classes.Internal (Laws(..), myForAllShrink)
+
+#if HAVE_SEMIRINGS
+-- | Tests the following properties:
+--
+-- [/Additive Commutativity/]
+--   @a + b ≡ b + a@
+-- [/Additive Left Identity/]
+--   @0 + a ≡ a@
+-- [/Additive Right Identity/]
+--   @a + 0 ≡ a@
+-- [/Multiplicative Associativity/]
+--   @a * (b * c) ≡ (a * b) * c@
+-- [/Multiplicative Left Identity/]
+--   @1 * a ≡ a@
+-- [/Multiplicative Right Identity/]
+--   @a * 1 ≡ a@
+-- [/Multiplication Left Distributes Over Addition/]
+--   @a * (b + c) ≡ (a * b) + (a * c)@
+-- [/Multiplication Right Distributes Over Addition/]
+--   @(a + b) * c ≡ (a * c) + (b * c)@
+-- [/Multiplicative Left Annihilation/]
+--   @0 * a ≡ 0@
+-- [/Multiplicative Right Annihilation/]
+--   @a * 0 ≡ 0@
+--
+-- Also tests that 'fromNatural' is a homomorphism of semirings:
+--
+-- [/FromNatural Maps Zero/]
+--   'fromNatural' 0 = 'zero'
+-- [/FromNatural Maps One/]
+--   'fromNatural' 1 = 'one'
+-- [/FromNatural Maps Plus/]
+--   'fromNatural' (@a@ + @b@) = 'fromNatural' @a@ + 'fromNatural' @b@
+-- [/FromNatural Maps Times/]
+--   'fromNatural' (@a@ * @b@) = 'fromNatural' @a@ * 'fromNatural' @b@
+semiringLaws :: (Semiring a, Eq a, Arbitrary a, Show a) => Proxy a -> Laws
+semiringLaws p = Laws "Semiring"
+  [ ("Additive Commutativity", semiringCommutativePlus p)
+  , ("Additive Left Identity", semiringLeftIdentityPlus p)
+  , ("Additive Right Identity", semiringRightIdentityPlus p)
+  , ("Multiplicative Associativity", semiringAssociativeTimes p)
+  , ("Multiplicative Left Identity", semiringLeftIdentityTimes p)
+  , ("Multiplicative Right Identity", semiringRightIdentityTimes p)
+  , ("Multiplication Left Distributes Over Addition", semiringLeftMultiplicationDistributes p)
+  , ("Multiplication Right Distributes Over Addition", semiringRightMultiplicationDistributes p)
+  , ("Multiplicative Left Annihilation", semiringLeftAnnihilation p)
+  , ("Multiplicative Right Annihilation", semiringRightAnnihilation p)
+  , ("FromNatural Maps Zero", semiringFromNaturalMapsZero p)
+  , ("FromNatural Maps One", semiringFromNaturalMapsOne p)
+  , ("FromNatural Maps Plus", semiringFromNaturalMapsPlus p)
+  , ("FromNatural Maps Times", semiringFromNaturalMapsTimes p)
+  ]
+
+semiringLeftMultiplicationDistributes :: forall a. (Semiring a, Eq a, Arbitrary a, Show a) => Proxy a -> Property
+semiringLeftMultiplicationDistributes _ = myForAllShrink True (const True)
+  (\(a :: a,b,c) -> ["a = " ++ show a, "b = " ++ show b, "c = " ++ show c])
+  "a * (b + c)"
+  (\(a,b,c) -> a * (b + c))
+  "(a * b) + (a * c)"
+  (\(a,b,c) -> (a * b) + (a * c))
+
+semiringRightMultiplicationDistributes :: forall a. (Semiring a, Eq a, Arbitrary a, Show a) => Proxy a -> Property
+semiringRightMultiplicationDistributes _ = myForAllShrink True (const True)
+  (\(a :: a,b,c) -> ["a = " ++ show a, "b = " ++ show b, "c = " ++ show c])
+  "(a + b) * c"
+  (\(a,b,c) -> (a + b) * c)
+  "(a * c) + (b * c)"
+  (\(a,b,c) -> (a * c) + (b * c))
+
+semiringLeftIdentityPlus :: forall a. (Semiring a, Eq a, Arbitrary a, Show a) => Proxy a -> Property
+semiringLeftIdentityPlus _ = myForAllShrink False (const True)
+  (\(a :: a) -> ["a = " ++ show a])
+  "0 + a"
+  (\a -> zero + a)
+  "a"
+  (\a -> a)
+
+semiringRightIdentityPlus :: forall a. (Semiring a, Eq a, Arbitrary a, Show a) => Proxy a -> Property
+semiringRightIdentityPlus _ = myForAllShrink False (const True)
+  (\(a :: a) -> ["a = " ++ show a])
+  "a + 0"
+  (\a -> a + zero)
+  "a"
+  (\a -> a)
+
+semiringRightIdentityTimes :: forall a. (Semiring a, Eq a, Arbitrary a, Show a) => Proxy a -> Property
+semiringRightIdentityTimes _ = myForAllShrink False (const True)
+  (\(a :: a) -> ["a = " ++ show a])
+  "a * 1"
+  (\a -> a * one)
+  "a"
+  (\a -> a)
+
+semiringLeftIdentityTimes :: forall a. (Semiring a, Eq a, Arbitrary a, Show a) => Proxy a -> Property
+semiringLeftIdentityTimes _ = myForAllShrink False (const True)
+  (\(a :: a) -> ["a = " ++ show a])
+  "1 * a"
+  (\a -> one * a)
+  "a"
+  (\a -> a)
+
+semiringLeftAnnihilation :: forall a. (Semiring a, Eq a, Arbitrary a, Show a) => Proxy a -> Property
+semiringLeftAnnihilation _ = myForAllShrink False (const True)
+  (\(a :: a) -> ["a = " ++ show a])
+  "0 * a"
+  (\a -> zero * a)
+  "0"
+  (\_ -> zero)
+
+semiringRightAnnihilation :: forall a. (Semiring a, Eq a, Arbitrary a, Show a) => Proxy a -> Property
+semiringRightAnnihilation _ = myForAllShrink False (const True)
+  (\(a :: a) -> ["a = " ++ show a])
+  "a * 0"
+  (\a -> a * zero)
+  "0"
+  (\_ -> zero)
+
+semiringCommutativePlus :: forall a. (Semiring a, Eq a, Arbitrary a, Show a) => Proxy a -> Property
+semiringCommutativePlus _ = myForAllShrink True (const True)
+  (\(a :: a,b) -> ["a = " ++ show a, "b = " ++ show b])
+  "a + b"
+  (\(a,b) -> a + b)
+  "b + a"
+  (\(a,b) -> b + a)
+
+semiringAssociativeTimes :: forall a. (Semiring a, Eq a, Arbitrary a, Show a) => Proxy a -> Property
+semiringAssociativeTimes _ = myForAllShrink True (const True)
+  (\(a :: a,b,c) -> ["a = " ++ show a, "b = " ++ show b, "c = " ++ show c])
+  "a * (b * c)"
+  (\(a,b,c) -> a * (b * c))
+  "(a * b) * c"
+  (\(a,b,c) -> (a * b) * c)
+
+semiringFromNaturalMapsZero :: forall a. (Semiring a, Eq a, Arbitrary a, Show a) => Proxy a -> Property
+semiringFromNaturalMapsZero _ = myForAllShrink False (const True)
+  (\_ -> [""])
+  "fromNatural 0"
+  (\() -> fromNatural 0 :: a)
+  "zero"
+  (\() -> zero)
+
+semiringFromNaturalMapsOne :: forall a. (Semiring a, Eq a, Arbitrary a, Show a) => Proxy a -> Property
+semiringFromNaturalMapsOne _ = myForAllShrink False (const True)
+  (\_ -> [""])
+  "fromNatural 1"
+  (\() -> fromNatural 1 :: a)
+  "one"
+  (\() -> one)
+
+-- | There is no Arbitrary instance for Natural in QuickCheck,
+-- so we use NonNegative Integer instead.
+semiringFromNaturalMapsPlus :: forall a. (Semiring a, Eq a, Arbitrary a, Show a) => Proxy a -> Property
+semiringFromNaturalMapsPlus _ = myForAllShrink True (const True)
+  (\(NonNegative a, NonNegative b) -> ["a = " ++ show a, "b = " ++ show b])
+  "fromNatural (a + b)"
+  (\(NonNegative a, NonNegative b) -> fromNatural (fromInteger (a + b)) :: a)
+  "fromNatural a + fromNatural b"
+  (\(NonNegative a, NonNegative b) -> fromNatural (fromInteger a) + fromNatural (fromInteger b))
+
+semiringFromNaturalMapsTimes :: forall a. (Semiring a, Eq a, Arbitrary a, Show a) => Proxy a -> Property
+semiringFromNaturalMapsTimes _ = myForAllShrink True (const True)
+  (\(NonNegative a, NonNegative b) -> ["a = " ++ show a, "b = " ++ show b])
+  "fromNatural (a * b)"
+  (\(NonNegative a, NonNegative b) -> fromNatural (fromInteger (a * b)) :: a)
+  "fromNatural a * fromNatural b"
+  (\(NonNegative a, NonNegative b) -> fromNatural (fromInteger a) * fromNatural (fromInteger b))
+
+#endif
diff --git a/test/Advanced.hs b/test/Advanced.hs
new file mode 100644
--- /dev/null
+++ b/test/Advanced.hs
@@ -0,0 +1,193 @@
+{-# language DerivingStrategies #-}
+{-# language DerivingVia #-}
+{-# language GeneralizedNewtypeDeriving #-}
+{-# language LambdaCase #-}
+{-# language ScopedTypeVariables #-}
+{-# language TypeApplications #-}
+
+import Test.Tasty (TestTree,defaultMain,testGroup,adjustOption)
+import Test.QuickCheck (Arbitrary)
+import Data.Proxy (Proxy(..))
+import Data.Set (Set)
+import Data.Primitive (Array)
+import Control.Monad (forM_,replicateM)
+import Data.Monoid (All(..))
+import Test.QuickCheck.Classes (eqLaws,ordLaws)
+import Data.Typeable (Typeable,typeRep)
+import Data.Coerce (coerce)
+import Data.Set (Set)
+
+import qualified Data.Set as S
+import qualified Data.List as L
+import qualified GHC.Exts as E
+import qualified Test.QuickCheck as QC
+import qualified Test.Tasty.QuickCheck as TQC
+import qualified Test.QuickCheck.Classes as QCC
+
+main :: IO ()
+main = defaultMain tests
+
+tests :: TestTree
+tests = testGroup "universe"
+  [ testGroup "deriving"
+    [ testGroup "strict"
+      [ laws @A [eqLaws,ordLaws]
+      , laws @B [eqLaws,ordLaws]
+      , laws @C [eqLaws,ordLaws]
+      , laws @D [eqLaws,ordLaws]
+      , laws @E [eqLaws,ordLaws]
+      , laws @F [eqLaws,ordLaws]
+      , laws @G [eqLaws,ordLaws]
+      , laws @H [eqLaws,ordLaws]
+      , laws @I [eqLaws,ordLaws]
+      , laws @K [eqLaws,ordLaws]
+      ]
+    , testGroup "thunk"
+      [ laws @(Thunk A) [eqLaws,ordLaws]
+      , laws @(Thunk B) [eqLaws,ordLaws]
+      , laws @(Thunk C) [eqLaws,ordLaws]
+      , laws @(Thunk D) [eqLaws,ordLaws]
+      , laws @(Thunk E) [eqLaws,ordLaws]
+      , laws @(Thunk F) [eqLaws,ordLaws]
+      , laws @(Thunk G) [eqLaws,ordLaws]
+      , laws @(Thunk H) [eqLaws,ordLaws]
+      , laws @(Thunk I) [eqLaws,ordLaws]
+      , laws @(Thunk K) [eqLaws,ordLaws]
+      ]
+    , testGroup "lazy"
+      [ laws @(Lazy A) [eqLaws,ordLaws]
+      , laws @(Lazy B) [eqLaws,ordLaws]
+      , laws @(Lazy C) [eqLaws,ordLaws]
+      , laws @(Lazy D) [eqLaws,ordLaws]
+      , laws @(Lazy E) [eqLaws,ordLaws]
+      , laws @(Lazy F) [eqLaws,ordLaws]
+      , laws @(Lazy G) [eqLaws,ordLaws]
+      , laws @(Lazy H) [eqLaws,ordLaws]
+      , laws @(Lazy I) [eqLaws,ordLaws]
+      , laws @(Lazy K) [eqLaws,ordLaws]
+      ]
+    ]
+  , testGroup "containers"
+    [ testGroup "strict"
+      [ laws @(Set A) [eqLaws,ordLaws]
+      , laws @(Set B) [eqLaws,ordLaws]
+      , laws @(Set C) [eqLaws,ordLaws]
+      , laws @(Set D) [eqLaws,ordLaws]
+      , laws @(Set E) [eqLaws,ordLaws]
+      , laws @(Set F) [eqLaws,ordLaws]
+      , laws @(Set G) [eqLaws,ordLaws]
+      , laws @(Set H) [eqLaws,ordLaws]
+      , laws @(Set I) [eqLaws,ordLaws]
+      , laws @(Set K) [eqLaws,ordLaws]
+      ]
+    , testGroup "lazy"
+      [ laws @(SmallLazySet A) [eqLaws,ordLaws]
+      , laws @(SmallLazySet B) [eqLaws,ordLaws]
+      , laws @(SmallLazySet C) [eqLaws,ordLaws]
+      , laws @(SmallLazySet D) [eqLaws,ordLaws]
+      , laws @(SmallLazySet E) [eqLaws,ordLaws]
+      , laws @(SmallLazySet F) [eqLaws,ordLaws]
+      , laws @(SmallLazySet G) [eqLaws,ordLaws]
+      , laws @(SmallLazySet H) [eqLaws,ordLaws]
+      , laws @(SmallLazySet I) [eqLaws,ordLaws]
+      , laws @(SmallLazySet K) [eqLaws,ordLaws]
+      ]
+    ]
+  ]
+
+data A = A0
+  deriving stock (Eq,Ord,Show,Read,Bounded,Enum)
+  deriving Arbitrary via (Enumeration A)
+
+data B = B0 | B1
+  deriving stock (Eq,Ord,Show,Read,Bounded,Enum)
+  deriving Arbitrary via (Enumeration B)
+
+data C = C0 | C1 | C2
+  deriving stock (Eq,Ord,Show,Read,Bounded,Enum)
+  deriving Arbitrary via (Enumeration C)
+
+data D = D0 | D1 | D2 | D3
+  deriving stock (Eq,Ord,Show,Read,Bounded,Enum)
+  deriving Arbitrary via (Enumeration D)
+
+data E = E0 | E1 | E2 | E3 | E4
+  deriving stock (Eq,Ord,Show,Read,Bounded,Enum)
+  deriving Arbitrary via (Enumeration E)
+
+data F = F0 | F1 | F2 | F3 | F4 | F5
+  deriving stock (Eq,Ord,Show,Read,Bounded,Enum)
+  deriving Arbitrary via (Enumeration F)
+
+data G = G0 | G1 | G2 | G3 | G4 | G5 | G6
+  deriving stock (Eq,Ord,Show,Read,Bounded,Enum)
+  deriving Arbitrary via (Enumeration G)
+
+data H = H0 | H1 | H2 | H3 | H4 | H5 | H6 | H7
+  deriving stock (Eq,Ord,Show,Read,Bounded,Enum)
+  deriving Arbitrary via (Enumeration H)
+
+data I = I0 | I1 | I2 | I3 | I4 | I5 | I6 | I7 | I8
+  deriving stock (Eq,Ord,Show,Read,Bounded,Enum)
+  deriving Arbitrary via (Enumeration I)
+
+data J = J0 | J1 | J2 | J3 | J4 | J5 | J6 | J7 | J8 | J9
+  deriving stock (Eq,Ord,Show,Read,Bounded,Enum)
+  deriving Arbitrary via (Enumeration J)
+
+data K = K0 | K1 | K2 | K3 | K4 | K5 | K6 | K7 | K8 | K9 | K10
+  deriving stock (Eq,Ord,Show,Read,Bounded,Enum)
+  deriving Arbitrary via (Enumeration K)
+
+laws :: forall a. Typeable a => [Proxy a -> QCC.Laws] -> TestTree
+laws = testGroup (show (typeRep (Proxy :: Proxy a))) . map
+  ( \f -> let QCC.Laws name pairs = f (Proxy :: Proxy a) in
+    testGroup name (map (uncurry TQC.testProperty) pairs)
+  )
+
+newtype Enumeration a = Enumeration a
+
+instance (Bounded a, Enum a, Eq a) => Arbitrary (Enumeration a) where
+  arbitrary = fmap Enumeration TQC.arbitraryBoundedEnum
+  shrink (Enumeration x) = if x == minBound
+    then []
+    else [Enumeration (pred x)]
+
+data Thunk a = Thunk a
+  deriving stock (Eq,Ord,Show,Read)
+
+newtype Lazy a = Lazy a
+  deriving newtype (Eq,Ord,Show,Read)
+
+newtype SmallLazySet a = SmallLazySet (Set a)
+  deriving newtype (Eq,Ord,Show,Read)
+
+instance Arbitrary a => Arbitrary (Thunk a) where
+  arbitrary = do
+    a <- TQC.arbitrary
+    let {-# NOINLINE b #-}
+        b () = a
+    pure (Thunk (b ()))
+  shrink (Thunk x) = map Thunk (TQC.shrink x)
+
+instance Arbitrary a => Arbitrary (Lazy a) where
+  arbitrary = do
+    a <- TQC.arbitrary
+    let {-# NOINLINE b #-}
+        b () = a
+    pure (Lazy (b ()))
+  shrink (Lazy x) = map Lazy (TQC.shrink x)
+
+instance (Arbitrary a, Ord a) => Arbitrary (SmallLazySet a) where
+  arbitrary = do
+    a <- TQC.arbitrary
+    b <- TQC.arbitrary
+    c <- TQC.arbitrary
+    let {-# NOINLINE a' #-}
+        a' () = a
+    let {-# NOINLINE b' #-}
+        b' () = b
+    let {-# NOINLINE c' #-}
+        c' () = c
+    pure (SmallLazySet (S.fromList [a' (), b' (), c' (), a' (), b' (), c' ()]))
+
diff --git a/test/Spec.hs b/test/Spec.hs
--- a/test/Spec.hs
+++ b/test/Spec.hs
@@ -1,76 +1,260 @@
 {-# LANGUAGE CPP #-}
 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE KindSignatures #-}
 {-# LANGUAGE RankNTypes #-}
 {-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE DeriveFunctor #-}
+{-# LANGUAGE DeriveTraversable #-}
 
-import Test.QuickCheck
-import Data.Proxy
-import Data.Word
-import Data.Int
+#if HAVE_QUANTIFIED_CONSTRAINTS
+{-# LANGUAGE QuantifiedConstraints #-}
+#endif
+
 import Control.Monad
-import Data.Primitive
+import Control.Monad.Zip (MonadZip)
+import Control.Applicative
+#if defined(VERSION_aeson)
+import Data.Aeson (ToJSON,FromJSON)
+#endif
+import Data.Bits
 import Data.Foldable
-import Data.Monoid (Sum)
-import Foreign.Storable
+import Data.Map (Map)
+import qualified Data.Map as M
+#if MIN_VERSION_containers(0,5,9)
+import qualified Data.Map.Merge.Strict as MM
+#endif
+import Data.Traversable
+#if HAVE_SEMIGROUPOIDS
+import Data.Functor.Apply (Apply((<.>)))
+#endif
+#if HAVE_BINARY_LAWS
+import Data.Functor.Const (Const(..))
+#endif
+#if HAVE_UNARY_LAWS
 import Data.Functor.Classes
-import Data.Aeson (ToJSON,FromJSON)
+#endif
+import Data.Int
+import Data.Monoid (Sum(..),Monoid,mappend,mconcat,mempty)
+import Data.Orphans ()
+import Data.Primitive
+import Data.Proxy
 import Data.Vector (Vector)
+import Data.Word
+import Foreign.Storable
+import Test.QuickCheck
+import Text.Show.Functions
 
 import qualified Data.Vector as V
+import qualified Data.Foldable as F
 
 import Test.QuickCheck.Classes
+import qualified Spec.ShowRead
 
 main :: IO ()
-main = lawsCheckMany allPropsApplied
+main = do
+#if HAVE_SEMIGROUPOIDS
+#if MIN_VERSION_containers(0,5,9)
+  quickCheck prop_map_apply_equals
+#endif
+#endif
+  lawsCheckMany allPropsApplied
 
 allPropsApplied :: [(String,[Laws])]
-allPropsApplied = 
+allPropsApplied = M.toList . M.fromListWith (++) $
   [ ("Int",allLaws (Proxy :: Proxy Int))
   , ("Int64",allLaws (Proxy :: Proxy Int64))
   , ("Word",allLaws (Proxy :: Proxy Word))
-#if MIN_VERSION_QuickCheck(2,10,0)
-  , ("Maybe",allHigherLaws (Proxy :: Proxy Maybe))
-  , ("List",allHigherLaws (Proxy :: Proxy []))
+#if HAVE_BINARY_LAWS
+  , ("Tuple"
+    , [ bitraversableLaws (Proxy :: Proxy (,))
+      , bifoldableLaws (Proxy :: Proxy (,))
+      ]
+    )
+  , ("Const"
+    , [ bifoldableLaws (Proxy :: Proxy Const)
+      , bitraversableLaws (Proxy :: Proxy Const)
+      ]
+    )
+  , ("Either"
+    , [ bitraversableLaws (Proxy :: Proxy Either)
+      , bifoldableLaws (Proxy :: Proxy Either)
+      ]
+    )
 #endif
-  , ("Vector",[isListLaws (Proxy :: Proxy (Vector Word))])
+#if HAVE_UNARY_LAWS
+  , ("Maybe",allHigherLaws (Proxy1 :: Proxy1 Maybe))
+  , ("List",allHigherLaws (Proxy1 :: Proxy1 []))
+--  , ("BadList",allHigherLaws (Proxy1 :: Proxy1 BadList))
+#endif
+#if defined(HAVE_SEMIGROUPOIDS) && defined(HAVE_UNARY_LAWS)
+#if MIN_VERSION_base(4,9,0) && MIN_VERSION_containers(0,5,9)
+  , ("Map", someHigherLaws (Proxy1 :: Proxy1 (Map Int)))
+  , ("Pound", someHigherLaws (Proxy1 :: Proxy1 (Pound Int)))
+#endif
+#endif
+#if MIN_VERSION_base(4,7,0)
+  , ("Vector",
+    [ isListLaws (Proxy :: Proxy (Vector Word))
+#if HAVE_VECTOR
+    , muvectorLaws (Proxy :: Proxy Word8)
+    , muvectorLaws (Proxy :: Proxy (Int, Word))
+#endif
+    ])
+#endif
   ]
+  ++ Spec.ShowRead.lawsApplied
 
-allLaws :: forall a. (Num a, Prim a, Storable a, Ord a, Arbitrary a, Show a, Read a, ToJSON a, FromJSON a) => Proxy a -> [Laws]
-allLaws p = 
+allLaws :: forall a.
+  ( Integral a
+  , Num a
+  , Prim a
+  , Storable a
+  , Ord a
+  , Arbitrary a
+  , Show a
+  , Read a
+  , Enum a
+  , Bounded a
+#if defined(VERSION_aeson)
+  , ToJSON a
+  , FromJSON a
+#endif
+#if MIN_VERSION_base(4,7,0)
+  , FiniteBits a
+#endif
+  ) => Proxy a -> [Laws]
+allLaws p =
   [ primLaws p
   , storableLaws p
+  , semigroupLaws (Proxy :: Proxy (Sum a))
   , monoidLaws (Proxy :: Proxy (Sum a))
-  , showReadLaws p
+  , boundedEnumLaws p
+#if defined(VERSION_aeson)
   , jsonLaws p
+#endif
   , eqLaws p
   , ordLaws p
+  , numLaws p
+  , integralLaws p
+#if MIN_VERSION_base(4,7,0)
+  , bitsLaws p
+#endif
   ]
 
 foldlMapM :: (Foldable t, Monoid b, Monad m) => (a -> m b) -> t a -> m b
-foldlMapM f = foldlM (\b a -> fmap (mappend b) (f a)) mempty
+foldlMapM f = foldlM (\b a -> liftM (mappend b) (f a)) mempty
 
-#if MIN_VERSION_QuickCheck(2,10,0)
-allHigherLaws :: (Foldable f, Monad f, Eq1 f, Arbitrary1 f, Show1 f) => Proxy f -> [Laws]
-allHigherLaws p = 
+#if HAVE_UNARY_LAWS
+allHigherLaws ::
+  (Traversable f, MonadZip f, MonadPlus f, Applicative f,
+#if HAVE_QUANTIFIED_CONSTRAINTS
+   forall a. Eq a => Eq (f a), forall a. Arbitrary a => Arbitrary (f a),
+   forall a. Show a => Show (f a)
+#else
+   Eq1 f, Arbitrary1 f, Show1 f
+#endif
+  ) => proxy f -> [Laws]
+allHigherLaws p =
   [ functorLaws p
   , applicativeLaws p
   , monadLaws p
+  , monadPlusLaws p
+  , monadZipLaws p
   , foldableLaws p
+  , traversableLaws p
   ]
 #endif
 
--- This type is fails the laws for the strict functions
+#if defined(HAVE_SEMIGROUPOIDS) && defined(HAVE_UNARY_LAWS)
+someHigherLaws ::
+  (Apply f,
+#if HAVE_QUANTIFIED_CONSTRAINTS
+   forall a. Eq a => Eq (f a), forall a. Arbitrary a => Arbitrary (f a),
+   forall a. Show a => Show (f a)
+#else
+   Eq1 f, Arbitrary1 f, Show1 f
+#endif
+  ) => proxy f -> [Laws]
+someHigherLaws p =
+  [ applyLaws p
+  ]
+#endif
+
+-- This type fails the laws for the strict functions
 -- in Foldable. It is used just to confirm that
 -- those property tests actually work.
-newtype Rouge a = Rouge [a]
-  deriving (Eq,Show,Arbitrary,Arbitrary1,Eq1,Show1)
+newtype Rogue a = Rogue [a]
+  deriving
+  ( Eq, Show, Arbitrary
+#if HAVE_UNARY_LAWS
+  , Arbitrary1
+  , Eq1
+  , Show1
+#endif
+  )
 
-instance Foldable Rouge where
-  foldMap f (Rouge xs) = foldMap f xs
-  foldl f x (Rouge xs) = foldl f x xs
-  foldl' f x (Rouge xs) = foldl f x xs
-  foldr' f x (Rouge xs) = foldr f x xs
+-- Note: when using base < 4.6, the Rogue type does
+-- not really test anything.
+instance Foldable Rogue where
+  foldMap f (Rogue xs) = F.foldMap f xs
+  foldl f x (Rogue xs) = F.foldl f x xs
+#if MIN_VERSION_base(4,6,0)
+  foldl' f x (Rogue xs) = F.foldl f x xs
+  foldr' f x (Rogue xs) = F.foldr f x xs
+#endif
 
+newtype BadList a = BadList [a]
+  deriving
+  ( Eq, Show, Arbitrary
+  , Arbitrary1, Eq1, Show1
+  , Traversable, Functor, MonadZip, Monad, Applicative, MonadPlus, Alternative
+  )
+
+instance Foldable BadList where
+  foldMap f (BadList xs) = F.foldMap f xs
+  fold (BadList xs) = fold (reverse xs)
+
+newtype Pound k v = Pound { getPound :: Map k v }
+  deriving
+  ( Eq, Functor, Show, Arbitrary
+#if HAVE_UNARY_LAWS
+  , Arbitrary1
+  -- The following instances are only available for the variants
+  -- of the type classes in base, not for those in transformers.
+#if MIN_VERSION_base(4,9,0) && MIN_VERSION_containers(0,5,9)
+  , Eq1
+  , Show1
+#endif
+#endif
+  )
+
+#if HAVE_SEMIGROUPOIDS
+#if MIN_VERSION_containers(0,5,9)
+instance Ord k => Apply (Pound k) where
+  Pound m1 <.> Pound m2 = Pound $
+    MM.merge
+      MM.dropMissing
+      MM.dropMissing
+      (MM.zipWithMatched (\_ f a -> f a))
+      m1
+      m2
+#endif
+#endif
+
+#if HAVE_SEMIGROUPOIDS
+#if MIN_VERSION_containers(0,5,9)
+prop_map_apply_equals :: Map Int (Int -> Int)
+                      -> Map Int Int
+                      -> Bool
+prop_map_apply_equals mf ma =
+  let pf = Pound mf
+      pa = Pound ma
+      m = mf <.> ma
+      p = pf <.> pa
+  in m == (getPound p)
+#endif
+#endif
+
 -------------------
 -- Orphan Instances
 -------------------
@@ -79,3 +263,14 @@
   arbitrary = V.fromList <$> arbitrary
   shrink v = map V.fromList (shrink (V.toList v))
 
+#if !MIN_VERSION_QuickCheck(2,8,2)
+instance (Ord k, Arbitrary k, Arbitrary v) => Arbitrary (Map k v) where
+  arbitrary = M.fromList <$> arbitrary
+  shrink m = map M.fromList (shrink (M.toList m))
+#endif
+
+#if !MIN_VERSION_QuickCheck(2,9,0)
+instance Arbitrary a => Arbitrary (Sum a) where
+  arbitrary = Sum <$> arbitrary
+  shrink = map Sum . shrink . getSum
+#endif
diff --git a/test/Spec/ShowRead.hs b/test/Spec/ShowRead.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec/ShowRead.hs
@@ -0,0 +1,121 @@
+{-# LANGUAGE CPP #-}
+{-# OPTIONS_GHC -Wall #-}
+
+module Spec.ShowRead where
+
+import Control.Applicative (liftA2)
+import Data.Complex (Complex)
+import Data.Fixed (E0, E1, E12, Fixed, HasResolution)
+import Data.Int (Int64, Int8)
+import Data.Orphans ()
+import Data.Proxy (Proxy(Proxy))
+import Data.Ratio (Ratio)
+import Data.Word
+import Test.QuickCheck (Arbitrary(arbitrary), elements)
+#if MIN_VERSION_QuickCheck(2,8,2)
+import Data.IntMap (IntMap)
+import Data.IntSet (IntSet)
+import Data.Map (Map)
+import Data.Sequence (Seq)
+import Data.Set (Set)
+#endif
+#if MIN_VERSION_QuickCheck(2,9,0)
+import Control.Applicative (Const, ZipList)
+import Data.Functor.Constant (Constant)
+import Data.Functor.Identity (Identity)
+import Data.Version (Version)
+#endif
+#if MIN_VERSION_QuickCheck(2,10,0)
+import Data.Functor.Compose (Compose)
+import Data.Functor.Product (Product)
+#endif
+
+import Test.QuickCheck.Classes
+
+data Prefix = Prefix | Prefix' | Prefix_
+  deriving (Eq, Read, Show)
+
+instance Arbitrary Prefix where
+  arbitrary = elements [Prefix, Prefix', Prefix_]
+
+data WeirdRecord = (:*) { left :: Int, right :: Int }
+  deriving (Eq, Read, Show)
+
+instance Arbitrary WeirdRecord where
+  arbitrary = liftA2 (:*) arbitrary arbitrary
+
+lawsApplied :: [(String,[Laws])]
+lawsApplied =
+  [ -- local
+    ("Prefix",         allShowReadLaws (Proxy :: Proxy Prefix))
+  , ("WeirdRecord",    allShowReadLaws (Proxy :: Proxy WeirdRecord))
+
+    -- base
+  , ("()",             allShowReadLaws (Proxy :: Proxy ()))
+  , ("Bool",           allShowReadLaws (Proxy :: Proxy Bool))
+  , ("Char",           allShowReadLaws (Proxy :: Proxy Char))
+  , ("Complex Float",  allShowReadLaws (Proxy :: Proxy (Complex Float)))
+  , ("Complex Double", allShowReadLaws (Proxy :: Proxy (Complex Double)))
+  , ("Double",         allShowReadLaws (Proxy :: Proxy Double))
+  , ("Either",         allShowReadLaws (Proxy :: Proxy (Either Int Int)))
+  , ("Fixed E12",      allFixedLaws (Proxy :: Proxy (Fixed E12)))
+  -- , ("Fixed E9",       allFixedLaws (Proxy :: Proxy (Fixed E9)))
+  -- , ("Fixed E6",       allFixedLaws (Proxy :: Proxy (Fixed E6)))
+  -- , ("Fixed E3",       allFixedLaws (Proxy :: Proxy (Fixed E3)))
+  -- , ("Fixed E2",       allFixedLaws (Proxy :: Proxy (Fixed E2)))
+  , ("Fixed E1",       allFixedLaws (Proxy :: Proxy (Fixed E1)))
+  , ("Fixed E0",       allFixedLaws (Proxy :: Proxy (Fixed E0)))
+  , ("Float",          allShowReadLaws (Proxy :: Proxy Float))
+  , ("Int",            allShowReadLaws (Proxy :: Proxy Int))
+  -- , ("Int16",          allShowReadLaws (Proxy :: Proxy Int16))
+  -- , ("Int32",          allShowReadLaws (Proxy :: Proxy Int32))
+  , ("Int64",          allShowReadLaws (Proxy :: Proxy Int64))
+  , ("Int8",           allShowReadLaws (Proxy :: Proxy Int8))
+  , ("Integer",        allShowReadLaws (Proxy :: Proxy Integer))
+  , ("List",           allShowReadLaws (Proxy :: Proxy [Int]))
+  , ("Maybe",          allShowReadLaws (Proxy :: Proxy (Maybe Int)))
+  , ("Ordering",       allShowReadLaws (Proxy :: Proxy Ordering))
+  , ("Ratio",          allShowReadLaws (Proxy :: Proxy (Ratio Int)))
+  , ("Tuple2",         allShowReadLaws (Proxy :: Proxy (Int,Int)))
+  , ("Tuple3",         allShowReadLaws (Proxy :: Proxy (Int,Int,Int)))
+  , ("Word",           allShowReadLaws (Proxy :: Proxy Word))
+  -- , ("Word16",         allShowReadLaws (Proxy :: Proxy Word16))
+  -- , ("Word32",         allShowReadLaws (Proxy :: Proxy Word32))
+  , ("Word64",         allShowReadLaws (Proxy :: Proxy Word64))
+  , ("Word8",          allShowReadLaws (Proxy :: Proxy Word8))
+#if MIN_VERSION_QuickCheck(2,9,0)
+  , ("Const",          allShowReadLaws (Proxy :: Proxy (Const Int Int)))
+  , ("Constant",       allShowReadLaws (Proxy :: Proxy (Constant Int Int)))
+  , ("Identity",       allShowReadLaws (Proxy :: Proxy (Identity Int)))
+  , ("Version",        allShowReadLaws (Proxy :: Proxy Version))
+  , ("ZipList",        allShowReadLaws (Proxy :: Proxy (ZipList Int)))
+#endif
+#if MIN_VERSION_QuickCheck(2,10,0)
+  , ("Compose",        allShowReadLaws (Proxy :: Proxy (Compose [] Maybe Int)))
+  , ("Product",        allShowReadLaws (Proxy :: Proxy (Product [] Maybe Int)))
+#endif
+
+  -- containers
+#if MIN_VERSION_QuickCheck(2,8,2)
+  , ("IntMap",         allShowReadLaws (Proxy :: Proxy (IntMap Int)))
+  , ("IntSet",         allShowReadLaws (Proxy :: Proxy IntSet))
+  , ("Map",            allShowReadLaws (Proxy :: Proxy (Map Int Int)))
+  , ("Seq",            allShowReadLaws (Proxy :: Proxy (Seq Int)))
+  , ("Set",            allShowReadLaws (Proxy :: Proxy (Set Int)))
+#endif
+  ]
+
+allShowReadLaws :: (Show a, Read a, Eq a, Arbitrary a) => Proxy a -> [Laws]
+allShowReadLaws p = map ($p)
+  [ showLaws
+  , showReadLaws
+  ]
+
+allFixedLaws :: HasResolution e => Proxy (Fixed e) -> [Laws]
+allFixedLaws p = map ($p)
+  [ showLaws
+#if MIN_VERSION_base(4,7,0)
+  -- Earlier versions of base have a buggy read instance.
+  , showReadLaws
+#endif
+  ]
