diff --git a/.hlint.yaml b/.hlint.yaml
new file mode 100644
--- /dev/null
+++ b/.hlint.yaml
@@ -0,0 +1,2 @@
+- ignore: {name: Unused LANGUAGE pragma}
+- ignore: {name: Avoid lambda}
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
--- a/.travis.yml
+++ /dev/null
@@ -1,56 +0,0 @@
-language: haskell
-
-env:
-  - GHCVER=7.4.2
-  - GHCVER=7.6.3
-  - GHCVER=7.8.3
-  - GHCVER=head
-
-matrix:
-  allow_failures:
-    - env: GHCVER=head
-
-before_install:
-  # If $GHCVER is the one travis has, don't bother reinstalling it.
-  # We can also have faster builds by installing some libraries with
-  # `apt`. If it isn't, install the GHC we want from hvr's PPA along
-  # with cabal-1.18.
-  - |
-    if [ $GHCVER = `ghc --numeric-version` ]; then
-      # Try installing some of the build-deps with apt-get for speed.
-      travis/cabal-apt-install --enable-tests $MODE
-      export CABAL=cabal
-    else
-      # Install the GHC we want from hvr's PPA
-      travis_retry sudo add-apt-repository -y ppa:hvr/ghc
-      travis_retry sudo apt-get update
-      travis_retry sudo apt-get install cabal-install-1.18 ghc-$GHCVER happy
-      export CABAL=cabal-1.18
-      export PATH=/opt/ghc/$GHCVER/bin:$PATH
-    fi
-  # Uncomment whenever hackage is down.
-  # - mkdir -p ~/.cabal && cp travis/config ~/.cabal/config && $CABAL update
-  - $CABAL update
-
-  # Update happy when building with GHC head
-  - |
-    if [ $GHCVER = "head" ] || [ $GHCVER = "7.8.3" ]; then
-      $CABAL install happy alex
-      export PATH=$HOME/.cabal/bin:$PATH
-    fi
-
-install:
-  - $CABAL install --dependencies-only --enable-tests
-  - $CABAL configure --enable-tests $MODE
-
-script:
-  - $CABAL build
-  - $CABAL test --show-details=always
-
-notifications:
-  irc:
-    channels:
-      - "irc.freenode.org#haskell-lens"
-    skip_join: true
-    template:
-      - "\x0313reflection\x03/\x0306%{branch}\x03 \x0314%{commit}\x03 %{build_url} %{message}"
diff --git a/CHANGELOG.markdown b/CHANGELOG.markdown
--- a/CHANGELOG.markdown
+++ b/CHANGELOG.markdown
@@ -1,3 +1,45 @@
+# 2.1.9 [2024.12.04]
+* Drop support for pre-8.0 versions of GHC.
+
+# 2.1.8 [2024.05.04]
+* Fix a memory leak in `reifyTypeable`.
+
+# 2.1.7 [2023.02.28]
+* When building with `base-4.18` (GHC 9.6) or later, implement `reifyNat` and
+  `reifySymbol` using the API provided by `GHC.TypeLits` instead of resorting
+  to `unsafeCoerce`.
+
+# 2.1.6 [2020.05.16]
+* Fix a bug in which `give` (and possibly `reify`, `reifyNat`, and
+  `reifySymbol`) could be unsoundly inlined by GHC 8.10 or older to produce
+  incorrect runtime results.
+
+# 2.1.5 [2019.08.27]
+* Fix a bug in which `reifyNat` would yield incorrect results for very large
+  `Integer`s on GHC 8.2 or later.
+
+# 2.1.4 [2018.07.02]
+* Allow building with GHC 8.6.
+
+# 2.1.3 [2018.01.18]
+* Add `Semigroup` instance for `ReflectedMonoid`.
+
+# 2.1.2
+* Support cross-compilation and unregistered GHC builds.
+
+# 2.1.1.1
+* Fixed typos in the documentation.
+
+# 2.1.1
+* Fixed support for GHC 7.0
+
+# 2.1
+* Added `ReifiedMonoid` and `ReifiedApplicative`.
+
+# 2
+* Added `reifyNat` and `reifySymbol` for GHC 7.8+, capable of reflecting into the `KnownNat` and `KnownSymbol` classes respectively for use with other APIs.
+* Back-ported `reifyTypeable` from `lens`. This enables us to perform a (less efficient) form of `Typeable` reflection.
+
 # 1.5.2
 * Renamed the flag for disabling the use of `template-haskell`, to `-f-template-haskell` for consistency with my other packages.
 
diff --git a/README.markdown b/README.markdown
--- a/README.markdown
+++ b/README.markdown
@@ -1,7 +1,7 @@
 reflection
 ==========
 
-[![Build Status](https://secure.travis-ci.org/ekmett/reflection.png?branch=master)](http://travis-ci.org/ekmett/reflection)
+[![Hackage](https://img.shields.io/hackage/v/reflection.svg)](https://hackage.haskell.org/package/reflection) [![Build Status](https://github.com/ekmett/reflection/workflows/Haskell-CI/badge.svg)](https://github.com/ekmett/reflection/actions?query=workflow%3AHaskell-CI)
 
 This package provides an implementation of the ideas presented in [Functional Pearl: Implicit Configurations](http://okmij.org/ftp/Haskell/tr-15-04.pdf) by Oleg Kiselyov and Chung-Chieh Shan. However, the API has been implemented in a much more efficient manner.
 
diff --git a/examples/Constraints.hs b/examples/Constraints.hs
--- a/examples/Constraints.hs
+++ b/examples/Constraints.hs
@@ -1,19 +1,24 @@
-{-# LANGUAGE Rank2Types, TypeFamilies, TypeOperators, ConstraintKinds, PolyKinds, FlexibleInstances, MultiParamTypeClasses, ScopedTypeVariables, FlexibleContexts, UndecidableInstances #-}
-import Control.Newtype        -- from newtype
-import Data.Constraint        -- from constraints
-import Data.Constraint.Unsafe -- from constraints
-import Data.Monoid            -- from base
-import Data.Proxy             -- from tagged
-import Data.Reflection        -- from reflection
+{-# LANGUAGE Rank2Types, TypeFamilies, TypeOperators, ConstraintKinds, PolyKinds, FlexibleInstances, MultiParamTypeClasses, ScopedTypeVariables, FlexibleContexts, UndecidableInstances, CPP #-}
+module Constraints where
 
--- | Values in our dynamically constructed monoid over 'a'
+import Control.Newtype.Generics -- from newtype-generics
+import Data.Constraint          -- from constraints
+import Data.Constraint.Unsafe   -- from constraints
+import Data.Proxy               -- from tagged
+import Data.Reflection          -- from reflection
+#if !(MIN_VERSION_base(4,11,0))
+import Data.Semigroup           -- from semigroups
+#endif
+
+-- | Values in our dynamically constructed monoid over @a@
 newtype Lift (p :: * -> Constraint) (a :: *) (s :: *) = Lift { lower :: a }
 
 class ReifiableConstraint p where
   data Def (p :: * -> Constraint) (a :: *)
   reifiedIns :: Reifies s (Def p a) :- p (Lift p a s)
 
-instance Newtype (Lift p a s) a where
+instance Newtype (Lift p a s) where
+  type O (Lift p a s) = a
   pack = Lift
   unpack = lower
 
@@ -22,7 +27,7 @@
 with :: Def p a -> (forall s. Reifies s (Def p a) => Lift p a s) -> a
 with d v = reify d $ lower . asProxyOf v
 
-reifyInstance :: Def p a -> (forall s. Reifies s (Def p a) => Proxy s -> r) -> r
+reifyInstance :: Def p a -> (forall (s :: *). Reifies s (Def p a) => Proxy s -> r) -> r
 reifyInstance = reify
 
 asProxyOf :: f s -> Proxy s -> f s
@@ -40,8 +45,11 @@
   data Def Monoid a = Monoid { mappend_ :: a -> a -> a, mempty_ :: a }
   reifiedIns = Sub Dict
 
+instance Reifies s (Def Monoid a) => Semigroup (Lift Monoid a s) where
+  a <> b             = Lift $ mappend_ (reflect a) (lower a) (lower b)
+
 instance Reifies s (Def Monoid a) => Monoid (Lift Monoid a s) where
-  mappend a b        = Lift $ mappend_ (reflect a) (lower a) (lower b)
+  mappend            = (<>)
   mempty = a where a = Lift $ mempty_ (reflect a)
 
 data ClassProxy (p :: * -> Constraint) = ClassProxy
diff --git a/examples/FromJSON.hs b/examples/FromJSON.hs
--- a/examples/FromJSON.hs
+++ b/examples/FromJSON.hs
@@ -1,27 +1,30 @@
 -- Example of a dynamically generated FromJSON instance.
 --
--- Can be useful when one needs to use a function with a 
--- FromJSON constraint, but some detail about the 
+-- Can be useful when one needs to use a function with a
+-- FromJSON constraint, but some detail about the
 -- conversion from JSON is not known until runtime.
+{-# LANGUAGE CPP #-}
 {-# LANGUAGE Rank2Types #-}
 {-# LANGUAGE FlexibleContexts #-}
 {-# LANGUAGE UndecidableInstances #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE OverloadedStrings #-}
 
-import Data.Reflection -- from reflection
-import Data.Monoid     -- from base
-import Data.Proxy      -- from tagged
-import Data.Text       -- from text
-import Data.Monoid  
-import Data.Aeson      -- from aeson     
+import Data.Aeson             -- from aeson
+import qualified Data.Aeson.Key as Key
 import Data.Aeson.Types (Parser)
+import Data.Proxy             -- from tagged
+import Data.Reflection        -- from reflection
+#if !(MIN_VERSION_base(4,11,0))
+import Data.Semigroup         -- from base
+#endif
+import Data.Text (Text, pack) -- from text
 
 import Control.Applicative
 
 -- These imports are only for constructing the example value
-import Control.Lens (preview)           -- from lens
-import Data.Aeson.Lens (_Value,_String) -- form lens-aeson
+import Lens.Micro.Aeson (_Value)   -- form microlens-aeson
+import Lens.Micro.Extras (preview) -- from microlens
 
 data Foo = Foo
     {
@@ -31,19 +34,20 @@
 
 fooParser :: Text -> Object -> Parser Foo
 fooParser prefix o = do
-    Foo <$> o .: (prefix <> "field1") <*> o .: (prefix <> "field2")
+    Foo <$> o .: (Key.fromText prefix <> "field1") <*> o .: (Key.fromText prefix <> "field2")
 
 -- A wrapper over Foo carrying a phantom type s
 newtype J a s = J { runJ :: a }
- 
--- If the phantom type s reifies the parsing function, we can 
--- use reflect to recover the function and implement 
+
+-- If the phantom type s reifies the parsing function, we can
+-- use reflect to recover the function and implement
 -- our FromJSON instance for J.
 instance Reifies s (Object -> Parser a) => FromJSON (J a s) where
     parseJSON (Object v) = J <$> reflect (Proxy :: Proxy s) v
- 
+    parseJSON _          = empty
+
 -- Convince the compiler that the phantom type in the proxy
--- supplied by reify is the same as the phantom type in J. 
+-- supplied by reify is the same as the phantom type in J.
 --
 -- Otherwise the FromJSON instance for J won't kick in.
 asProxyJ :: Proxy s -> J a s -> J a s
@@ -53,18 +57,18 @@
 exampleJSON = maybe Null id (preview _Value str)
   where
     str = "{ \"zzfield1\" : 5, \"zzfield2\" : 7 }"::Text
- 
+
 main :: IO ()
 main = do
     putStrLn "Enter prefix for the fields: "
     -- "zz" must be entered for the parse to succeed
     prefix <- fmap pack getLine
- 
+
     -- fromJSON uses the dynamically generated FromJSON instance
     let result = reify (fooParser prefix) $ \proxy ->
             -- We must eliminate the J newtype before returning
             -- because, thanks to parametricity,
             -- the phantom type cannot escape the callback.
-            runJ . asProxyJ proxy <$> fromJSON exampleJSON 
- 
+            runJ . asProxyJ proxy <$> fromJSON exampleJSON
+
     putStrLn (show (result :: Result Foo))
diff --git a/examples/LICENSE b/examples/LICENSE
new file mode 100644
--- /dev/null
+++ b/examples/LICENSE
@@ -0,0 +1,31 @@
+Copyright (c) 2009-2013 Edward Kmett
+Copyright (c) 2004 Oleg Kiselyov and Chung-chieh Shan
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of Edward Kmett nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/examples/Monoid.hs b/examples/Monoid.hs
--- a/examples/Monoid.hs
+++ b/examples/Monoid.hs
@@ -1,16 +1,25 @@
-{-# LANGUAGE Rank2Types, FlexibleContexts, UndecidableInstances #-}
-import Data.Reflection -- from reflection
-import Data.Monoid     -- from base
+{-# LANGUAGE CPP, Rank2Types, FlexibleContexts, UndecidableInstances #-}
+module Monoid where
+
 import Data.Proxy      -- from tagged
+import Data.Reflection -- from reflection
+#if !(MIN_VERSION_base(4,11,0))
+import Data.Semigroup  -- from base
+#endif
 
--- | Values in our dynamically-constructed 'Monoid' over 'a'
+-- | Values in our dynamically-constructed 'Monoid' over @a@
 newtype M a s = M { runM :: a } deriving (Eq,Ord)
 
 -- | A dictionary describing a 'Monoid'
 data Monoid_ a = Monoid_ { mappend_ :: a -> a -> a, mempty_ :: a }
 
+instance Reifies s (Monoid_ a) => Semigroup (M a s) where
+  a <> b = M $ mappend_ (reflect a) (runM a) (runM b)
+
 instance Reifies s (Monoid_ a) => Monoid (M a s) where
-  mappend a b        = M $ mappend_ (reflect a) (runM a) (runM b)
+#if !(MIN_VERSION_base(4,11,0))
+  mappend = (<>)
+#endif
   mempty = a where a = M $ mempty_ (reflect a)
 
 -- Construct a 'Monoid' instance out of a binary operation and unit that you have in scope!
diff --git a/examples/ReaderLike.hs b/examples/ReaderLike.hs
new file mode 100644
--- /dev/null
+++ b/examples/ReaderLike.hs
@@ -0,0 +1,70 @@
+-- The UndecidableInstances here is benign, just for the "advanced"
+-- example at the end.
+{-# LANGUAGE FlexibleContexts, ScopedTypeVariables, UndecidableInstances #-}
+
+-- I don't demonstrate the advantages over implicit parameters here,
+-- like multiple reifications of the same thing coexisting in
+-- different, statically-checked contexts, etc.; this is intended as a
+-- comparison to Reader(T).
+
+import Data.Proxy
+import Data.Reflection
+import System.IO
+
+data MyConfig = MyConfig
+  { magic  :: Bool
+  , volume :: Integer
+  }
+
+data Report p = Report
+  { magicality :: String
+  , loud :: Bool
+  } deriving (Show)
+
+-- some arbitrary thing we do calculations with
+newtype Datum p = Datum Integer deriving (Read, Show)
+
+report :: forall p. (Reifies p MyConfig) => Report p
+report = Report
+  { magicality = if magic conf then "Magical." else "Not so magical..."
+  , loud = volume conf >= 11
+  } where
+  conf = reflect (Proxy :: Proxy p)
+
+calculate :: forall p. (Reifies p MyConfig) => Datum p -> Datum p -> Datum p
+calculate (Datum m) (Datum n) = Datum ((m+n) * volume conf) where
+  conf = reflect (Proxy :: Proxy p)
+
+run :: forall p. (Reifies p MyConfig) => Proxy p -> IO ()
+run _ = do
+  d1 <- ask "Datum 1:" :: IO (Datum p)
+  d2 <- ask "Datum 2:" :: IO (Datum p)
+  -- look ma, no plumbing
+  print $ calculate d1 (calculate d2 d1)
+  print (report :: Report p)
+
+ask :: Read s => String -> IO s
+ask prompt = do
+  putStr prompt
+  putChar ' '
+  hFlush stdout
+  readLn
+
+main :: IO ()
+main = do
+  conf <- MyConfig <$> ask "Magic?" <*> ask "Volume:"
+  reify conf run
+
+-- If you're feeling adventurous, here is something we could not do
+-- with ReaderT:
+instance Reifies p MyConfig => Num (Datum p) where
+  (+) = calculate
+  m * n
+    | magic conf = m + n
+    | otherwise  = Datum 0  -- sorry, no magic for you.
+    where conf = reflect (Proxy :: Proxy p)
+  abs = undefined
+  signum = undefined
+  (-) = undefined
+  negate = undefined
+  fromInteger = Datum
diff --git a/examples/reflection-examples.cabal b/examples/reflection-examples.cabal
new file mode 100644
--- /dev/null
+++ b/examples/reflection-examples.cabal
@@ -0,0 +1,80 @@
+name:           reflection-examples
+version:        0.1
+license:        BSD3
+license-file:   LICENSE
+author:         Edward A. Kmett, Elliott Hird, Oleg Kiselyov and Chung-chieh Shan
+maintainer:     Edward A. Kmett <ekmett@gmail.com>
+stability:      experimental
+homepage:       http://github.com/ekmett/reflection
+bug-reports:    http://github.com/ekmett/reflection/issues
+category:       Data, Reflection, Dependent Types
+synopsis:       Examples of reflection
+description:    Examples of reflection.
+copyright:      2009-2013 Edward A. Kmett,
+                2012 Elliott Hird,
+                2004 Oleg Kiselyov and Chung-chieh Shan
+build-type:     Simple
+cabal-version:  >= 1.10
+tested-with:   GHC == 8.0.2
+             , GHC == 8.2.2
+             , GHC == 8.4.4
+             , GHC == 8.6.5
+             , GHC == 8.8.4
+             , GHC == 8.10.7
+             , GHC == 9.0.2
+             , GHC == 9.2.8
+             , GHC == 9.4.8
+             , GHC == 9.6.6
+             , GHC == 9.8.4
+             , GHC == 9.10.1
+             , GHC == 9.12.1
+
+flag examples
+  default: True
+
+library
+  if !flag(examples)
+    buildable:         False
+  exposed-modules:     Constraints
+                       Monoid
+  build-depends:       base >= 4.9 && < 5,
+                       constraints,
+                       newtype-generics >= 0.5.3,
+                       reflection,
+                       semigroups,
+                       tagged
+  hs-source-dirs:      .
+  ghc-options:         -Wall
+  if impl(ghc >= 8.6)
+    ghc-options:       -Wno-star-is-type
+  default-language:    Haskell2010
+
+executable reflection-from-json
+  if !flag(examples)
+    buildable:         False
+  main-is:             FromJSON.hs
+  build-depends:       aeson >= 2 && < 2.3,
+                       base >= 4.9 && < 5,
+                       microlens,
+                       microlens-aeson >= 2.5.1,
+                       reflection,
+                       semigroups,
+                       tagged,
+                       text
+  hs-source-dirs:      .
+  ghc-options:         -Wall -threaded -rtsopts
+  default-language:    Haskell2010
+
+executable reflection-reader-like
+  if !flag(examples)
+    buildable:         False
+  main-is:             ReaderLike.hs
+  build-depends:       base >= 4.9 && < 5,
+                       reflection,
+                       tagged
+  hs-source-dirs:      .
+  ghc-options:         -Wall -threaded -rtsopts
+  if impl(ghc >= 8.6)
+    ghc-options:       -Wno-star-is-type
+  default-language:    Haskell2010
+
diff --git a/fast/Data/Reflection.hs b/fast/Data/Reflection.hs
--- a/fast/Data/Reflection.hs
+++ b/fast/Data/Reflection.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE CPP #-}
+{-# LANGUAGE DataKinds #-}
 {-# LANGUAGE EmptyDataDecls #-}
 {-# LANGUAGE FlexibleContexts #-}
 {-# LANGUAGE FlexibleInstances #-}
@@ -6,22 +7,28 @@
 {-# LANGUAGE KindSignatures #-}
 {-# LANGUAGE MultiParamTypeClasses #-}
 {-# LANGUAGE PatternGuards #-}
+{-# LANGUAGE PolyKinds #-}
 {-# LANGUAGE Rank2Types #-}
 {-# LANGUAGE ScopedTypeVariables #-}
-#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ >= 706
-{-# LANGUAGE DataKinds #-}
-{-# LANGUAGE PolyKinds #-}
 {-# LANGUAGE TypeOperators #-}
-#define USE_TYPE_LITS 1
-#endif
 #ifdef MIN_VERSION_template_haskell
-{-# LANGUAGE TemplateHaskell #-}
+-- TH-subset that works with stage1 & unregisterised GHCs
+{-# LANGUAGE TemplateHaskellQuotes #-}
 #endif
-{-# OPTIONS_GHC -fno-warn-orphans #-}
+
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE UndecidableInstances #-}
+
+{-# OPTIONS_GHC -fno-cse #-}
+{-# OPTIONS_GHC -fno-full-laziness #-}
+{-# OPTIONS_GHC -fno-float-in #-}
+{-# OPTIONS_GHC -Wno-orphans #-}
+{-# OPTIONS_GHC -Wno-unused-binds #-}
+
 ----------------------------------------------------------------------------
 -- |
 -- Module     : Data.Reflection
--- Copyright  : 2009-2013 Edward Kmett,
+-- Copyright  : 2009-2015 Edward Kmett,
 --              2012 Elliott Hird,
 --              2004 Oleg Kiselyov and Chung-chieh Shan
 -- License    : BSD3
@@ -58,6 +65,9 @@
     -- * Reflection
       Reifies(..)
     , reify
+    , reifyNat
+    , reifySymbol
+    , reifyTypeable
     -- * Given
     , Given(..)
     , give
@@ -67,25 +77,74 @@
 #endif
     -- * Useful compile time naturals
     , Z, D, SD, PD
+
+    -- * Reified Monoids
+    , ReifiedMonoid(..)
+    , ReflectedMonoid(..)
+    , reifyMonoid
+    , foldMapBy
+    , foldBy
+
+    -- * Reified Applicatives
+    , ReifiedApplicative(..)
+    , ReflectedApplicative(..)
+    , reifyApplicative
+    , traverseBy
+    , sequenceBy
     ) where
 
+import Control.Applicative
+import Control.Exception
+
+#ifdef MIN_VERSION_template_haskell
+import Control.Monad
+#endif
+
+import Data.Bits
+import Data.Coerce (Coercible, coerce)
 import Data.Proxy
+import Data.Semigroup as Sem
+import Data.Typeable
+import Data.Word
+import Foreign.Ptr
+import Foreign.StablePtr
 
-#if (defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ >= 707) || (defined(MIN_VERSION_template_haskell) && USE_TYPE_LITS)
 import GHC.TypeLits
+#if MIN_VERSION_base(4,10,0)
+import qualified Numeric.Natural as Numeric (Natural)
+#else
+import Control.Exception (ArithException(..), throw)
 #endif
 
+#ifdef __HUGS__
+import Hugs.IOExts
+#endif
+
 #ifdef MIN_VERSION_template_haskell
 import Language.Haskell.TH hiding (reify)
-import Control.Monad
 #endif
 
-#ifdef __HUGS__
-import Hugs.IOExts
-#else
+import System.IO.Unsafe
+
+#ifndef __HUGS__
 import Unsafe.Coerce
 #endif
 
+#if MIN_VERSION_base(4,18,0)
+import qualified GHC.TypeNats as TN
+#endif
+
+-- Due to https://gitlab.haskell.org/ghc/ghc/issues/16893, inlining
+-- unsafeCoerce too aggressively can cause optimization to become unsound on
+-- old versions of GHC. As a workaround, we mark unsafeCoerce-using definitions
+-- as NOINLINE where necessary.
+-- See https://github.com/ekmett/reflection/issues/47.
+#if __GLASGOW_HASKELL__ >= 811
+# define INLINE_UNSAFE_COERCE INLINE
+#else
+# define INLINE_UNSAFE_COERCE NOINLINE
+#endif
+
 ------------------------------------------------------------------------------
 -- Reifies
 ------------------------------------------------------------------------------
@@ -100,16 +159,87 @@
 -- | Reify a value at the type level, to be recovered with 'reflect'.
 reify :: forall a r. a -> (forall (s :: *). Reifies s a => Proxy s -> r) -> r
 reify a k = unsafeCoerce (Magic k :: Magic a r) (const a) Proxy
-{-# INLINE reify #-}
+{-# INLINE_UNSAFE_COERCE reify #-}
 
-#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ >= 707
 instance KnownNat n => Reifies n Integer where
   reflect = natVal
 
 instance KnownSymbol n => Reifies n String where
   reflect = symbolVal
+
+--------------------------------------------------------------------------------
+-- KnownNat
+--------------------------------------------------------------------------------
+
+-- | This upgraded version of 'reify' can be used to generate a 'KnownNat' suitable for use with other APIs.
+--
+-- Attemping to pass a negative 'Integer' as an argument will result in an
+-- 'Underflow' exception.
+--
+-- /Available only on GHC 7.8+/
+--
+-- >>> import GHC.TypeLits
+--
+-- >>> reifyNat 4 natVal
+-- 4
+--
+-- >>> reifyNat 4 reflect
+-- 4
+
+reifyNat :: forall r. Integer -> (forall (n :: Nat). KnownNat n => Proxy n -> r) -> r
+#if MIN_VERSION_base(4,18,0)
+-- With base-4.18 or later, we can use the API in GHC.TypeNats to define this
+-- function directly.
+reifyNat n k = TN.withSomeSNat (fromInteger n :: Numeric.Natural) $
+               \(sn :: (SNat n)) -> TN.withKnownNat sn $ k (Proxy :: Proxy n)
+{-# INLINE reifyNat #-}
+#else
+-- On older versions of base, we resort to unsafeCoerce.
+reifyNat n k = unsafeCoerce (MagicNat k :: MagicNat r)
+# if MIN_VERSION_base(4,10,0)
+                             -- Starting with base-4.10, the internal
+                             -- representation of KnownNat changed from Integer
+                             -- to Natural, so make sure to perform the same
+                             -- conversion before unsafeCoercing.
+                             (fromInteger n :: Numeric.Natural)
+# else
+                             (if n < 0 then throw Underflow else n)
+# endif
+                             Proxy
+{-# INLINE_UNSAFE_COERCE reifyNat #-}
+
+newtype MagicNat r = MagicNat (forall (n :: Nat). KnownNat n => Proxy n -> r)
 #endif
 
+--------------------------------------------------------------------------------
+-- KnownSymbol
+--------------------------------------------------------------------------------
+
+-- | This upgraded version of 'reify' can be used to generate a 'KnownSymbol' suitable for use with other APIs.
+--
+-- /Available only on GHC 7.8+/
+--
+-- >>> import GHC.TypeLits
+--
+-- >>> reifySymbol "hello" symbolVal
+-- "hello"
+--
+-- >>> reifySymbol "hello" reflect
+-- "hello"
+reifySymbol :: forall r. String -> (forall (n :: Symbol). KnownSymbol n => Proxy n -> r) -> r
+#if MIN_VERSION_base(4,18,0)
+-- With base-4.18 or later, we can use the API in GHC.TypeNats to define this
+-- function directly.
+reifySymbol s k = withSomeSSymbol s $ \(ss :: SSymbol s) -> withKnownSymbol ss (k (Proxy :: Proxy s))
+{-# INLINE reifySymbol #-}
+#else
+-- On older versions of base, we resort to unsafeCoerce.
+reifySymbol n k = unsafeCoerce (MagicSymbol k :: MagicSymbol r) n Proxy
+{-# INLINE_UNSAFE_COERCE reifySymbol #-}
+#endif
+
+newtype MagicSymbol r = MagicSymbol (forall (n :: Symbol). KnownSymbol n => Proxy n -> r)
+
 ------------------------------------------------------------------------------
 -- Given
 ------------------------------------------------------------------------------
@@ -126,17 +256,25 @@
 
 -- | Reify a value into an instance to be recovered with 'given'.
 --
--- You should only 'give' a single value for each type. If multiple instances
+-- You should /only/ 'give' a single value for each type. If multiple instances
 -- are in scope, then the behavior is implementation defined.
 give :: forall a r. a -> (Given a => r) -> r
 give a k = unsafeCoerce (Gift k :: Gift a r) a
-{-# INLINE give #-}
+{-# INLINE_UNSAFE_COERCE give #-}
 
-data Z -- 0
-data D  (n :: *) -- 2n
-data SD (n :: *) -- 2n+1
-data PD (n :: *) -- 2n-1
+--------------------------------------------------------------------------------
+-- Explicit Numeric Reflection
+--------------------------------------------------------------------------------
 
+-- | 0
+data Z
+-- | 2/n/
+data D  (n :: *)
+-- | 2/n/ + 1
+data SD (n :: *)
+-- | 2/n/ - 1
+data PD (n :: *)
+
 instance Reifies Z Int where
   reflect _ = 0
   {-# INLINE reflect #-}
@@ -172,9 +310,7 @@
 -- in the \"Functional Pearl: Implicit Configurations\" paper by Oleg Kiselyov and Chung-Chieh Shan.
 --
 -- @instance Num (Q Exp)@ provided in this package allows writing @$(3)@
--- instead of @$(int 3)@. Sometimes the two will produce the same
--- representation (if compiled without the @-DUSE_TYPE_LITS@ preprocessor
--- directive).
+-- instead of @$(int 3)@.
 int :: Int -> TypeQ
 int n = case quotRem n 2 of
   (0, 0) -> conT ''Z
@@ -191,12 +327,6 @@
   | n >= 0 = int n
   | otherwise = error "nat: negative"
 
-#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ < 704
-instance Show (Q a) where
-  show _ = "Q"
-instance Eq (Q a) where
-  _ == _ = False
-#endif
 instance Num a => Num (Q a) where
   (+) = liftM2 (+)
   (*) = liftM2 (*)
@@ -213,29 +343,13 @@
 
 -- | This permits the use of $(5) as a type splice.
 instance Num Type where
-#ifdef USE_TYPE_LITS
   LitT (NumTyLit a) + LitT (NumTyLit b) = LitT (NumTyLit (a+b))
   a + b = AppT (AppT (VarT ''(+)) a) b
 
   LitT (NumTyLit a) * LitT (NumTyLit b) = LitT (NumTyLit (a*b))
-  a * b = AppT (AppT (VarT ''(*)) a) b
-#if MIN_VERSION_base(4,8,0)
+  (*) a b = AppT (AppT (VarT ''(GHC.TypeLits.*)) a) b
   a - b = AppT (AppT (VarT ''(-)) a) b
-#else
-  (-) = error "Type.(-): undefined"
-#endif
   fromInteger = LitT . NumTyLit
-#else
-  (+) = error "Type.(+): undefined"
-  (*) = error "Type.(*): undefined"
-  (-) = error "Type.(-): undefined"
-  fromInteger n = case quotRem n 2 of
-      (0, 0) -> ConT ''Z
-      (q,-1) -> ConT ''PD `AppT` fromInteger q
-      (q, 0) -> ConT ''D  `AppT` fromInteger q
-      (q, 1) -> ConT ''SD `AppT` fromInteger q
-      _ -> error "ghc is bad at math"
-#endif
   abs = error "Type.abs"
   signum = error "Type.signum"
 
@@ -245,7 +359,7 @@
     | proxyName == ''Proxy = ConE 'Proxy `SigE` (ConT ''Proxy `AppT` f ta)
 onProxyType1 f a =
         LamE [SigP WildP na] body `AppE` a
-    where 
+    where
           body = ConE 'Proxy `SigE` (ConT ''Proxy `AppT` f na)
           na = VarT (mkName "na")
 
@@ -270,26 +384,251 @@
   signum = onProxyType1 signum
   fromInteger n = ConE 'Proxy `SigE` (ConT ''Proxy `AppT` fromInteger n)
 
-#ifdef USE_TYPE_LITS
 addProxy :: Proxy a -> Proxy b -> Proxy (a + b)
 addProxy _ _ = Proxy
 mulProxy :: Proxy a -> Proxy b -> Proxy (a * b)
 mulProxy _ _ = Proxy
-#if MIN_VERSION_base(4,8,0)
 subProxy :: Proxy a -> Proxy b -> Proxy (a - b)
 subProxy _ _ = Proxy
-#else
-subProxy :: Proxy a -> Proxy b -> Proxy c
-subProxy _ _ = error "Exp.(-): undefined"
-#endif
---  fromInteger = LitT . NumTyLit
-#else
-addProxy :: Proxy a -> Proxy b -> Proxy c
-addProxy _ _ = error "Exp.(+): undefined"
-mulProxy :: Proxy a -> Proxy b -> Proxy c
-mulProxy _ _ = error "Exp.(*): undefined"
-subProxy :: Proxy a -> Proxy b -> Proxy c
-subProxy _ _ = error "Exp.(-): undefined"
+
 #endif
 
+--------------------------------------------------------------------------------
+-- * Typeable Reflection
+--------------------------------------------------------------------------------
+
+
+class Typeable s => B s where
+  reflectByte :: proxy s -> IntPtr
+
+#define BYTES(GO) \
+  GO(T0,0) GO(T1,1) GO(T2,2) GO(T3,3) GO(T4,4) GO(T5,5) GO(T6,6) GO(T7,7) GO(T8,8) GO(T9,9) GO(T10,10) GO(T11,11) \
+  GO(T12,12) GO(T13,13) GO(T14,14) GO(T15,15) GO(T16,16) GO(T17,17) GO(T18,18) GO(T19,19) GO(T20,20) GO(T21,21) GO(T22,22) \
+  GO(T23,23) GO(T24,24) GO(T25,25) GO(T26,26) GO(T27,27) GO(T28,28) GO(T29,29) GO(T30,30) GO(T31,31) GO(T32,32) GO(T33,33) \
+  GO(T34,34) GO(T35,35) GO(T36,36) GO(T37,37) GO(T38,38) GO(T39,39) GO(T40,40) GO(T41,41) GO(T42,42) GO(T43,43) GO(T44,44) \
+  GO(T45,45) GO(T46,46) GO(T47,47) GO(T48,48) GO(T49,49) GO(T50,50) GO(T51,51) GO(T52,52) GO(T53,53) GO(T54,54) GO(T55,55) \
+  GO(T56,56) GO(T57,57) GO(T58,58) GO(T59,59) GO(T60,60) GO(T61,61) GO(T62,62) GO(T63,63) GO(T64,64) GO(T65,65) GO(T66,66) \
+  GO(T67,67) GO(T68,68) GO(T69,69) GO(T70,70) GO(T71,71) GO(T72,72) GO(T73,73) GO(T74,74) GO(T75,75) GO(T76,76) GO(T77,77) \
+  GO(T78,78) GO(T79,79) GO(T80,80) GO(T81,81) GO(T82,82) GO(T83,83) GO(T84,84) GO(T85,85) GO(T86,86) GO(T87,87) GO(T88,88) \
+  GO(T89,89) GO(T90,90) GO(T91,91) GO(T92,92) GO(T93,93) GO(T94,94) GO(T95,95) GO(T96,96) GO(T97,97) GO(T98,98) GO(T99,99) \
+  GO(T100,100) GO(T101,101) GO(T102,102) GO(T103,103) GO(T104,104) GO(T105,105) GO(T106,106) GO(T107,107) GO(T108,108) \
+  GO(T109,109) GO(T110,110) GO(T111,111) GO(T112,112) GO(T113,113) GO(T114,114) GO(T115,115) GO(T116,116) GO(T117,117) \
+  GO(T118,118) GO(T119,119) GO(T120,120) GO(T121,121) GO(T122,122) GO(T123,123) GO(T124,124) GO(T125,125) GO(T126,126) \
+  GO(T127,127) GO(T128,128) GO(T129,129) GO(T130,130) GO(T131,131) GO(T132,132) GO(T133,133) GO(T134,134) GO(T135,135) \
+  GO(T136,136) GO(T137,137) GO(T138,138) GO(T139,139) GO(T140,140) GO(T141,141) GO(T142,142) GO(T143,143) GO(T144,144) \
+  GO(T145,145) GO(T146,146) GO(T147,147) GO(T148,148) GO(T149,149) GO(T150,150) GO(T151,151) GO(T152,152) GO(T153,153) \
+  GO(T154,154) GO(T155,155) GO(T156,156) GO(T157,157) GO(T158,158) GO(T159,159) GO(T160,160) GO(T161,161) GO(T162,162) \
+  GO(T163,163) GO(T164,164) GO(T165,165) GO(T166,166) GO(T167,167) GO(T168,168) GO(T169,169) GO(T170,170) GO(T171,171) \
+  GO(T172,172) GO(T173,173) GO(T174,174) GO(T175,175) GO(T176,176) GO(T177,177) GO(T178,178) GO(T179,179) GO(T180,180) \
+  GO(T181,181) GO(T182,182) GO(T183,183) GO(T184,184) GO(T185,185) GO(T186,186) GO(T187,187) GO(T188,188) GO(T189,189) \
+  GO(T190,190) GO(T191,191) GO(T192,192) GO(T193,193) GO(T194,194) GO(T195,195) GO(T196,196) GO(T197,197) GO(T198,198) \
+  GO(T199,199) GO(T200,200) GO(T201,201) GO(T202,202) GO(T203,203) GO(T204,204) GO(T205,205) GO(T206,206) GO(T207,207) \
+  GO(T208,208) GO(T209,209) GO(T210,210) GO(T211,211) GO(T212,212) GO(T213,213) GO(T214,214) GO(T215,215) GO(T216,216) \
+  GO(T217,217) GO(T218,218) GO(T219,219) GO(T220,220) GO(T221,221) GO(T222,222) GO(T223,223) GO(T224,224) GO(T225,225) \
+  GO(T226,226) GO(T227,227) GO(T228,228) GO(T229,229) GO(T230,230) GO(T231,231) GO(T232,232) GO(T233,233) GO(T234,234) \
+  GO(T235,235) GO(T236,236) GO(T237,237) GO(T238,238) GO(T239,239) GO(T240,240) GO(T241,241) GO(T242,242) GO(T243,243) \
+  GO(T244,244) GO(T245,245) GO(T246,246) GO(T247,247) GO(T248,248) GO(T249,249) GO(T250,250) GO(T251,251) GO(T252,252) \
+  GO(T253,253) GO(T254,254) GO(T255,255)
+
+#define GO(Tn,n) \
+  newtype Tn = Tn Tn; \
+  instance B Tn where { \
+    reflectByte _ = n \
+  };
+BYTES(GO)
+#undef GO
+
+impossible :: a
+impossible = error "Data.Reflection.reifyByte: impossible"
+
+reifyByte :: Word8 -> (forall (s :: *). B s => Proxy s -> r) -> r
+reifyByte w k = case w of {
+#define GO(Tn,n) n -> k (Proxy :: Proxy Tn);
+BYTES(GO)
+#undef GO
+_ -> impossible
+}
+
+newtype W (b0 :: *) (b1 :: *) (b2 :: *) (b3 :: *) = W (W b0 b1 b2 b3)
+newtype StableBox (w0 :: *) (w1 :: *) (a :: *) = StableBox (StableBox w0 w1 a)
+newtype Stable (w0 :: *) (w1 :: *) (a :: *) = Stable (Stable w0 w1 a)
+
+data Box a = Box a
+
+stableBox :: p (Stable w1 w2 a) -> Proxy (StableBox w1 w2 a)
+stableBox _ = Proxy
+{-# INLINE stableBox #-}
+
+stable :: p b0 -> p b1 -> p b2 -> p b3 -> p b4 -> p b5 -> p b6 -> p b7
+       -> Proxy (Stable (W b0 b1 b2 b3) (W b4 b5 b6 b7) a)
+stable _ _ _ _ _ _ _ _ = Proxy
+{-# INLINE stable #-}
+
+stablePtrToIntPtr :: StablePtr a -> IntPtr
+stablePtrToIntPtr = ptrToIntPtr . castStablePtrToPtr
+{-# INLINE stablePtrToIntPtr #-}
+
+intPtrToStablePtr :: IntPtr -> StablePtr a
+intPtrToStablePtr = castPtrToStablePtr . intPtrToPtr
+{-# INLINE intPtrToStablePtr #-}
+
+byte0 :: p (StableBox (W b0 b1 b2 b3) w1 a) -> Proxy b0
+byte0 _ = Proxy
+
+byte1 :: p (StableBox (W b0 b1 b2 b3) w1 a) -> Proxy b1
+byte1 _ = Proxy
+
+byte2 :: p (StableBox (W b0 b1 b2 b3) w1 a) -> Proxy b2
+byte2 _ = Proxy
+
+byte3 :: p (StableBox (W b0 b1 b2 b3) w1 a) -> Proxy b3
+byte3 _ = Proxy
+
+byte4 :: p (StableBox w0 (W b4 b5 b6 b7) a) -> Proxy b4
+byte4 _ = Proxy
+
+byte5 :: p (StableBox w0 (W b4 b5 b6 b7) a) -> Proxy b5
+byte5 _ = Proxy
+
+byte6 :: p (StableBox w0 (W b4 b5 b6 b7) a) -> Proxy b6
+byte6 _ = Proxy
+
+byte7 :: p (StableBox w0 (W b4 b5 b6 b7) a) -> Proxy b7
+byte7 _ = Proxy
+
+argument :: (p s -> r) -> Proxy s
+argument _ = Proxy
+
+instance (B b0, B b1, B b2, B b3, B b4, B b5, B b6, B b7, w0 ~ W b0 b1 b2 b3, w1 ~ W b4 b5 b6 b7)
+    => Reifies (StableBox w0 w1 a) (Box a) where
+  reflect = r where
+      r = unsafePerformIO $ const <$> deRefStablePtr p <* freeStablePtr p
+      s = argument r
+      p = intPtrToStablePtr $
+        reflectByte (byte0 s) .|.
+        (reflectByte (byte1 s) `shiftL` 8) .|.
+        (reflectByte (byte2 s) `shiftL` 16) .|.
+        (reflectByte (byte3 s) `shiftL` 24) .|.
+        (reflectByte (byte4 s) `shiftL` 32) .|.
+        (reflectByte (byte5 s) `shiftL` 40) .|.
+        (reflectByte (byte6 s) `shiftL` 48) .|.
+        (reflectByte (byte7 s) `shiftL` 56)
+  {-# NOINLINE reflect #-}
+
+instance Reifies (StableBox w0 w1 a) (Box b) => Reifies (Stable w0 w1 a) b where
+  reflect p = case reflect (stableBox p) of
+    Box a -> a
+
+-- Ensure that exactly one dictionary of Reifies (StableBox ...) is created and evaluated per reifyTypeable call.
+--
+-- Evaluating the dictionary's thunk frees the allocated StablePtr, and the contents of the StablePtr replace the thunk.
+-- Creating two dictionaries would mean a double free upon their evaluation, and leaving a dictionary unevaluated would
+-- leak the StablePtr (see https://github.com/ekmett/reflection/issues/54).
+--
+-- To separate evaluation of the dictionary and evaluation of the actual argument passed to reifyTypeable, we insert a
+-- Box in between.
+withStableBox :: Reifies (StableBox w0 w1 a) (Box a) => (Reifies (Stable w0 w1 a) a => Proxy (Stable w0 w1 a) -> r) -> Proxy (Stable w0 w1 a) -> IO r
+withStableBox k p = do
+  _ <- evaluate $ reflect (stableBox p)
+  evaluate $ k p
+{-# NOINLINE withStableBox #-}
+
+-- | Reify a value at the type level in a 'Typeable'-compatible fashion, to be recovered with 'reflect'.
+--
+-- This can be necessary to work around the changes to @Data.Typeable@ in GHC HEAD.
+reifyTypeable :: Typeable a => a -> (forall (s :: *). (Typeable s, Reifies s a) => Proxy s -> r) -> r
+reifyTypeable a k = unsafeDupablePerformIO $ do
+  p <- newStablePtr (Box a)
+  let n = stablePtrToIntPtr p
+  reifyByte (fromIntegral n) (\s0 ->
+    reifyByte (fromIntegral (n `shiftR` 8)) (\s1 ->
+      reifyByte (fromIntegral (n `shiftR` 16)) (\s2 ->
+        reifyByte (fromIntegral (n `shiftR` 24)) (\s3 ->
+          reifyByte (fromIntegral (n `shiftR` 32)) (\s4 ->
+            reifyByte (fromIntegral (n `shiftR` 40)) (\s5 ->
+              reifyByte (fromIntegral (n `shiftR` 48)) (\s6 ->
+                reifyByte (fromIntegral (n `shiftR` 56)) (\s7 ->
+                  withStableBox k $ stable s0 s1 s2 s3 s4 s5 s6 s7))))))))
+
+data ReifiedMonoid a = ReifiedMonoid { reifiedMappend :: a -> a -> a, reifiedMempty :: a }
+
+instance Reifies s (ReifiedMonoid a) => Sem.Semigroup (ReflectedMonoid a s) where
+  ReflectedMonoid x <> ReflectedMonoid y = reflectResult (\m -> ReflectedMonoid (reifiedMappend m x y))
+
+instance Reifies s (ReifiedMonoid a) => Monoid (ReflectedMonoid a s) where
+#if !(MIN_VERSION_base(4,11,0))
+  mappend = (<>)
 #endif
+  mempty = reflectResult (\m -> ReflectedMonoid (reifiedMempty  m    ))
+
+reflectResult :: forall f s a. Reifies s a => (a -> f s) -> f s
+reflectResult f = f (reflect (Proxy :: Proxy s))
+
+newtype ReflectedMonoid a s = ReflectedMonoid a
+
+unreflectedMonoid :: ReflectedMonoid a s -> proxy s -> a
+unreflectedMonoid (ReflectedMonoid a) _ = a
+
+reifyMonoid :: (a -> a -> a) -> a -> (forall (s :: *). Reifies s (ReifiedMonoid a) => t -> ReflectedMonoid a s) -> t -> a
+reifyMonoid f z m xs = reify (ReifiedMonoid f z) (unreflectedMonoid (m xs))
+
+-- | Fold a value using its 'Foldable' instance using
+-- explicitly provided 'Monoid' operations. This is like 'Data.Foldable.fold'
+-- where the 'Monoid' instance can be manually specified.
+--
+-- @
+-- 'foldBy' 'mappend' 'mempty' ≡ 'fold'
+-- @
+--
+-- >>> foldBy (++) [] ["hello","world"]
+-- "helloworld"
+foldBy :: Foldable t => (a -> a -> a) -> a -> t a -> a
+foldBy f z = reifyMonoid f z (foldMap ReflectedMonoid)
+
+-- | Fold a value using its 'Foldable' instance using
+-- explicitly provided 'Monoid' operations. This is like 'foldMap'
+-- where the 'Monoid' instance can be manually specified.
+--
+-- @
+-- 'foldMapBy' 'mappend' 'mempty' ≡ 'foldMap'
+-- @
+--
+-- >>> foldMapBy (+) 0 length ["hello","world"]
+-- 10
+foldMapBy :: Foldable t => (r -> r -> r) -> r -> (a -> r) -> t a -> r
+foldMapBy f z g = reifyMonoid f z (foldMap (ReflectedMonoid #. g))
+
+data ReifiedApplicative f = ReifiedApplicative { reifiedPure :: forall a. a -> f a, reifiedAp :: forall a b. f (a -> b) -> f a -> f b }
+
+newtype ReflectedApplicative f s a = ReflectedApplicative (f a)
+
+instance Reifies s (ReifiedApplicative f) => Functor (ReflectedApplicative f s) where
+  fmap = liftA
+
+instance Reifies s (ReifiedApplicative f) => Applicative (ReflectedApplicative f s) where
+  pure a = reflectResult1 (\m -> ReflectedApplicative (reifiedPure m a))
+  ReflectedApplicative x <*> ReflectedApplicative y = reflectResult1 (\m -> ReflectedApplicative (reifiedAp m x y))
+
+reflectResult1 :: forall f s a b. Reifies s a => (a -> f s b) -> f s b
+reflectResult1 f = f (reflect (Proxy :: Proxy s))
+
+unreflectedApplicative :: ReflectedApplicative f s a -> proxy s -> f a
+unreflectedApplicative (ReflectedApplicative a) _ = a
+
+reifyApplicative :: (forall x. x -> f x) -> (forall x y. f (x -> y) -> f x -> f y) -> (forall (s :: *). Reifies s (ReifiedApplicative f) => t -> ReflectedApplicative f s a) -> t -> f a
+reifyApplicative f g m xs = reify (ReifiedApplicative f g) (unreflectedApplicative (m xs))
+
+-- | Traverse a container using its 'Traversable' instance using
+-- explicitly provided 'Applicative' operations. This is like 'traverse'
+-- where the 'Applicative' instance can be manually specified.
+traverseBy :: Traversable t => (forall x. x -> f x) -> (forall x y. f (x -> y) -> f x -> f y) -> (a -> f b) -> t a -> f (t b)
+traverseBy pur app f = reifyApplicative pur app (traverse (ReflectedApplicative #. f))
+
+-- | Sequence a container using its 'Traversable' instance using
+-- explicitly provided 'Applicative' operations. This is like 'sequence'
+-- where the 'Applicative' instance can be manually specified.
+sequenceBy :: Traversable t => (forall x. x -> f x) -> (forall x y. f (x -> y) -> f x -> f y) -> t (f a) -> f (t a)
+sequenceBy pur app = reifyApplicative pur app (traverse ReflectedApplicative)
+
+(#.) :: Coercible c b => (b -> c) -> (a -> b) -> (a -> c)
+(#.) _ = coerce (\x -> x :: b) :: forall a b. Coercible b a => a -> b
diff --git a/reflection.cabal b/reflection.cabal
--- a/reflection.cabal
+++ b/reflection.cabal
@@ -1,5 +1,5 @@
 name:           reflection
-version:        1.5.2.1
+version:        2.1.9
 license:        BSD3
 license-file:   LICENSE
 author:         Edward A. Kmett, Elliott Hird, Oleg Kiselyov and Chung-chieh Shan
@@ -16,8 +16,8 @@
 cabal-version:  >= 1.10
 description:
   This package addresses the /configuration problem/ which is
-  propogating configurations that are available at run-time, allowing
-  multible configurations to coexist without resorting to mutable
+  propagating configurations that are available at run-time, allowing
+  multiple configurations to coexist without resorting to mutable
   global variables or 'System.IO.Unsafe.unsafePerformIO'.
   .
   That package is an implementation of the ideas presented in the
@@ -25,19 +25,35 @@
   and Chung-chieh Shan (<http://okmij.org/ftp/Haskell/tr-15-04.pdf original paper>).
   However, the API has been streamlined to improve performance.
   .
-  Austin Seipp's tutorial <https://www.fpcomplete.com/user/thoughtpolice/using-reflection Reflecting values to types and back> provides a summary of the
+  There are a handful of tutorials on how to use this library:
+  .
+  * Austin Seipp's tutorial <https://www.schoolofhaskell.com/user/thoughtpolice/using-reflection Reflecting values to types and back> provides a summary of the
   approach taken by this library, along with more motivating examples.
+  .
+  * Arnaud Spiwack's tutorial <https://www.tweag.io/posts/2017-12-21-reflection-tutorial.html All about reflection> explains how to use this library.
+tested-with:   GHC == 8.0.2
+             , GHC == 8.2.2
+             , GHC == 8.4.4
+             , GHC == 8.6.5
+             , GHC == 8.8.4
+             , GHC == 8.10.7
+             , GHC == 9.0.2
+             , GHC == 9.2.8
+             , GHC == 9.4.8
+             , GHC == 9.6.6
+             , GHC == 9.8.4
+             , GHC == 9.10.1
+             , GHC == 9.12.1
 
 extra-source-files:
-  examples/Monoid.hs
-  examples/FromJSON.hs
-  examples/Constraints.hs
-  examples/Benchmark.hs
+  .hlint.yaml
+  examples/reflection-examples.cabal
+  examples/LICENSE
+  examples/*.hs
   CHANGELOG.markdown
   README.markdown
   slow/Data/Reflection.hs
   fast/Data/Reflection.hs
-  .travis.yml
 
 flag slow
   description:
@@ -59,20 +75,18 @@
 
 library
   ghc-options: -Wall
+  if impl(ghc >= 8.6)
+    ghc-options: -Wno-star-is-type
 
-  if impl(ghc >= 7.2)
-    default-extensions: Trustworthy
+  default-extensions: Trustworthy
 
   build-depends:
-    base >= 2 && < 5
-
-  if impl(ghc < 7.8)
-    build-depends:
-      tagged >= 0.4.4 && < 1
+    base >= 4.9 && < 5
 
   default-language: Haskell98
 
   if flag(template-haskell) && impl(ghc)
+    -- other-extensions: TemplateHaskellQuotes -- Hackage doesn't know this extension yet
     build-depends: template-haskell
 
   if !flag(slow) && (impl(ghc) || impl(hugs))
@@ -88,3 +102,19 @@
     CPP
 
   exposed-modules: Data.Reflection
+
+test-suite spec
+  type: exitcode-stdio-1.0
+  hs-source-dirs: tests
+  main-is: Spec.hs
+  other-modules: ReifyNatSpec
+                 T47Spec
+  ghc-options: -Wall
+  default-language: Haskell98
+  build-tool-depends: hspec-discover:hspec-discover >= 1.8
+  build-depends:
+    base       >= 4.9 && < 5,
+    containers >= 0.1 && < 0.8,
+    hspec      >= 2   && < 3,
+    QuickCheck >= 2   && < 3,
+    reflection
diff --git a/slow/Data/Reflection.hs b/slow/Data/Reflection.hs
--- a/slow/Data/Reflection.hs
+++ b/slow/Data/Reflection.hs
@@ -1,12 +1,15 @@
 {-# LANGUAGE CPP #-}
+{-# LANGUAGE MonoLocalBinds #-}
 {-# LANGUAGE MultiParamTypeClasses #-}
 {-# LANGUAGE FunctionalDependencies #-}
+{-# LANGUAGE FlexibleContexts #-}
 {-# LANGUAGE FlexibleInstances #-}
 {-# LANGUAGE Rank2Types #-}
+{-# LANGUAGE UndecidableInstances #-}
 {-# OPTIONS_GHC -fno-cse #-}
 {-# OPTIONS_GHC -fno-full-laziness #-}
 {-# OPTIONS_GHC -fno-float-in #-}
-{-# OPTIONS_GHC -fno-warn-unused-binds #-}
+{-# OPTIONS_GHC -Wno-unused-binds #-}
 ----------------------------------------------------------------------------
 -- |
 -- Module     : Data.Reflection
@@ -49,6 +52,7 @@
 import Foreign.StablePtr
 import System.IO.Unsafe
 import Control.Applicative
+import Control.Exception
 import Data.Proxy
 import Data.Bits
 import Data.Word
@@ -115,9 +119,18 @@
   -- reified type.
   reflect :: proxy s -> a
 
+newtype StableBox b0 b1 b2 b3 b4 b5 b6 b7 a =
+  StableBox (StableBox b0 b1 b2 b3 b4 b5 b6 b7 a)
 newtype Stable b0 b1 b2 b3 b4 b5 b6 b7 a =
   Stable (Stable b0 b1 b2 b3 b4 b5 b6 b7 a)
 
+data Box a = Box a
+
+stableBox :: p (Stable b0 b1 b2 b3 b4 b5 b6 b7 a)
+          -> Proxy (StableBox b0 b1 b2 b3 b4 b5 b6 b7 a)
+stableBox _ = Proxy
+{-# INLINE stableBox #-}
+
 stable :: p b0 -> p b1 -> p b2 -> p b3 -> p b4 -> p b5 -> p b6 -> p b7
        -> Proxy (Stable b0 b1 b2 b3 b4 b5 b6 b7 a)
 stable _ _ _ _ _ _ _ _ = Proxy
@@ -131,35 +144,35 @@
 intPtrToStablePtr = castPtrToStablePtr . intPtrToPtr
 {-# INLINE intPtrToStablePtr #-}
 
-byte0 :: p (Stable b0 b1 b2 b3 b4 b5 b6 b7 a) -> Proxy b0
+byte0 :: p (StableBox b0 b1 b2 b3 b4 b5 b6 b7 a) -> Proxy b0
 byte0 _ = Proxy
 
-byte1 :: p (Stable b0 b1 b2 b3 b4 b5 b6 b7 a) -> Proxy b1
+byte1 :: p (StableBox b0 b1 b2 b3 b4 b5 b6 b7 a) -> Proxy b1
 byte1 _ = Proxy
 
-byte2 :: p (Stable b0 b1 b2 b3 b4 b5 b6 b7 a) -> Proxy b2
+byte2 :: p (StableBox b0 b1 b2 b3 b4 b5 b6 b7 a) -> Proxy b2
 byte2 _ = Proxy
 
-byte3 :: p (Stable b0 b1 b2 b3 b4 b5 b6 b7 a) -> Proxy b3
+byte3 :: p (StableBox b0 b1 b2 b3 b4 b5 b6 b7 a) -> Proxy b3
 byte3 _ = Proxy
 
-byte4 :: p (Stable b0 b1 b2 b3 b4 b5 b6 b7 a) -> Proxy b4
+byte4 :: p (StableBox b0 b1 b2 b3 b4 b5 b6 b7 a) -> Proxy b4
 byte4 _ = Proxy
 
-byte5 :: p (Stable b0 b1 b2 b3 b4 b5 b6 b7 a) -> Proxy b5
+byte5 :: p (StableBox b0 b1 b2 b3 b4 b5 b6 b7 a) -> Proxy b5
 byte5 _ = Proxy
 
-byte6 :: p (Stable b0 b1 b2 b3 b4 b5 b6 b7 a) -> Proxy b6
+byte6 :: p (StableBox b0 b1 b2 b3 b4 b5 b6 b7 a) -> Proxy b6
 byte6 _ = Proxy
 
-byte7 :: p (Stable b0 b1 b2 b3 b4 b5 b6 b7 a) -> Proxy b7
+byte7 :: p (StableBox b0 b1 b2 b3 b4 b5 b6 b7 a) -> Proxy b7
 byte7 _ = Proxy
 
 argument :: (p s -> r) -> Proxy s
 argument _ = Proxy
 
 instance (B b0, B b1, B b2, B b3, B b4, B b5, B b6, B b7)
-    => Reifies (Stable b0 b1 b2 b3 b4 b5 b6 b7 a) a where
+    => Reifies (StableBox b0 b1 b2 b3 b4 b5 b6 b7 a) (Box a) where
   reflect = r where
       r = unsafePerformIO $ const <$> deRefStablePtr p <* freeStablePtr p
       s = argument r
@@ -174,12 +187,34 @@
         (reflectByte (byte7 s) `shiftL` 56)
   {-# NOINLINE reflect #-}
 
--- This had to be moved to the top level, due to an apparent bug in
--- the ghc inliner introduced in ghc 7.0.x
-reflectBefore :: Reifies s a => (Proxy s -> b) -> proxy s -> b
-reflectBefore f = const $! f Proxy
-{-# NOINLINE reflectBefore #-}
+instance Reifies (StableBox b0 b1 b2 b3 b4 b5 b6 b7 a) (Box b)
+    => Reifies (Stable b0 b1 b2 b3 b4 b5 b6 b7 a) b where
+  reflect p = case reflect (stableBox p) of
+    Box a -> a
 
+-- Ensure that exactly one dictionary of Reifies (StableBox ...) is created and
+-- evaluated per reifyTypeable call.
+--
+-- Evaluating the dictionary's thunk frees the allocated StablePtr, and the
+-- contents of the StablePtr replace the thunk. Creating two dictionaries would
+-- mean a double free upon their evaluation, and leaving a dictionary
+-- unevaluated would leak the StablePtr
+-- (see https://github.com/ekmett/reflection/issues/54).
+--
+-- To separate evaluation of the dictionary and evaluation of the actual
+-- argument passed to reifyTypeable, we insert a Box in between.
+withStableBox
+  :: Reifies (StableBox b0 b1 b2 b3 b4 b5 b6 b7 a) (Box a)
+  => (Reifies (Stable b0 b1 b2 b3 b4 b5 b6 b7 a) a
+    => Proxy (Stable b0 b1 b2 b3 b4 b5 b6 b7 a)
+    -> r)
+  -> Proxy (Stable b0 b1 b2 b3 b4 b5 b6 b7 a)
+  -> IO r
+withStableBox k p = do
+  _ <- evaluate $ reflect (stableBox p)
+  evaluate $ k p
+{-# NOINLINE withStableBox #-}
+
 -- | Reify a value at the type level, to be recovered with 'reflect'.
 reify :: a -> (forall s. (Reifies s a) => Proxy s -> r) -> r
 reify a k = unsafeDupablePerformIO $ do
@@ -193,5 +228,4 @@
             reifyByte (fromIntegral (n `shiftR` 40)) (\s5 ->
               reifyByte (fromIntegral (n `shiftR` 48)) (\s6 ->
                 reifyByte (fromIntegral (n `shiftR` 56)) (\s7 ->
-                  reflectBefore (fmap return k) $
-                    stable s0 s1 s2 s3 s4 s5 s6 s7))))))))
+                  withStableBox k $ stable s0 s1 s2 s3 s4 s5 s6 s7))))))))
diff --git a/tests/ReifyNatSpec.hs b/tests/ReifyNatSpec.hs
new file mode 100644
--- /dev/null
+++ b/tests/ReifyNatSpec.hs
@@ -0,0 +1,50 @@
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+module ReifyNatSpec where
+
+import Data.Reflection
+import Test.Hspec.QuickCheck
+import Test.QuickCheck (NonNegative(..))
+
+#if MIN_VERSION_base(4,10,0)
+import GHC.TypeNats (natVal)
+import Numeric.Natural (Natural)
+#endif
+
+#if __GLASGOW_HASKELL__ != 900
+import Control.Exception (ArithException(..), evaluate)
+import Test.QuickCheck (Negative(..))
+#endif
+
+import Test.Hspec
+
+main :: IO ()
+main = hspec spec
+
+spec :: Spec
+spec =
+  describe "reifyNat" $ do
+    prop "reify positive Integers and reflect them back" $
+      \(NonNegative (i :: Integer)) -> reifyNat i $ \p -> reflect p `shouldBe` i
+#if __GLASGOW_HASKELL__ != 900
+    -- Inexplicably, this test fails on GHC 9.0 with hspec-2.8.4 or later.
+    -- Moreover, I suspect that undefined behavior is involved in some way,
+    -- as the output of hspec will occasionally be swallowed entirely. I have
+    -- not been able to observe this phenomenon on any other version of GHC,
+    -- so I will simply disable this test on 9.0.
+    prop "should throw an Underflow exception on negative inputs" $
+      \(Negative (i :: Integer)) ->
+        reifyNat i (evaluate . reflect) `shouldThrow` (== Underflow)
+#endif
+#if MIN_VERSION_base(4,10,0)
+    it "should reflect very large Naturals correctly" $ do -- #41
+      let d42, d2_63, d2_64 :: Natural
+          d42   = 42
+          d2_63 = 2^(63 :: Natural)
+          d2_64 = 2^(64 :: Natural)
+      reifyNat (toInteger d42)       $ \p -> natVal p `shouldBe` d42
+      reifyNat (toInteger (d2_63-1)) $ \p -> natVal p `shouldBe` d2_63-1
+      reifyNat (toInteger d2_63)     $ \p -> natVal p `shouldBe` d2_63
+      reifyNat (toInteger (d2_64-1)) $ \p -> natVal p `shouldBe` d2_64-1
+      reifyNat (toInteger d2_64)     $ \p -> natVal p `shouldBe` d2_64
+#endif
diff --git a/tests/Spec.hs b/tests/Spec.hs
new file mode 100644
--- /dev/null
+++ b/tests/Spec.hs
@@ -0,0 +1,1 @@
+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
diff --git a/tests/T47Spec.hs b/tests/T47Spec.hs
new file mode 100644
--- /dev/null
+++ b/tests/T47Spec.hs
@@ -0,0 +1,64 @@
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE UndecidableInstances #-}
+-- | A regression test for issue #47.
+module T47Spec where
+
+import qualified Data.Map as M
+import Data.Map (Map)
+import Data.Reflection
+import Test.Hspec
+
+main :: IO ()
+main = hspec spec
+
+spec :: Spec
+spec =
+  describe "Given" $ do
+    it "should give Normal properly" $
+      give Normal (toJSON (Foo Bar)) `shouldBe`
+      Object (M.fromList [("Foo",String "Bar")])
+    it "should give ViaShow properly" $
+      give ViaShow (toJSON (Foo Bar)) `shouldBe`
+      Object (M.fromList [("Foo",String "SHOWBAR")])
+
+----------------------------------------------------------------------------
+-- Types
+----------------------------------------------------------------------------
+
+data Foo = Foo Bar
+
+instance Show Foo where
+  show _ = "SHOWFOO"
+
+data Bar = Bar | BarBar
+
+instance Show Bar where
+  show _ = "SHOWBAR"
+
+----------------------------------------------------------------------------
+-- ToJSON instances
+----------------------------------------------------------------------------
+
+instance Given Style => ToJSON Foo where
+  toJSON (Foo x) = Object $ M.singleton "Foo" (toJSON x)
+
+instance Given Style => ToJSON Bar where
+  toJSON x = case given of
+    Normal -> String $ case x of
+                Bar    -> "Bar"
+                BarBar -> "BarBar"
+    ViaShow -> String $ show x
+
+data Style = Normal | ViaShow
+
+----------------------------------------------------------------------------
+-- Minimized aeson
+----------------------------------------------------------------------------
+
+class ToJSON a where
+  toJSON :: a -> Value
+
+data Value
+  = Object !(Map String Value)
+  | String !String
+  deriving (Eq, Show)
