diff --git a/Control/Monad/Primitive.hs b/Control/Monad/Primitive.hs
--- a/Control/Monad/Primitive.hs
+++ b/Control/Monad/Primitive.hs
@@ -1,6 +1,11 @@
 {-# LANGUAGE CPP, MagicHash, UnboxedTuples, TypeFamilies #-}
 {-# LANGUAGE FlexibleContexts, FlexibleInstances, UndecidableInstances #-}
 {-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE DataKinds #-}
+#if __GLASGOW_HASKELL__ < 806
+{-# LANGUAGE TypeInType #-}
+#endif
 {-# OPTIONS_GHC -fno-warn-deprecations #-}
 
 -- |
@@ -11,66 +16,73 @@
 -- Maintainer  : Roman Leshchinskiy <rl@cse.unsw.edu.au>
 -- Portability : non-portable
 --
--- Primitive state-transformer monads
---
+-- Primitive state-transformer monads.
 
 module Control.Monad.Primitive (
   PrimMonad(..), RealWorld, primitive_,
   PrimBase(..),
+  MonadPrim,
+  MonadPrimBase,
   liftPrim, primToPrim, primToIO, primToST, ioToPrim, stToPrim,
   unsafePrimToPrim, unsafePrimToIO, unsafePrimToST, unsafeIOToPrim,
   unsafeSTToPrim, unsafeInlinePrim, unsafeInlineIO, unsafeInlineST,
-  touch, evalPrim
+  touch, touchUnlifted,
+  keepAlive, keepAliveUnlifted,
+  evalPrim, unsafeInterleave, unsafeDupableInterleave, noDuplicate
 ) where
 
-import GHC.Prim   ( State#, RealWorld, touch# )
-import GHC.Base   ( unsafeCoerce#, realWorld# )
-#if MIN_VERSION_base(4,4,0)
-import GHC.Base   ( seq# )
-#else
-import Control.Exception (evaluate)
+import Data.Kind (Type)
+
+import GHC.Exts   ( State#, RealWorld, noDuplicate#, touch#
+                  , unsafeCoerce#, realWorld#, seq# )
+import Data.Primitive.Internal.Operations (UnliftedType)
+#if defined(HAVE_KEEPALIVE)
+import Data.Primitive.Internal.Operations (keepAliveLiftedLifted#,keepAliveUnliftedLifted#)
 #endif
-#if MIN_VERSION_base(4,2,0)
 import GHC.IO     ( IO(..) )
-#else
-import GHC.IOBase ( IO(..) )
-#endif
 import GHC.ST     ( ST(..) )
 
-import Control.Monad.Trans.Class (lift)
-#if !MIN_VERSION_base(4,8,0)
-import Data.Monoid (Monoid)
+#if __GLASGOW_HASKELL__ >= 802
+import qualified Control.Monad.ST.Lazy as L
 #endif
 
+import Control.Monad.Trans.Class (lift)
+
 import Control.Monad.Trans.Cont     ( ContT    )
 import Control.Monad.Trans.Identity ( IdentityT (IdentityT) )
-import Control.Monad.Trans.List     ( ListT    )
 import Control.Monad.Trans.Maybe    ( MaybeT   )
-import Control.Monad.Trans.Error    ( ErrorT, Error)
 import Control.Monad.Trans.Reader   ( ReaderT  )
 import Control.Monad.Trans.State    ( StateT   )
 import Control.Monad.Trans.Writer   ( WriterT  )
 import Control.Monad.Trans.RWS      ( RWST     )
 
-#if MIN_VERSION_transformers(0,4,0)
-import Control.Monad.Trans.Except   ( ExceptT  )
+#if !MIN_VERSION_transformers(0,6,0)
+import Control.Monad.Trans.List     ( ListT    )
+import Control.Monad.Trans.Error    ( ErrorT, Error)
 #endif
 
+import Control.Monad.Trans.Except   ( ExceptT  )
+
 #if MIN_VERSION_transformers(0,5,3)
 import Control.Monad.Trans.Accum    ( AccumT   )
 import Control.Monad.Trans.Select   ( SelectT  )
 #endif
 
+#if MIN_VERSION_transformers(0,5,6)
+import qualified Control.Monad.Trans.Writer.CPS as CPS
+import qualified Control.Monad.Trans.RWS.CPS as CPS
+#endif
+
 import qualified Control.Monad.Trans.RWS.Strict    as Strict ( RWST   )
 import qualified Control.Monad.Trans.State.Strict  as Strict ( StateT )
 import qualified Control.Monad.Trans.Writer.Strict as Strict ( WriterT )
 
--- | Class of monads which can perform primitive state-transformer actions
+-- | Class of monads which can perform primitive state-transformer actions.
 class Monad m => PrimMonad m where
-  -- | State token type
+  -- | State token type.
   type PrimState m
 
-  -- | Execute a primitive operation
+  -- | Execute a primitive operation.
   primitive :: (State# (PrimState m) -> (# State# (PrimState m), a #)) -> m a
 
 -- | Class of primitive monads for state-transformer actions.
@@ -81,10 +93,10 @@
 --
 -- @since 0.6.0.0
 class PrimMonad m => PrimBase m where
-  -- | Expose the internal structure of the monad
+  -- | Expose the internal structure of the monad.
   internal :: m a -> State# (PrimState m) -> (# State# (PrimState m), a #)
 
--- | Execute a primitive operation with no result
+-- | Execute a primitive operation with no result.
 primitive_ :: PrimMonad m
               => (State# (PrimState m) -> State# (PrimState m)) -> m ()
 {-# INLINE primitive_ #-}
@@ -96,6 +108,7 @@
   type PrimState IO = RealWorld
   primitive = IO
   {-# INLINE primitive #-}
+
 instance PrimBase IO where
   internal (IO p) = p
   {-# INLINE internal #-}
@@ -116,18 +129,20 @@
   internal (IdentityT m) = internal m
   {-# INLINE internal #-}
 
+#if !MIN_VERSION_transformers(0,6,0)
 instance PrimMonad m => PrimMonad (ListT m) where
   type PrimState (ListT m) = PrimState m
   primitive = lift . primitive
   {-# INLINE primitive #-}
 
-instance PrimMonad m => PrimMonad (MaybeT m) where
-  type PrimState (MaybeT m) = PrimState m
+instance (Error e, PrimMonad m) => PrimMonad (ErrorT e m) where
+  type PrimState (ErrorT e m) = PrimState m
   primitive = lift . primitive
   {-# INLINE primitive #-}
+#endif
 
-instance (Error e, PrimMonad m) => PrimMonad (ErrorT e m) where
-  type PrimState (ErrorT e m) = PrimState m
+instance PrimMonad m => PrimMonad (MaybeT m) where
+  type PrimState (MaybeT m) = PrimState m
   primitive = lift . primitive
   {-# INLINE primitive #-}
 
@@ -146,29 +161,39 @@
   primitive = lift . primitive
   {-# INLINE primitive #-}
 
+#if MIN_VERSION_transformers(0,5,6)
+instance (Monoid w, PrimMonad m) => PrimMonad (CPS.WriterT w m) where
+  type PrimState (CPS.WriterT w m) = PrimState m
+  primitive = lift . primitive
+  {-# INLINE primitive #-}
+#endif
+
 instance (Monoid w, PrimMonad m) => PrimMonad (RWST r w s m) where
   type PrimState (RWST r w s m) = PrimState m
   primitive = lift . primitive
   {-# INLINE primitive #-}
 
-#if MIN_VERSION_transformers(0,4,0)
+#if MIN_VERSION_transformers(0,5,6)
+instance (Monoid w, PrimMonad m) => PrimMonad (CPS.RWST r w s m) where
+  type PrimState (CPS.RWST r w s m) = PrimState m
+  primitive = lift . primitive
+  {-# INLINE primitive #-}
+#endif
+
 instance PrimMonad m => PrimMonad (ExceptT e m) where
   type PrimState (ExceptT e m) = PrimState m
   primitive = lift . primitive
   {-# INLINE primitive #-}
-#endif
 
 #if MIN_VERSION_transformers(0,5,3)
 -- | @since 0.6.3.0
 instance ( Monoid w
          , PrimMonad m
-# if !(MIN_VERSION_base(4,8,0))
-         , Functor m
-# endif
          ) => PrimMonad (AccumT w m) where
   type PrimState (AccumT w m) = PrimState m
   primitive = lift . primitive
   {-# INLINE primitive #-}
+
 instance PrimMonad m => PrimMonad (SelectT r m) where
   type PrimState (SelectT r m) = PrimState m
   primitive = lift . primitive
@@ -194,10 +219,40 @@
   type PrimState (ST s) = s
   primitive = ST
   {-# INLINE primitive #-}
+
 instance PrimBase (ST s) where
   internal (ST p) = p
   {-# INLINE internal #-}
 
+-- see https://gitlab.haskell.org/ghc/ghc/commit/2f5cb3d44d05e581b75a47fec222577dfa7a533e
+-- for why we only support an instance for ghc >= 8.2
+#if __GLASGOW_HASKELL__ >= 802
+-- @since 0.7.1.0
+instance PrimMonad (L.ST s) where
+  type PrimState (L.ST s) = s
+  primitive = L.strictToLazyST . primitive
+  {-# INLINE primitive #-}
+
+-- @since 0.7.1.0
+instance PrimBase (L.ST s) where
+  internal = internal . L.lazyToStrictST
+  {-# INLINE internal #-}
+#endif
+
+-- | 'PrimMonad'\'s state token type can be annoying to handle
+--   in constraints. This typeclass lets users (visually) notice
+--   'PrimState' equality constraints less, by witnessing that
+--   @s ~ 'PrimState' m@.
+class (PrimMonad m, s ~ PrimState m) => MonadPrim s m
+instance (PrimMonad m, s ~ PrimState m) => MonadPrim s m
+
+-- | 'PrimBase'\'s state token type can be annoying to handle
+--   in constraints. This typeclass lets users (visually) notice
+--   'PrimState' equality constraints less, by witnessing that
+--   @s ~ 'PrimState' m@.
+class (PrimBase m, MonadPrim s m) => MonadPrimBase s m
+instance (PrimBase m, MonadPrim s m) => MonadPrimBase s m
+
 -- | Lifts a 'PrimBase' into another 'PrimMonad' with the same underlying state
 -- token type.
 liftPrim
@@ -222,7 +277,7 @@
 primToST = primToPrim
 
 -- | Convert an 'IO' action to a 'PrimMonad'.
--- 
+--
 -- @since 0.6.2.0
 ioToPrim :: (PrimMonad m, PrimState m ~ RealWorld) => IO a -> m a
 {-# INLINE ioToPrim #-}
@@ -252,9 +307,9 @@
 {-# INLINE unsafePrimToIO #-}
 unsafePrimToIO = unsafePrimToPrim
 
--- | Convert an 'ST' action with an arbitraty state token to any 'PrimMonad'.
+-- | Convert an 'ST' action with an arbitrary state token to any 'PrimMonad'.
 -- This operation is highly unsafe!
--- 
+--
 -- @since 0.6.2.0
 unsafeSTToPrim :: PrimMonad m => ST s a -> m a
 {-# INLINE unsafeSTToPrim #-}
@@ -268,31 +323,91 @@
 {-# INLINE unsafeIOToPrim #-}
 unsafeIOToPrim = unsafePrimToPrim
 
+-- | See 'unsafeInlineIO'. This function is not recommended for the same
+-- reasons.
 unsafeInlinePrim :: PrimBase m => m a -> a
 {-# INLINE unsafeInlinePrim #-}
 unsafeInlinePrim m = unsafeInlineIO (unsafePrimToIO m)
 
+-- | Generally, do not use this function. It is the same as
+-- @accursedUnutterablePerformIO@ from @bytestring@ and is well behaved under
+-- narrow conditions. See the documentation of that function to get an idea
+-- of when this is sound. In most cases @GHC.IO.Unsafe.unsafeDupablePerformIO@
+-- should be preferred.
 unsafeInlineIO :: IO a -> a
 {-# INLINE unsafeInlineIO #-}
 unsafeInlineIO m = case internal m realWorld# of (# _, r #) -> r
 
+-- | See 'unsafeInlineIO'. This function is not recommended for the same
+-- reasons. Prefer @runST@ when @s@ is free.
 unsafeInlineST :: ST s a -> a
 {-# INLINE unsafeInlineST #-}
 unsafeInlineST = unsafeInlinePrim
 
+-- | Ensure that the value is considered alive by the garbage collection.
+-- Warning: GHC has optimization passes that can erase @touch@ if it is
+-- certain that an exception is thrown afterward. Prefer 'keepAlive'.
 touch :: PrimMonad m => a -> m ()
 {-# INLINE touch #-}
 touch x = unsafePrimToPrim
         $ (primitive (\s -> case touch# x s of { s' -> (# s', () #) }) :: IO ())
 
+-- | Variant of 'touch' that keeps a value of an unlifted type
+-- (e.g. @MutableByteArray#@) alive.
+touchUnlifted :: forall (m :: Type -> Type) (a :: UnliftedType). PrimMonad m => a -> m ()
+{-# INLINE touchUnlifted #-}
+touchUnlifted x = unsafePrimToPrim
+        $ (primitive (\s -> case touch# x s of { s' -> (# s', () #) }) :: IO ())
+
+-- | Keep value @x@ alive until computation @k@ completes.
+-- Warning: This primop exists for completeness, but it is difficult to use
+-- correctly. Prefer 'keepAliveUnlifted' if the value to keep alive is simply
+-- a wrapper around an unlifted type (e.g. @ByteArray@).
+keepAlive :: PrimBase m
+  => a -- ^ Value @x@ to keep alive while computation @k@ runs.
+  -> m r -- ^ Computation @k@
+  -> m r
+#if defined(HAVE_KEEPALIVE)
+{-# INLINE keepAlive #-}
+keepAlive x k =
+  primitive $ \s0 -> keepAliveLiftedLifted# x s0 (internal k)
+
+#else
+{-# NOINLINE keepAlive #-}
+keepAlive x k = k <* touch x
+#endif
+
+-- | Variant of 'keepAlive' in which the value kept alive is of an unlifted
+-- boxed type.
+keepAliveUnlifted :: forall (m :: Type -> Type) (a :: UnliftedType) (r :: Type). PrimBase m => a -> m r -> m r
+#if defined(HAVE_KEEPALIVE)
+{-# INLINE keepAliveUnlifted #-}
+keepAliveUnlifted x k =
+  primitive $ \s0 -> keepAliveUnliftedLifted# x s0 (internal k)
+
+#else
+{-# NOINLINE keepAliveUnlifted #-}
+keepAliveUnlifted x k = k <* touchUnlifted x
+#endif
+
 -- | Create an action to force a value; generalizes 'Control.Exception.evaluate'
 --
 -- @since 0.6.2.0
 evalPrim :: forall a m . PrimMonad m => a -> m a
-#if MIN_VERSION_base(4,4,0)
 evalPrim a = primitive (\s -> seq# a s)
+
+noDuplicate :: PrimMonad m => m ()
+#if __GLASGOW_HASKELL__ >= 802
+noDuplicate = primitive $ \ s -> (# noDuplicate# s, () #)
 #else
--- This may or may not work so well, but there's probably nothing better to do.
-{-# NOINLINE evalPrim #-}
-evalPrim a = unsafePrimToPrim (evaluate a :: IO a)
+-- noDuplicate# was limited to RealWorld
+noDuplicate = unsafeIOToPrim $ primitive $ \s -> (# noDuplicate# s, () #)
 #endif
+
+unsafeInterleave, unsafeDupableInterleave :: PrimBase m => m a -> m a
+unsafeInterleave x = unsafeDupableInterleave (noDuplicate >> x)
+unsafeDupableInterleave x = primitive $ \ s -> let r' = case internal x s of (# _, r #) -> r in (# s, r' #)
+{-# INLINE unsafeInterleave #-}
+{-# NOINLINE unsafeDupableInterleave #-}
+-- See Note [unsafeDupableInterleaveIO should not be inlined]
+-- in GHC.IO.Unsafe
diff --git a/Data/Primitive.hs b/Data/Primitive.hs
--- a/Data/Primitive.hs
+++ b/Data/Primitive.hs
@@ -1,5 +1,3 @@
-{-# LANGUAGE MagicHash #-}
-{-# OPTIONS_GHC -fno-warn-duplicate-exports #-}
 -- |
 -- Module      : Data.Primitive
 -- Copyright   : (c) Roman Leshchinskiy 2009-2012
@@ -8,28 +6,24 @@
 -- Maintainer  : Roman Leshchinskiy <rl@cse.unsw.edu.au>
 -- Portability : non-portable
 --
--- Reexports all primitive operations
---
-module Data.Primitive (
-  -- * Re-exports
-  module Data.Primitive.Types
-  ,module Data.Primitive.Array
-  ,module Data.Primitive.ByteArray
-  ,module Data.Primitive.Addr
-  ,module Data.Primitive.SmallArray
-  ,module Data.Primitive.UnliftedArray
-  ,module Data.Primitive.PrimArray
-  ,module Data.Primitive.MutVar
+-- Reexports all primitive operations.
+
+module Data.Primitive
+  ( -- * Re-exports
+    module Data.Primitive.Types
+  , module Data.Primitive.Array
+  , module Data.Primitive.ByteArray
+  , module Data.Primitive.SmallArray
+  , module Data.Primitive.PrimArray
+  , module Data.Primitive.MutVar
   -- * Naming Conventions
   -- $namingConventions
-) where
+  ) where
 
 import Data.Primitive.Types
 import Data.Primitive.Array
 import Data.Primitive.ByteArray
-import Data.Primitive.Addr
 import Data.Primitive.SmallArray
-import Data.Primitive.UnliftedArray
 import Data.Primitive.PrimArray
 import Data.Primitive.MutVar
 
@@ -43,21 +37,22 @@
 > indexPrimArray  :: Prim a => PrimArray  a -> Int -> a
 
 In a few places, where the language sounds more natural, the array type
-is instead used as a prefix. For example, @Data.Primitive.SmallArray@
-exports @smallArrayFromList@, which would sound unnatural if it used
+is instead used as a prefix. For example, "Data.Primitive.SmallArray"
+exports 'smallArrayFromList', which would sound unnatural if it used
 @SmallArray@ as a suffix instead.
 
-This library provides several functions traversing, building, and filtering
+This library provides several functions for traversing, building, and filtering
 arrays. These functions are suffixed with an additional character to
-indicate their the nature of their effectfulness:
+indicate the nature of their effectfulness:
 
 * No suffix: A non-effectful pass over the array.
-* @-A@ suffix: An effectful pass over the array, where the effect is 'Applicative'.
-* @-P@ suffix: An effectful pass over the array, where the effect is 'PrimMonad'.
+* @A@ suffix: An effectful pass over the array, where the effect is 'Applicative'.
+* @P@ suffix: An effectful pass over the array, where the effect is 'Control.Monad.Primitive.PrimMonad'.
 
 Additionally, an apostrophe can be used to indicate strictness in the elements.
-The variants with an apostrophe are used in @Data.Primitive.Array@ but not
-in @Data.Primitive.PrimArray@ since the array type it provides is always strict in the element.
+The variants with an apostrophe are used in "Data.Primitive.Array" but not
+in "Data.Primitive.PrimArray" since the array type it provides is always strict in the element anyway.
+
 For example, there are three variants of the function that filters elements
 from a primitive array.
 
@@ -65,14 +60,19 @@
 > filterPrimArrayA :: (Prim a, Applicative f) => (a -> f Bool) -> PrimArray a -> f (PrimArray a)
 > filterPrimArrayP :: (Prim a, PrimMonad   m) => (a -> m Bool) -> PrimArray a -> m (PrimArray a)
 
-As long as the effectful context is a monad that is sufficiently affine
-the behaviors of the 'Applicative' and 'PrimMonad' variants produce the same results
-and differ only in their strictness. Monads that are sufficiently affine
-include:
+As long as the effectful context is a monad that is sufficiently affine,
+the behaviors of the 'Applicative' and 'Control.Monad.Primitive.PrimMonad'
+variants produce the same results and differ only in their strictness.
+Monads that are sufficiently affine include:
 
-* 'IO' and 'ST'
-* Any combination of 'MaybeT', 'ExceptT', 'StateT' and 'Writer' on top
-  of another sufficiently affine monad.
+* 'IO' and 'Control.Monad.ST'
+* Any combination of 'Control.Monad.Trans.Maybe.MaybeT',
+  'Control.Monad.Trans.Except.ExceptT', 'Control.Monad.Trans.State.Lazy.StateT'
+  and 'Control.Monad.Trans.Writer.Lazy.WriterT' on top of another sufficiently
+  affine monad.
+* Any Monad which does not include backtracking or other mechanisms where an effect can
+  happen more than once is an affine Monad in the sense we care about. @ContT@, @LogicT@, @ListT@ are all
+  examples of search/control monads which are NOT affine: they can run a sub computation more than once.
 
 There is one situation where the names deviate from effectful suffix convention
 described above. Throughout the haskell ecosystem, the 'Applicative' variant of
diff --git a/Data/Primitive/Addr.hs b/Data/Primitive/Addr.hs
deleted file mode 100644
--- a/Data/Primitive/Addr.hs
+++ /dev/null
@@ -1,133 +0,0 @@
-{-# LANGUAGE MagicHash, UnboxedTuples, CPP #-}
-
--- |
--- Module      : Data.Primitive.Addr
--- Copyright   : (c) Roman Leshchinskiy 2009-2012
--- License     : BSD-style
---
--- Maintainer  : Roman Leshchinskiy <rl@cse.unsw.edu.au>
--- Portability : non-portable
---
--- Primitive operations on machine addresses
---
-
-module Data.Primitive.Addr (
-  -- * Types
-  Addr(..),
-
-  -- * Address arithmetic
-  nullAddr, plusAddr, minusAddr, remAddr,
-
-  -- * Element access
-  indexOffAddr, readOffAddr, writeOffAddr,
-
-  -- * Block operations
-  copyAddr,
-#if __GLASGOW_HASKELL__ >= 708
-  copyAddrToByteArray,
-#endif
-  moveAddr, setAddr,
-
-  -- * Conversion
-  addrToInt
-) where
-
-import Control.Monad.Primitive
-import Data.Primitive.Types
-#if __GLASGOW_HASKELL__ >= 708
-import Data.Primitive.ByteArray
-#endif
-
-import GHC.Base ( Int(..) )
-import GHC.Prim
-
-import GHC.Ptr
-import Foreign.Marshal.Utils
-
-
--- | The null address
-nullAddr :: Addr
-nullAddr = Addr nullAddr#
-
-infixl 6 `plusAddr`, `minusAddr`
-infixl 7 `remAddr`
-
--- | Offset an address by the given number of bytes
-plusAddr :: Addr -> Int -> Addr
-plusAddr (Addr a#) (I# i#) = Addr (plusAddr# a# i#)
-
--- | Distance in bytes between two addresses. The result is only valid if the
--- difference fits in an 'Int'.
-minusAddr :: Addr -> Addr -> Int
-minusAddr (Addr a#) (Addr b#) = I# (minusAddr# a# b#)
-
--- | The remainder of the address and the integer.
-remAddr :: Addr -> Int -> Int
-remAddr (Addr a#) (I# i#) = I# (remAddr# a# i#)
-
--- | Read a value from a memory position given by an address and an offset.
--- The memory block the address refers to must be immutable. The offset is in
--- elements of type @a@ rather than in bytes.
-indexOffAddr :: Prim a => Addr -> Int -> a
-{-# INLINE indexOffAddr #-}
-indexOffAddr (Addr addr#) (I# i#) = indexOffAddr# addr# i#
-
--- | Read a value from a memory position given by an address and an offset.
--- The offset is in elements of type @a@ rather than in bytes.
-readOffAddr :: (Prim a, PrimMonad m) => Addr -> Int -> m a
-{-# INLINE readOffAddr #-}
-readOffAddr (Addr addr#) (I# i#) = primitive (readOffAddr# addr# i#)
-
--- | Write a value to a memory position given by an address and an offset.
--- The offset is in elements of type @a@ rather than in bytes.
-writeOffAddr :: (Prim a, PrimMonad m) => Addr -> Int -> a -> m ()
-{-# INLINE writeOffAddr #-}
-writeOffAddr (Addr addr#) (I# i#) x = primitive_ (writeOffAddr# addr# i# x)
-
--- | Copy the given number of bytes from the second 'Addr' to the first. The
--- areas may not overlap.
-copyAddr :: PrimMonad m => Addr         -- ^ destination address
-                        -> Addr         -- ^ source address
-                        -> Int          -- ^ number of bytes
-                        -> m ()
-{-# INLINE copyAddr #-}
-copyAddr (Addr dst#) (Addr src#) n
-  = unsafePrimToPrim $ copyBytes (Ptr dst#) (Ptr src#) n
-
-#if __GLASGOW_HASKELL__ >= 708
--- | Copy the given number of bytes from the 'Addr' to the 'MutableByteArray'.
---   The areas may not overlap. This function is only available when compiling
---   with GHC 7.8 or newer.
---   
---   @since 0.6.4.0
-copyAddrToByteArray :: PrimMonad m
-  => MutableByteArray (PrimState m) -- ^ destination
-  -> Int -- ^ offset into the destination array
-  -> Addr -- ^ source
-  -> Int -- ^ number of bytes to copy
-  -> m ()
-{-# INLINE copyAddrToByteArray #-}
-copyAddrToByteArray (MutableByteArray marr) (I# off) (Addr addr) (I# len) =
-  primitive_ $ copyAddrToByteArray# addr marr off len
-#endif
-
--- | Copy the given number of bytes from the second 'Addr' to the first. The
--- areas may overlap.
-moveAddr :: PrimMonad m => Addr         -- ^ destination address
-                        -> Addr         -- ^ source address
-                        -> Int          -- ^ number of bytes
-                        -> m ()
-{-# INLINE moveAddr #-}
-moveAddr (Addr dst#) (Addr src#) n
-  = unsafePrimToPrim $ moveBytes (Ptr dst#) (Ptr src#) n
-
--- | Fill a memory block of with the given value. The length is in
--- elements of type @a@ rather than in bytes.
-setAddr :: (Prim a, PrimMonad m) => Addr -> Int -> a -> m ()
-{-# INLINE setAddr #-}
-setAddr (Addr addr#) (I# n#) x = primitive_ (setOffAddr# addr# 0# n# x)
-
--- | Convert an 'Addr' to an 'Int'.
-addrToInt :: Addr -> Int
-{-# INLINE addrToInt #-}
-addrToInt (Addr addr#) = I# (addr2Int# addr#)
diff --git a/Data/Primitive/Array.hs b/Data/Primitive/Array.hs
--- a/Data/Primitive/Array.hs
+++ b/Data/Primitive/Array.hs
@@ -1,6 +1,7 @@
 {-# LANGUAGE CPP, MagicHash, UnboxedTuples, DeriveDataTypeable, BangPatterns #-}
 {-# LANGUAGE RankNTypes #-}
 {-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE TemplateHaskellQuotes #-}
 
 -- |
 -- Module      : Data.Primitive.Array
@@ -11,89 +12,117 @@
 -- Portability : non-portable
 --
 -- Primitive arrays of boxed values.
---
 
 module Data.Primitive.Array (
   Array(..), MutableArray(..),
 
   newArray, readArray, writeArray, indexArray, indexArrayM, indexArray##,
-  freezeArray, thawArray, runArray,
+  freezeArray, thawArray, runArray, createArray,
   unsafeFreezeArray, unsafeThawArray, sameMutableArray,
   copyArray, copyMutableArray,
   cloneArray, cloneMutableArray,
   sizeofArray, sizeofMutableArray,
-  fromListN, fromList,
+  emptyArray,
+  arrayFromListN, arrayFromList,
   mapArray',
   traverseArrayP
 ) where
 
+import Control.DeepSeq
 import Control.Monad.Primitive
+import Data.Primitive.Internal.Read (Tag(..),lexTag)
 
-import GHC.Base  ( Int(..) )
-import GHC.Prim
+import GHC.Exts hiding (toList)
 import qualified GHC.Exts as Exts
-#if (MIN_VERSION_base(4,7,0))
-import GHC.Exts (fromListN, fromList)
-#endif
 
 import Data.Typeable ( Typeable )
 import Data.Data
-  (Data(..), DataType, mkDataType, Constr, mkConstr, Fixity(..), constrIndex)
-import Data.Primitive.Internal.Compat ( isTrue#, mkNoRepType )
+  (Data(..), DataType, mkDataType, mkNoRepType, Constr, mkConstr, Fixity(..), constrIndex)
 
-import Control.Monad.ST(ST,runST)
+import Control.Monad.ST (ST, runST)
 
 import Control.Applicative
-import Control.Monad (MonadPlus(..), when)
+import Control.Monad (MonadPlus(..), when, liftM2)
+import qualified Control.Monad.Fail as Fail
 import Control.Monad.Fix
-#if MIN_VERSION_base(4,4,0)
+import qualified Data.Foldable as Foldable
 import Control.Monad.Zip
-#endif
 import Data.Foldable (Foldable(..), toList)
-#if !(MIN_VERSION_base(4,8,0))
-import Data.Traversable (Traversable(..))
-import Data.Monoid
-#endif
-#if MIN_VERSION_base(4,9,0)
 import qualified GHC.ST as GHCST
 import qualified Data.Foldable as F
 import Data.Semigroup
-#endif
-#if MIN_VERSION_base(4,8,0)
 import Data.Functor.Identity
-#endif
-#if MIN_VERSION_base(4,10,0)
-import GHC.Exts (runRW#)
-#elif MIN_VERSION_base(4,9,0)
-import GHC.Base (runRW#)
-#endif
 
+import Text.Read (Read (..), parens, prec)
+import Text.ParserCombinators.ReadPrec (ReadPrec)
+import qualified Text.ParserCombinators.ReadPrec as RdPrc
 import Text.ParserCombinators.ReadP
 
-#if MIN_VERSION_base(4,9,0) || MIN_VERSION_transformers(0,4,0)
-import Data.Functor.Classes (Eq1(..),Ord1(..),Show1(..),Read1(..))
-#endif
+import Data.Functor.Classes (Eq1(..), Ord1(..), Show1(..), Read1(..))
+import Language.Haskell.TH.Syntax (Lift (..))
 
--- | Boxed arrays
+-- | Boxed arrays.
 data Array a = Array
   { array# :: Array# a }
   deriving ( Typeable )
 
+instance Lift a => Lift (Array a) where
+#if MIN_VERSION_template_haskell(2,16,0)
+  liftTyped ary = case lst of
+    [] -> [|| Array (emptyArray# (##)) ||]
+    [x] -> [|| pure $! x ||]
+    x : xs -> [|| unsafeArrayFromListN' len x xs ||]
+#else
+  lift ary = case lst of
+    [] -> [| Array (emptyArray# (##)) |]
+    [x] -> [| pure $! x |]
+    x : xs -> [| unsafeArrayFromListN' len x xs |]
+#endif
+    where
+      len = length ary
+      lst = toList ary
+
+-- | Strictly create an array from a nonempty list (represented as
+-- a first element and a list of the rest) of a known length. If the length
+-- of the list does not match the given length, this makes demons fly
+-- out of your nose. We use it in the 'Lift' instance. If you edit the
+-- splice and break it, you get to keep both pieces.
+unsafeArrayFromListN' :: Int -> a -> [a] -> Array a
+unsafeArrayFromListN' n y ys =
+  createArray n y $ \ma ->
+    let go !_ix [] = return ()
+        go !ix (!x : xs) = do
+            writeArray ma ix x
+            go (ix+1) xs
+    in go 1 ys
+
+#if MIN_VERSION_deepseq(1,4,3)
+instance NFData1 Array where
+  liftRnf r = Foldable.foldl' (\_ -> r) ()
+#endif
+
+instance NFData a => NFData (Array a) where
+  rnf = Foldable.foldl' (\_ -> rnf) ()
+
 -- | Mutable boxed arrays associated with a primitive state token.
 data MutableArray s a = MutableArray
   { marray# :: MutableArray# s a }
   deriving ( Typeable )
 
+-- | The number of elements in an immutable array.
 sizeofArray :: Array a -> Int
 sizeofArray a = I# (sizeofArray# (array# a))
 {-# INLINE sizeofArray #-}
 
+-- | The number of elements in a mutable array.
 sizeofMutableArray :: MutableArray s a -> Int
 sizeofMutableArray a = I# (sizeofMutableArray# (marray# a))
 {-# INLINE sizeofMutableArray #-}
 
 -- | Create a new mutable array of the specified size and initialise all
 -- elements with the given value.
+--
+-- /Note:/ this function does not check if the input is non-negative.
 newArray :: PrimMonad m => Int -> a -> m (MutableArray (PrimState m) a)
 {-# INLINE newArray #-}
 newArray (I# n#) x = primitive
@@ -103,16 +132,22 @@
                in (# s'# , ma #))
 
 -- | Read a value from the array at the given index.
+--
+-- /Note:/ this function does not do bounds checking.
 readArray :: PrimMonad m => MutableArray (PrimState m) a -> Int -> m a
 {-# INLINE readArray #-}
 readArray arr (I# i#) = primitive (readArray# (marray# arr) i#)
 
 -- | Write a value to the array at the given index.
+--
+-- /Note:/ this function does not do bounds checking.
 writeArray :: PrimMonad m => MutableArray (PrimState m) a -> Int -> a -> m ()
 {-# INLINE writeArray #-}
 writeArray arr (I# i#) x = primitive_ (writeArray# (marray# arr) i# x)
 
 -- | Read a value from the immutable array at the given index.
+--
+-- /Note:/ this function does not do bounds checking.
 indexArray :: Array a -> Int -> a
 {-# INLINE indexArray #-}
 indexArray arr (I# i#) = case indexArray# (array# arr) i# of (# x #) -> x
@@ -120,11 +155,13 @@
 -- | Read a value from the immutable array at the given index, returning
 -- the result in an unboxed unary tuple. This is currently used to implement
 -- folds.
+--
+-- /Note:/ this function does not do bounds checking.
 indexArray## :: Array a -> Int -> (# a #)
 indexArray## arr (I# i) = indexArray# (array# arr) i
 {-# INLINE indexArray## #-}
 
--- | Monadically read a value from the immutable array at the given index.
+-- | Read a value from the immutable array at the given index using an applicative.
 -- This allows us to be strict in the array while remaining lazy in the read
 -- element which is very useful for collective operations. Suppose we want to
 -- copy an array. We could do something like this:
@@ -133,7 +170,7 @@
 -- >                        writeArray marr i (indexArray arr i) ...
 -- >                        ...
 --
--- But since primitive arrays are lazy, the calls to 'indexArray' will not be
+-- But since the arrays are lazy, the calls to 'indexArray' will not be
 -- evaluated. Rather, @marr@ will be filled with thunks each of which would
 -- retain a reference to @arr@. This is definitely not what we want!
 --
@@ -147,15 +184,19 @@
 -- Now, indexing is executed immediately although the returned element is
 -- still not evaluated.
 --
-indexArrayM :: Monad m => Array a -> Int -> m a
+-- /Note:/ this function does not do bounds checking.
+indexArrayM :: Applicative m => Array a -> Int -> m a
 {-# INLINE indexArrayM #-}
 indexArrayM arr (I# i#)
-  = case indexArray# (array# arr) i# of (# x #) -> return x
+  = case indexArray# (array# arr) i# of (# x #) -> pure x
 
 -- | Create an immutable copy of a slice of an array.
 --
 -- This operation makes a copy of the specified section, so it is safe to
 -- continue using the mutable array afterward.
+--
+-- /Note:/ The provided array should contain the full subrange
+-- specified by the two Ints, but this is not checked.
 freezeArray
   :: PrimMonad m
   => MutableArray (PrimState m) a -- ^ source
@@ -181,6 +222,9 @@
 --
 -- This operation makes a copy of the specified slice, so it is safe to use the
 -- immutable array afterward.
+--
+-- /Note:/ The provided array should contain the full subrange
+-- specified by the two Ints, but this is not checked.
 thawArray
   :: PrimMonad m
   => Array a -- ^ source
@@ -209,6 +253,8 @@
   = isTrue# (sameMutableArray# (marray# arr) (marray# brr))
 
 -- | Copy a slice of an immutable array to a mutable array.
+--
+-- /Note:/ this function does not do bounds or overlap checking.
 copyArray :: PrimMonad m
           => MutableArray (PrimState m) a    -- ^ destination array
           -> Int                             -- ^ offset into destination array
@@ -217,22 +263,12 @@
           -> Int                             -- ^ number of elements to copy
           -> m ()
 {-# INLINE copyArray #-}
-#if __GLASGOW_HASKELL__ > 706
--- NOTE: copyArray# and copyMutableArray# are slightly broken in GHC 7.6.* and earlier
 copyArray (MutableArray dst#) (I# doff#) (Array src#) (I# soff#) (I# len#)
   = primitive_ (copyArray# src# soff# dst# doff# len#)
-#else
-copyArray !dst !doff !src !soff !len = go 0
-  where
-    go i | i < len = do
-                       x <- indexArrayM src (soff+i)
-                       writeArray dst (doff+i) x
-                       go (i+1)
-         | otherwise = return ()
-#endif
 
--- | Copy a slice of a mutable array to another array. The two arrays may
--- not be the same.
+-- | Copy a slice of a mutable array to another array. The two arrays may overlap.
+--
+-- /Note:/ this function does not do bounds or overlap checking.
 copyMutableArray :: PrimMonad m
           => MutableArray (PrimState m) a    -- ^ destination array
           -> Int                             -- ^ offset into destination array
@@ -241,23 +277,14 @@
           -> Int                             -- ^ number of elements to copy
           -> m ()
 {-# INLINE copyMutableArray #-}
-#if __GLASGOW_HASKELL__ >= 706
--- NOTE: copyArray# and copyMutableArray# are slightly broken in GHC 7.6.* and earlier
 copyMutableArray (MutableArray dst#) (I# doff#)
                  (MutableArray src#) (I# soff#) (I# len#)
   = primitive_ (copyMutableArray# src# soff# dst# doff# len#)
-#else
-copyMutableArray !dst !doff !src !soff !len = go 0
-  where
-    go i | i < len = do
-                       x <- readArray src (soff+i)
-                       writeArray dst (doff+i) x
-                       go (i+1)
-         | otherwise = return ()
-#endif
 
--- | Return a newly allocated Array with the specified subrange of the
--- provided Array. The provided Array should contain the full subrange
+-- | Return a newly allocated 'Array' with the specified subrange of the
+-- provided 'Array'.
+--
+-- /Note:/ The provided array should contain the full subrange
 -- specified by the two Ints, but this is not checked.
 cloneArray :: Array a -- ^ source array
            -> Int     -- ^ offset into destination array
@@ -267,9 +294,12 @@
 cloneArray (Array arr#) (I# off#) (I# len#)
   = case cloneArray# arr# off# len# of arr'# -> Array arr'#
 
--- | Return a newly allocated MutableArray. with the specified subrange of
--- the provided MutableArray. The provided MutableArray should contain the
+-- | Return a newly allocated 'MutableArray'. with the specified subrange of
+-- the provided 'MutableArray'. The provided 'MutableArray' should contain the
 -- full subrange specified by the two Ints, but this is not checked.
+--
+-- /Note:/ The provided array should contain the full subrange
+-- specified by the two Ints, but this is not checked.
 cloneMutableArray :: PrimMonad m
         => MutableArray (PrimState m) a -- ^ source array
         -> Int                          -- ^ offset into destination array
@@ -280,48 +310,15 @@
    (\s# -> case cloneMutableArray# arr# off# len# s# of
              (# s'#, arr'# #) -> (# s'#, MutableArray arr'# #))
 
+-- | The empty 'Array'.
 emptyArray :: Array a
 emptyArray =
   runST $ newArray 0 (die "emptyArray" "impossible") >>= unsafeFreezeArray
 {-# NOINLINE emptyArray #-}
 
-#if !MIN_VERSION_base(4,9,0)
-createArray
-  :: Int
-  -> a
-  -> (forall s. MutableArray s a -> ST s ())
-  -> Array a
-createArray 0 _ _ = emptyArray
-createArray n x f = runArray $ do
-  mary <- newArray n x
-  f mary
-  pure mary
-
-runArray
-  :: (forall s. ST s (MutableArray s a))
-  -> Array a
-runArray m = runST $ m >>= unsafeFreezeArray
-
-#else /* Below, runRW# is available. */
-
--- This low-level business is designed to work with GHC's worker-wrapper
--- transformation. A lot of the time, we don't actually need an Array
--- constructor. By putting it on the outside, and being careful about
--- how we special-case the empty array, we can make GHC smarter about this.
--- The only downside is that separately created 0-length arrays won't share
--- their Array constructors, although they'll share their underlying
--- Array#s.
-createArray
-  :: Int
-  -> a
-  -> (forall s. MutableArray s a -> ST s ())
-  -> Array a
-createArray 0 _ _ = Array (emptyArray# (# #))
-createArray n x f = runArray $ do
-  mary <- newArray n x
-  f mary
-  pure mary
-
+-- | Execute the monadic action and freeze the resulting array.
+--
+-- > runArray m = runST $ m >>= unsafeFreezeArray
 runArray
   :: (forall s. ST s (MutableArray s a))
   -> Array a
@@ -340,9 +337,35 @@
 emptyArray# :: (# #) -> Array# a
 emptyArray# _ = case emptyArray of Array ar -> ar
 {-# NOINLINE emptyArray# #-}
-#endif
 
+-- | Create an array of the given size with a default value,
+-- apply the monadic function and freeze the result. If the
+-- size is 0, return 'emptyArray' (rather than a new copy thereof).
+--
+-- > createArray 0 _ _ = emptyArray
+-- > createArray n x f = runArray $ do
+-- >   mary <- newArray n x
+-- >   f mary
+-- >   pure mary
+createArray
+  :: Int
+  -> a
+  -> (forall s. MutableArray s a -> ST s ())
+  -> Array a
+-- This low-level business is designed to work with GHC's worker-wrapper
+-- transformation. A lot of the time, we don't actually need an Array
+-- constructor. By putting it on the outside, and being careful about
+-- how we special-case the empty array, we can make GHC smarter about this.
+-- The only downside is that separately created 0-length arrays won't share
+-- their Array constructors, although they'll share their underlying
+-- Array#s.
+createArray 0 _ _ = Array (emptyArray# (# #))
+createArray n x f = runArray $ do
+  mary <- newArray n x
+  f mary
+  pure mary
 
+
 die :: String -> String -> a
 die fun problem = error $ "Data.Primitive.Array." ++ fun ++ ": " ++ problem
 
@@ -351,20 +374,14 @@
   where loop i | i < 0     = True
                | (# x1 #) <- indexArray## a1 i
                , (# x2 #) <- indexArray## a2 i
-               , otherwise = p x1 x2 && loop (i-1)
+               , otherwise = p x1 x2 && loop (i - 1)
 
 instance Eq a => Eq (Array a) where
   a1 == a2 = arrayLiftEq (==) a1 a2
 
-#if MIN_VERSION_base(4,9,0) || MIN_VERSION_transformers(0,4,0)
 -- | @since 0.6.4.0
 instance Eq1 Array where
-#if MIN_VERSION_base(4,9,0) || MIN_VERSION_transformers(0,5,0)
   liftEq = arrayLiftEq
-#else
-  eq1 = arrayLiftEq (==)
-#endif
-#endif
 
 instance Eq (MutableArray s a) where
   ma1 == ma2 = isTrue# (sameMutableArray# (marray# ma1) (marray# ma2))
@@ -377,22 +394,16 @@
     | i < mn
     , (# x1 #) <- indexArray## a1 i
     , (# x2 #) <- indexArray## a2 i
-    = elemCompare x1 x2 `mappend` loop (i+1)
+    = elemCompare x1 x2 `mappend` loop (i + 1)
     | otherwise = compare (sizeofArray a1) (sizeofArray a2)
 
 -- | Lexicographic ordering. Subject to change between major versions.
 instance Ord a => Ord (Array a) where
   compare a1 a2 = arrayLiftCompare compare a1 a2
 
-#if MIN_VERSION_base(4,9,0) || MIN_VERSION_transformers(0,4,0)
 -- | @since 0.6.4.0
 instance Ord1 Array where
-#if MIN_VERSION_base(4,9,0) || MIN_VERSION_transformers(0,5,0)
   liftCompare = arrayLiftCompare
-#else
-  compare1 = arrayLiftCompare compare
-#endif
-#endif
 
 instance Foldable Array where
   -- Note: we perform the array lookups eagerly so we won't
@@ -404,7 +415,7 @@
       go i
         | i == sz = z
         | (# x #) <- indexArray## ary i
-        = f x (go (i+1))
+        = f x (go (i + 1))
     in go 0
   {-# INLINE foldr #-}
   foldl f = \z !ary ->
@@ -412,7 +423,7 @@
       go i
         | i < 0 = z
         | (# x #) <- indexArray## ary i
-        = f (go (i-1)) x
+        = f (go (i - 1)) x
     in go (sizeofArray ary - 1)
   {-# INLINE foldl #-}
   foldr1 f = \ !ary ->
@@ -421,7 +432,7 @@
       go i =
         case indexArray## ary i of
           (# x #) | i == sz -> x
-                  | otherwise -> f x (go (i+1))
+                  | otherwise -> f x (go (i + 1))
     in if sz < 0
        then die "foldr1" "empty array"
        else go 0
@@ -437,13 +448,12 @@
        then die "foldl1" "empty array"
        else go sz
   {-# INLINE foldl1 #-}
-#if MIN_VERSION_base(4,6,0)
   foldr' f = \z !ary ->
     let
       go i !acc
         | i == -1 = acc
         | (# x #) <- indexArray## ary i
-        = go (i-1) (f x acc)
+        = go (i - 1) (f x acc)
     in go (sizeofArray ary - 1) z
   {-# INLINE foldr' #-}
   foldl' f = \z !ary ->
@@ -452,11 +462,9 @@
       go i !acc
         | i == sz = acc
         | (# x #) <- indexArray## ary i
-        = go (i+1) (f acc x)
+        = go (i + 1) (f acc x)
     in go 0 z
   {-# INLINE foldl' #-}
-#endif
-#if MIN_VERSION_base(4,8,0)
   null a = sizeofArray a == 0
   {-# INLINE null #-}
   length = sizeofArray
@@ -469,7 +477,7 @@
      go i !e
        | i == sz = e
        | (# x #) <- indexArray## ary i
-       = go (i+1) (max e x)
+       = go (i + 1) (max e x)
   {-# INLINE maximum #-}
   minimum ary | sz == 0   = die "minimum" "empty array"
               | (# frst #) <- indexArray## ary 0
@@ -478,15 +486,14 @@
          go i !e
            | i == sz = e
            | (# x #) <- indexArray## ary i
-           = go (i+1) (min e x)
+           = go (i + 1) (min e x)
   {-# INLINE minimum #-}
   sum = foldl' (+) 0
   {-# INLINE sum #-}
   product = foldl' (*) 1
   {-# INLINE product #-}
-#endif
 
-newtype STA a = STA {_runSTA :: forall s. MutableArray# s a -> ST s (Array a)}
+newtype STA a = STA { _runSTA :: forall s. MutableArray# s a -> ST s (Array a) }
 
 runSTA :: Int -> STA a -> Array a
 runSTA !sz = \ (STA m) -> runST $ newArray_ sz >>= \ ar -> m (marray# ar)
@@ -518,8 +525,8 @@
                   writeArray (MutableArray mary) i b >> m mary)
                (f x) (go (i + 1))
   in if len == 0
-     then pure emptyArray
-     else runSTA len <$> go 0
+    then pure emptyArray
+    else runSTA len <$> go 0
 {-# INLINE [1] traverseArray #-}
 
 {-# RULES
@@ -527,19 +534,15 @@
    traverseArrayP f
 "traverse/IO" forall (f :: a -> IO b). traverseArray f =
    traverseArrayP f
- #-}
-#if MIN_VERSION_base(4,8,0)
-{-# RULES
 "traverse/Id" forall (f :: a -> Identity b). traverseArray f =
    (coerce :: (Array a -> Array (Identity b))
            -> Array a -> Identity (Array b)) (fmap f)
  #-}
-#endif
 
 -- | This is the fastest, most straightforward way to traverse
 -- an array, but it only works correctly with a sufficiently
 -- "affine" 'PrimMonad' instance. In particular, it must only produce
--- *one* result array. 'Control.Monad.Trans.List.ListT'-transformed
+-- /one/ result array. 'Control.Monad.Trans.List.ListT'-transformed
 -- monads, for example, will not work right at all.
 traverseArrayP
   :: PrimMonad m
@@ -574,40 +577,45 @@
                   -- We use indexArrayM here so that we will perform the
                   -- indexing eagerly even if f is lazy.
                   let !y = f x
-                  writeArray mb i y >> go (i+1)
+                  writeArray mb i y >> go (i + 1)
      in go 0
 {-# INLINE mapArray' #-}
 
+-- | Create an array from a list of a known length. If the length
+-- of the list does not match the given length, this throws an exception.
+
+-- Note [fromListN]
+-- ~~~~~~~~~~~~~~~~
+-- We want arrayFromListN to be a "good consumer" in list fusion, so we define
+-- the function using foldr and inline it to help fire fusion rules.
+-- If fusion occurs with a "good producer", it may reduce to a fold on some
+-- structure. In certain cases (such as for Data.Set) GHC is not be able to
+-- optimize the index to an unboxed Int# (see GHC #24628), so we explicitly use
+-- an Int# here.
 arrayFromListN :: Int -> [a] -> Array a
+{-# INLINE arrayFromListN #-}
 arrayFromListN n l =
   createArray n (die "fromListN" "uninitialized element") $ \sma ->
-    let go !ix [] = if ix == n
+    let z ix# = if I# ix# == n
           then return ()
           else die "fromListN" "list length less than specified size"
-        go !ix (x : xs) = if ix < n
+        f x k = GHC.Exts.oneShot $ \ix# -> if I# ix# < n
           then do
-            writeArray sma ix x
-            go (ix+1) xs
+            writeArray sma (I# ix#) x
+            k (ix# +# 1#)
           else die "fromListN" "list length greater than specified size"
-    in go 0 l
+    in foldr f z l 0#
 
+-- | Create an array from a list.
 arrayFromList :: [a] -> Array a
 arrayFromList l = arrayFromListN (length l) l
 
-#if MIN_VERSION_base(4,7,0)
 instance Exts.IsList (Array a) where
   type Item (Array a) = a
   fromListN = arrayFromListN
   fromList = arrayFromList
   toList = toList
-#else
-fromListN :: Int -> [a] -> Array a
-fromListN = arrayFromListN
 
-fromList :: [a] -> Array a
-fromList = arrayFromList
-#endif
-
 instance Functor Array where
   fmap f a =
     createArray (sizeofArray a) (die "fmap" "impossible") $ \mb ->
@@ -615,47 +623,48 @@
                = return ()
                | otherwise
                = do x <- indexArrayM a i
-                    writeArray mb i (f x) >> go (i+1)
+                    writeArray mb i (f x) >> go (i + 1)
        in go 0
-#if MIN_VERSION_base(4,8,0)
   e <$ a = createArray (sizeofArray a) e (\ !_ -> pure ())
-#endif
 
 instance Applicative Array where
   pure x = runArray $ newArray 1 x
-  ab <*> a = createArray (szab*sza) (die "<*>" "impossible") $ \mb ->
+
+  ab <*> a = createArray (szab * sza) (die "<*>" "impossible") $ \mb ->
     let go1 i = when (i < szab) $
             do
               f <- indexArrayM ab i
-              go2 (i*sza) f 0
-              go1 (i+1)
+              go2 (i * sza) f 0
+              go1 (i + 1)
         go2 off f j = when (j < sza) $
             do
               x <- indexArrayM a j
               writeArray mb (off + j) (f x)
               go2 off f (j + 1)
     in go1 0
-   where szab = sizeofArray ab ; sza = sizeofArray a
-  a *> b = createArray (sza*szb) (die "*>" "impossible") $ \mb ->
-    let go i | i < sza   = copyArray mb (i * szb) b 0 szb
+   where szab = sizeofArray ab; sza = sizeofArray a
+
+  a *> b = createArray (sza * szb) (die "*>" "impossible") $ \mb ->
+    let go i | i < sza   = copyArray mb (i * szb) b 0 szb *> go (i + 1)
              | otherwise = return ()
-     in go 0
-   where sza = sizeofArray a ; szb = sizeofArray b
-  a <* b = createArray (sza*szb) (die "<*" "impossible") $ \ma ->
-    let fill off i e | i < szb   = writeArray ma (off+i) e >> fill off (i+1) e
+    in go 0
+   where sza = sizeofArray a; szb = sizeofArray b
+
+  a <* b = createArray (sza * szb) (die "<*" "impossible") $ \ma ->
+    let fill off i e | i < szb   = writeArray ma (off + i) e >> fill off (i + 1) e
                      | otherwise = return ()
         go i | i < sza
              = do x <- indexArrayM a i
-                  fill (i*szb) 0 x >> go (i+1)
+                  fill (i * szb) 0 x >> go (i + 1)
              | otherwise = return ()
-     in go 0
-   where sza = sizeofArray a ; szb = sizeofArray b
+    in go 0
+   where sza = sizeofArray a; szb = sizeofArray b
 
 instance Alternative Array where
   empty = emptyArray
   a1 <|> a2 = createArray (sza1 + sza2) (die "<|>" "impossible") $ \ma ->
     copyArray ma 0 a1 0 sza1 >> copyArray ma sza1 a2 0 sza2
-   where sza1 = sizeofArray a1 ; sza2 = sizeofArray a2
+   where sza1 = sizeofArray a1; sza2 = sizeofArray a2
   some a | sizeofArray a == 0 = emptyArray
          | otherwise = die "some" "infinite arrays are not well defined"
   many a | sizeofArray a == 0 = pure []
@@ -670,27 +679,32 @@
   return = pure
   (>>) = (*>)
 
-  ary >>= f = collect 0 EmptyStack (la-1)
+  ary >>= f = collect 0 EmptyStack (la - 1)
    where
-   la = sizeofArray ary
-   collect sz stk i
-     | i < 0 = createArray sz (die ">>=" "impossible") $ fill 0 stk
-     | (# x #) <- indexArray## ary i
-     , let sb = f x
-           lsb = sizeofArray sb
-       -- If we don't perform this check, we could end up allocating
-       -- a stack full of empty arrays if someone is filtering most
-       -- things out. So we refrain from pushing empty arrays.
-     = if lsb == 0
-       then collect sz stk (i - 1)
-       else collect (sz + lsb) (PushArray sb stk) (i-1)
+    la = sizeofArray ary
+    collect sz stk i
+      | i < 0 = createArray sz (die ">>=" "impossible") $ fill 0 stk
+      | (# x #) <- indexArray## ary i
+      , let sb = f x
+            lsb = sizeofArray sb
+        -- If we don't perform this check, we could end up allocating
+        -- a stack full of empty arrays if someone is filtering most
+        -- things out. So we refrain from pushing empty arrays.
+      = if lsb == 0
+        then collect sz stk (i - 1)
+        else collect (sz + lsb) (PushArray sb stk) (i - 1)
 
-   fill _   EmptyStack         _   = return ()
-   fill off (PushArray sb sbs) smb
-     | let lsb = sizeofArray sb
-     = copyArray smb off sb 0 (lsb)
-         *> fill (off + lsb) sbs smb
+    fill _ EmptyStack _ = return ()
+    fill off (PushArray sb sbs) smb
+      | let lsb = sizeofArray sb
+      = copyArray smb off sb 0 lsb
+          *> fill (off + lsb) sbs smb
 
+#if !(MIN_VERSION_base(4,13,0))
+  fail = Fail.fail
+#endif
+
+instance Fail.MonadFail Array where
   fail _ = empty
 
 instance MonadPlus Array where
@@ -704,13 +718,12 @@
                x <- indexArrayM aa i
                y <- indexArrayM ab i
                writeArray mc i (f x y)
-               go (i+1)
+               go (i + 1)
            | otherwise = return ()
    in go 0
  where mn = sizeofArray aa `min` sizeofArray ab
 {-# INLINE zipW #-}
 
-#if MIN_VERSION_base(4,4,0)
 instance MonadZip Array where
   mzip aa ab = zipW "mzip" (,) aa ab
   mzipWith f aa ab = zipW "mzipWith" f aa ab
@@ -722,11 +735,10 @@
           (a, b) <- indexArrayM aab i
           writeArray ma i a
           writeArray mb i b
-          go (i+1)
+          go (i + 1)
         go _ = return ()
     go 0
     (,) <$> unsafeFreezeArray ma <*> unsafeFreezeArray mb
-#endif
 
 instance MonadFix Array where
   mfix f = createArray (sizeofArray (f err))
@@ -738,17 +750,24 @@
       sz = sizeofArray (f err)
       err = error "mfix for Data.Primitive.Array applied to strict function."
 
-#if MIN_VERSION_base(4,9,0)
 -- | @since 0.6.3.0
 instance Semigroup (Array a) where
   (<>) = (<|>)
   sconcat = mconcat . F.toList
-#endif
+  stimes n arr = case compare n 0 of
+    LT -> die "stimes" "negative multiplier"
+    EQ -> empty
+    GT -> createArray (n' * sizeofArray arr) (die "stimes" "impossible") $ \ma ->
+      let go i = when (i < n') $ do
+            copyArray ma (i * sizeofArray arr) arr 0 (sizeofArray arr)
+            go (i + 1)
+      in go 0
+    where n' = fromIntegral n :: Int
 
 instance Monoid (Array a) where
   mempty = empty
 #if !(MIN_VERSION_base(4,11,0))
-  mappend = (<|>)
+  mappend = (<>)
 #endif
   mconcat l = createArray sz (die "mconcat" "impossible") $ \ma ->
     let go !_  [    ] = return ()
@@ -758,9 +777,8 @@
    where sz = sum . fmap sizeofArray $ l
 
 arrayLiftShowsPrec :: (Int -> a -> ShowS) -> ([a] -> ShowS) -> Int -> Array a -> ShowS
-arrayLiftShowsPrec elemShowsPrec elemListShowsPrec p a = showParen (p > 10) $
-  showString "fromListN " . shows (sizeofArray a) . showString " "
-    . listLiftShowsPrec elemShowsPrec elemListShowsPrec 11 (toList a)
+arrayLiftShowsPrec elemShowsPrec elemListShowsPrec _ a =
+  listLiftShowsPrec elemShowsPrec elemListShowsPrec 11 (toList a)
 
 -- this need to be included for older ghcs
 listLiftShowsPrec :: (Int -> a -> ShowS) -> ([a] -> ShowS) -> Int -> [a] -> ShowS
@@ -769,38 +787,36 @@
 instance Show a => Show (Array a) where
   showsPrec p a = arrayLiftShowsPrec showsPrec showList p a
 
-#if MIN_VERSION_base(4,9,0) || MIN_VERSION_transformers(0,4,0)
 -- | @since 0.6.4.0
 instance Show1 Array where
-#if MIN_VERSION_base(4,9,0) || MIN_VERSION_transformers(0,5,0)
   liftShowsPrec = arrayLiftShowsPrec
-#else
-  showsPrec1 = arrayLiftShowsPrec showsPrec showList
-#endif
-#endif
 
-arrayLiftReadsPrec :: (Int -> ReadS a) -> ReadS [a] -> Int -> ReadS (Array a)
-arrayLiftReadsPrec _ listReadsPrec p = readParen (p > 10) . readP_to_S $ do
-  () <$ string "fromListN"
-  skipSpaces
-  n <- readS_to_P reads
-  skipSpaces
-  l <- readS_to_P listReadsPrec
-  return $ arrayFromListN n l
-
 instance Read a => Read (Array a) where
-  readsPrec = arrayLiftReadsPrec readsPrec readList
+  readPrec = arrayLiftReadPrec readPrec readListPrec
 
-#if MIN_VERSION_base(4,9,0) || MIN_VERSION_transformers(0,4,0)
 -- | @since 0.6.4.0
 instance Read1 Array where
-#if MIN_VERSION_base(4,9,0) || MIN_VERSION_transformers(0,5,0)
-  liftReadsPrec = arrayLiftReadsPrec
-#else
-  readsPrec1 = arrayLiftReadsPrec readsPrec readList
-#endif
-#endif
+  liftReadPrec = arrayLiftReadPrec
 
+-- Note [Forgiving Array Read Instance]
+-- We're really forgiving here. We accept
+-- "[1,2,3]", "fromList [1,2,3]", and "fromListN 3 [1,2,3]".
+-- We consider fromListN with an invalid length to be an
+-- error, rather than a parse failure, because doing otherwise
+-- seems weird and likely to make debugging difficult.
+arrayLiftReadPrec :: ReadPrec a -> ReadPrec [a] -> ReadPrec (Array a)
+arrayLiftReadPrec _ read_list =
+  ( RdPrc.lift skipSpaces >> fmap fromList read_list )
+  RdPrc.+++
+  ( parens $ prec app_prec $ do
+      RdPrc.lift skipSpaces
+      tag <- RdPrc.lift lexTag
+      case tag of
+        FromListTag -> fromList <$> read_list
+        FromListNTag -> liftM2 fromListN readPrec read_list
+  )
+  where
+  app_prec = 10
 
 arrayDataType :: DataType
 arrayDataType = mkDataType "Data.Primitive.Array.Array" [fromListConstr]
diff --git a/Data/Primitive/ByteArray.hs b/Data/Primitive/ByteArray.hs
--- a/Data/Primitive/ByteArray.hs
+++ b/Data/Primitive/ByteArray.hs
@@ -1,6 +1,8 @@
-{-# LANGUAGE BangPatterns, CPP, MagicHash, UnboxedTuples, UnliftedFFITypes, DeriveDataTypeable #-}
+{-# LANGUAGE BangPatterns, CPP, MagicHash, UnboxedTuples, UnliftedFFITypes #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE TypeApplications #-}
+{-# LANGUAGE RankNTypes #-}
 
 -- |
 -- Module      : Data.Primitive.ByteArray
@@ -10,8 +12,11 @@
 -- Maintainer  : Roman Leshchinskiy <rl@cse.unsw.edu.au>
 -- Portability : non-portable
 --
--- Primitive operations on ByteArrays
---
+-- Primitive operations on byte arrays. Most functions in this module include
+-- an element type in their type signature and interpret the unit for offsets
+-- and lengths as that element. A few functions (e.g. 'copyByteArray',
+-- 'freezeByteArray') do not include an element type. Such functions
+-- interpret offsets and lengths as units of 8-bit words.
 
 module Data.Primitive.ByteArray (
   -- * Types
@@ -20,26 +25,36 @@
   -- * Allocation
   newByteArray, newPinnedByteArray, newAlignedPinnedByteArray,
   resizeMutableByteArray,
+  shrinkMutableByteArray,
 
   -- * Element access
   readByteArray, writeByteArray, indexByteArray,
+  -- * Char Element Access
+  -- $charElementAccess
+  readCharArray, writeCharArray, indexCharArray,
 
   -- * Constructing
+  emptyByteArray,
   byteArrayFromList, byteArrayFromListN,
 
   -- * Folding
   foldrByteArray,
 
+  -- * Comparing
+  compareByteArrays,
+
   -- * Freezing and thawing
+  freezeByteArray, thawByteArray, runByteArray, createByteArray,
   unsafeFreezeByteArray, unsafeThawByteArray,
 
   -- * Block operations
   copyByteArray, copyMutableByteArray,
-#if __GLASGOW_HASKELL__ >= 708
+  copyByteArrayToPtr, copyMutableByteArrayToPtr,
   copyByteArrayToAddr, copyMutableByteArrayToAddr,
-#endif
+  copyPtrToMutableByteArray,
   moveByteArray,
   setByteArray, fillByteArray,
+  cloneByteArray, cloneMutableByteArray,
 
   -- * Information
   sizeofByteArray,
@@ -47,57 +62,42 @@
 #if __GLASGOW_HASKELL__ >= 802
   isByteArrayPinned, isMutableByteArrayPinned,
 #endif
-  byteArrayContents, mutableByteArrayContents
+  byteArrayAsForeignPtr,
+  mutableByteArrayAsForeignPtr,
+  byteArrayContents,
+  withByteArrayContents,
+  mutableByteArrayContents,
+  withMutableByteArrayContents
 
 ) where
 
 import Control.Monad.Primitive
 import Control.Monad.ST
 import Data.Primitive.Types
-
-import Foreign.C.Types
-import Data.Word ( Word8 )
-import GHC.Base ( Int(..) )
-#if __GLASGOW_HASKELL__ >= 708
-import qualified GHC.Exts as Exts ( IsList(..) )
-#endif
-import GHC.Prim
-#if __GLASGOW_HASKELL__ >= 706
-    hiding (setByteArray#)
-#endif
-
-import Data.Typeable ( Typeable )
-import Data.Data ( Data(..) )
-import Data.Primitive.Internal.Compat ( isTrue#, mkNoRepType )
-import Numeric
-
-#if MIN_VERSION_base(4,9,0)
-import qualified Data.Semigroup as SG
-import qualified Data.Foldable as F
-#endif
+import Data.Proxy
 
-#if !(MIN_VERSION_base(4,8,0))
-import Data.Monoid (Monoid(..))
-#endif
+import qualified GHC.ST as GHCST
 
+import Data.Word ( Word8 )
 #if __GLASGOW_HASKELL__ >= 802
-import GHC.Exts as Exts (isByteArrayPinned#,isMutableByteArrayPinned#)
+import qualified GHC.Exts as Exts
 #endif
+import GHC.Exts hiding (setByteArray#)
+import GHC.ForeignPtr (ForeignPtr(..), ForeignPtrContents(..))
 
-#if __GLASGOW_HASKELL__ >= 804
-import GHC.Exts (compareByteArrays#)
-#else
+#if __GLASGOW_HASKELL__ < 804
+import Foreign.C.Types
 import System.IO.Unsafe (unsafeDupablePerformIO)
 #endif
 
--- | Byte arrays
-data ByteArray = ByteArray ByteArray# deriving ( Typeable )
+import Data.Array.Byte (ByteArray(..), MutableByteArray(..))
 
--- | Mutable byte arrays associated with a primitive state token
-data MutableByteArray s = MutableByteArray (MutableByteArray# s)
-                                        deriving( Typeable )
+import Data.Primitive.Internal.Operations (mutableByteArrayContentsShim)
 
 -- | Create a new mutable byte array of the specified size in bytes.
+-- The underlying memory is left uninitialized.
+--
+-- /Note:/ this function does not check if the input is non-negative.
 newByteArray :: PrimMonad m => Int -> m (MutableByteArray (PrimState m))
 {-# INLINE newByteArray #-}
 newByteArray (I# n#)
@@ -105,7 +105,9 @@
                         (# s'#, arr# #) -> (# s'#, MutableByteArray arr# #))
 
 -- | Create a /pinned/ byte array of the specified size in bytes. The garbage
--- collector is guaranteed not to move it.
+-- collector is guaranteed not to move it. The underlying memory is left uninitialized.
+--
+-- /Note:/ this function does not check if the input is non-negative.
 newPinnedByteArray :: PrimMonad m => Int -> m (MutableByteArray (PrimState m))
 {-# INLINE newPinnedByteArray #-}
 newPinnedByteArray (I# n#)
@@ -114,6 +116,9 @@
 
 -- | Create a /pinned/ byte array of the specified size in bytes and with the
 -- given alignment. The garbage collector is guaranteed not to move it.
+-- The underlying memory is left uninitialized.
+--
+-- /Note:/ this function does not check if the input is non-negative.
 newAlignedPinnedByteArray
   :: PrimMonad m
   => Int  -- ^ size
@@ -124,21 +129,67 @@
   = primitive (\s# -> case newAlignedPinnedByteArray# n# k# s# of
                         (# s'#, arr# #) -> (# s'#, MutableByteArray arr# #))
 
+-- | Create a foreign pointer that points to the array's data. This operation
+-- is only safe on /pinned/ byte arrays.  The array's data is not garbage
+-- collected while references to the foreign pointer exist. Writing to the
+-- array through the foreign pointer results in undefined behavior.
+byteArrayAsForeignPtr :: ByteArray -> ForeignPtr Word8
+{-# INLINE byteArrayAsForeignPtr #-}
+byteArrayAsForeignPtr (ByteArray arr#) = ForeignPtr (byteArrayContents# arr#) (PlainPtr (unsafeCoerce# arr#))
+
+
+-- | Variant of 'byteArrayAsForeignPtr' for mutable byte arrays. Similarly, this
+-- is only safe on /pinned/ mutable byte arrays. This function differs from the
+-- variant for immutable arrays in that it is safe to write to the array though
+-- the foreign pointer.
+mutableByteArrayAsForeignPtr :: MutableByteArray RealWorld -> ForeignPtr Word8
+{-# INLINE mutableByteArrayAsForeignPtr #-}
+mutableByteArrayAsForeignPtr (MutableByteArray arr#) = ForeignPtr (mutableByteArrayContentsShim arr#) (PlainPtr arr#)
+
 -- | Yield a pointer to the array's data. This operation is only safe on
--- /pinned/ byte arrays allocated by 'newPinnedByteArray' or
--- 'newAlignedPinnedByteArray'.
-byteArrayContents :: ByteArray -> Addr
+-- /pinned/ byte arrays. Byte arrays allocated by 'newPinnedByteArray' and
+-- 'newAlignedPinnedByteArray' are guaranteed to be pinned. Byte arrays
+-- allocated by 'newByteArray' may or may not be pinned (use
+-- 'isByteArrayPinned' to figure out).
+--
+-- Prefer 'withByteArrayContents', which ensures that the array is not
+-- garbage collected while the pointer is being used.
+byteArrayContents :: ByteArray -> Ptr Word8
 {-# INLINE byteArrayContents #-}
-byteArrayContents (ByteArray arr#) = Addr (byteArrayContents# arr#)
+byteArrayContents (ByteArray arr#) = Ptr (byteArrayContents# arr#)
 
+-- | A composition of 'byteArrayContents' and 'keepAliveUnlifted'.
+-- The callback function must not return the pointer. The argument byte
+-- array must be /pinned/. See 'byteArrayContents' for an explanation
+-- of which byte arrays are pinned.
+--
+-- Note: This could be implemented with 'keepAlive' instead of
+-- 'keepAliveUnlifted', but 'keepAlive' here would cause GHC to materialize
+-- the wrapper data constructor on the heap.
+withByteArrayContents :: PrimBase m => ByteArray -> (Ptr Word8 -> m a) -> m a
+{-# INLINE withByteArrayContents #-}
+withByteArrayContents (ByteArray arr#) f =
+  keepAliveUnlifted arr# (f (Ptr (byteArrayContents# arr#)))
+
 -- | Yield a pointer to the array's data. This operation is only safe on
--- /pinned/ byte arrays allocated by 'newPinnedByteArray' or
--- 'newAlignedPinnedByteArray'.
-mutableByteArrayContents :: MutableByteArray s -> Addr
+-- /pinned/ byte arrays. See 'byteArrayContents' for an explanation
+-- of which byte arrays are pinned.
+--
+-- Prefer 'withByteArrayContents', which ensures that the array is not
+-- garbage collected while the pointer is being used.
+mutableByteArrayContents :: MutableByteArray s -> Ptr Word8
 {-# INLINE mutableByteArrayContents #-}
-mutableByteArrayContents (MutableByteArray arr#)
-  = Addr (byteArrayContents# (unsafeCoerce# arr#))
+mutableByteArrayContents (MutableByteArray arr#) = Ptr (mutableByteArrayContentsShim arr#)
 
+-- | A composition of 'mutableByteArrayContents' and 'keepAliveUnlifted'.
+-- The callback function must not return the pointer. The argument byte
+-- array must be /pinned/. See 'byteArrayContents' for an explanation
+-- of which byte arrays are pinned.
+withMutableByteArrayContents :: PrimBase m => MutableByteArray (PrimState m) -> (Ptr Word8 -> m a) -> m a
+{-# INLINE withMutableByteArrayContents #-}
+withMutableByteArrayContents (MutableByteArray arr#) f =
+  keepAliveUnlifted arr# (f (Ptr (mutableByteArrayContentsShim arr#)))
+
 -- | Check if the two arrays refer to the same memory block.
 sameMutableByteArray :: MutableByteArray s -> MutableByteArray s -> Bool
 {-# INLINE sameMutableByteArray #-}
@@ -161,16 +212,9 @@
   :: PrimMonad m => MutableByteArray (PrimState m) -> Int
                  -> m (MutableByteArray (PrimState m))
 {-# INLINE resizeMutableByteArray #-}
-#if __GLASGOW_HASKELL__ >= 710
 resizeMutableByteArray (MutableByteArray arr#) (I# n#)
   = primitive (\s# -> case resizeMutableByteArray# arr# n# s# of
                         (# s'#, arr'# #) -> (# s'#, MutableByteArray arr'# #))
-#else
-resizeMutableByteArray arr n
-  = do arr' <- newByteArray n
-       copyMutableByteArray arr' 0 arr 0 (min (sizeofMutableByteArray arr) n)
-       return arr'
-#endif
 
 -- | Get the size of a byte array in bytes. Unlike 'sizeofMutableByteArray',
 -- this function ensures sequencing in the presence of resizing.
@@ -186,6 +230,48 @@
   = return (sizeofMutableByteArray arr)
 #endif
 
+-- | Create an immutable copy of a slice of a byte array. The offset and
+-- length are given in bytes.
+--
+-- This operation makes a copy of the specified section, so it is safe to
+-- continue using the mutable array afterward.
+--
+-- /Note:/ The provided array should contain the full subrange
+-- specified by the two Ints, but this is not checked.
+freezeByteArray
+  :: PrimMonad m
+  => MutableByteArray (PrimState m) -- ^ source
+  -> Int                            -- ^ offset in bytes
+  -> Int                            -- ^ length in bytes
+  -> m ByteArray
+{-# INLINE freezeByteArray #-}
+freezeByteArray !src !off !len = do
+  dst <- newByteArray len
+  copyMutableByteArray dst 0 src off len
+  unsafeFreezeByteArray dst
+
+-- | Create a mutable byte array from a slice of an immutable byte array.
+-- The offset and length are given in bytes.
+--
+-- This operation makes a copy of the specified slice, so it is safe to
+-- use the immutable array afterward.
+--
+-- /Note:/ The provided array should contain the full subrange
+-- specified by the two Ints, but this is not checked.
+--
+-- @since 0.7.2.0
+thawByteArray
+  :: PrimMonad m
+  => ByteArray -- ^ source
+  -> Int       -- ^ offset in bytes
+  -> Int       -- ^ length in bytes
+  -> m (MutableByteArray (PrimState m))
+{-# INLINE thawByteArray #-}
+thawByteArray !src !off !len = do
+  dst <- newByteArray len
+  copyByteArray dst 0 src off len
+  return dst
+
 -- | Convert a mutable byte array to an immutable one without copying. The
 -- array should not be modified after the conversion.
 unsafeFreezeByteArray
@@ -208,30 +294,48 @@
 {-# INLINE sizeofByteArray #-}
 sizeofByteArray (ByteArray arr#) = I# (sizeofByteArray# arr#)
 
--- | Size of the mutable byte array in bytes. This function\'s behavior 
+-- | Size of the mutable byte array in bytes.
+--
+-- This function is deprecated and will be removed. Its behavior
 -- is undefined if 'resizeMutableByteArray' is ever called on the mutable
--- byte array given as the argument. Consequently, use of this function
--- is discouraged. Prefer 'getSizeofMutableByteArray', which ensures correct
--- sequencing in the presence of resizing.
+-- byte array given as the argument. Prefer 'getSizeofMutableByteArray',
+-- which ensures correct sequencing in the presence of resizing.
 sizeofMutableByteArray :: MutableByteArray s -> Int
 {-# INLINE sizeofMutableByteArray #-}
+{-# DEPRECATED sizeofMutableByteArray "use getSizeofMutableByteArray instead" #-}
 sizeofMutableByteArray (MutableByteArray arr#) = I# (sizeofMutableByteArray# arr#)
 
+-- | Shrink a mutable byte array. The new size is given in bytes.
+-- It must be smaller than the old size. The array will be resized in place.
+--
+-- @since 0.7.1.0
+shrinkMutableByteArray :: PrimMonad m
+  => MutableByteArray (PrimState m)
+  -> Int -- ^ new size
+  -> m ()
+{-# INLINE shrinkMutableByteArray #-}
+shrinkMutableByteArray (MutableByteArray arr#) (I# n#)
+  = primitive_ (shrinkMutableByteArray# arr# n#)
+
 #if __GLASGOW_HASKELL__ >= 802
 -- | Check whether or not the byte array is pinned. Pinned byte arrays cannot
---   be moved by the garbage collector. It is safe to use 'byteArrayContents'
---   on such byte arrays. This function is only available when compiling with
---   GHC 8.2 or newer.
+-- be moved by the garbage collector. It is safe to use 'byteArrayContents' on
+-- such byte arrays.
 --
---   @since 0.6.4.0
+-- Caution: This function is only available when compiling with GHC 8.2 or
+-- newer.
+--
+-- @since 0.6.4.0
 isByteArrayPinned :: ByteArray -> Bool
 {-# INLINE isByteArrayPinned #-}
 isByteArrayPinned (ByteArray arr#) = isTrue# (Exts.isByteArrayPinned# arr#)
 
--- | Check whether or not the mutable byte array is pinned. This function is
---   only available when compiling with GHC 8.2 or newer.
+-- | Check whether or not the mutable byte array is pinned.
 --
---   @since 0.6.4.0
+-- Caution: This function is only available when compiling with GHC 8.2 or
+-- newer.
+--
+-- @since 0.6.4.0
 isMutableByteArrayPinned :: MutableByteArray s -> Bool
 {-# INLINE isMutableByteArrayPinned #-}
 isMutableByteArrayPinned (MutableByteArray marr#) = isTrue# (Exts.isMutableByteArrayPinned# marr#)
@@ -239,12 +343,16 @@
 
 -- | Read a primitive value from the byte array. The offset is given in
 -- elements of type @a@ rather than in bytes.
+--
+-- /Note:/ this function does not do bounds checking.
 indexByteArray :: Prim a => ByteArray -> Int -> a
 {-# INLINE indexByteArray #-}
 indexByteArray (ByteArray arr#) (I# i#) = indexByteArray# arr# i#
 
 -- | Read a primitive value from the byte array. The offset is given in
 -- elements of type @a@ rather than in bytes.
+--
+-- /Note:/ this function does not do bounds checking.
 readByteArray
   :: (Prim a, PrimMonad m) => MutableByteArray (PrimState m) -> Int -> m a
 {-# INLINE readByteArray #-}
@@ -253,6 +361,8 @@
 
 -- | Write a primitive value to the byte array. The offset is given in
 -- elements of type @a@ rather than in bytes.
+--
+-- /Note:/ this function does not do bounds checking.
 writeByteArray
   :: (Prim a, PrimMonad m) => MutableByteArray (PrimState m) -> Int -> a -> m ()
 {-# INLINE writeByteArray #-}
@@ -261,289 +371,344 @@
 
 -- | Right-fold over the elements of a 'ByteArray'.
 foldrByteArray :: forall a b. (Prim a) => (a -> b -> b) -> b -> ByteArray -> b
+{-# INLINE foldrByteArray #-}
 foldrByteArray f z arr = go 0
   where
     go i
-      | sizeofByteArray arr > i * sz = f (indexByteArray arr i) (go (i+1))
-      | otherwise                    = z
-    sz = sizeOf (undefined :: a)
+      | i < maxI  = f (indexByteArray arr i) (go (i + 1))
+      | otherwise = z
+    maxI = sizeofByteArray arr `quot` sizeOfType @a
 
+-- | Create a 'ByteArray' from a list.
+--
+-- @byteArrayFromList xs = `byteArrayFromListN` (length xs) xs@
 byteArrayFromList :: Prim a => [a] -> ByteArray
 byteArrayFromList xs = byteArrayFromListN (length xs) xs
 
-byteArrayFromListN :: Prim a => Int -> [a] -> ByteArray
-byteArrayFromListN n ys = runST $ do
-    marr <- newByteArray (n * sizeOf (head ys))
-    let go !ix [] = if ix == n
-          then return ()
-          else die "byteArrayFromListN" "list length less than specified size"
-        go !ix (x : xs) = if ix < n
-          then do
-            writeByteArray marr ix x
-            go (ix + 1) xs
-          else die "byteArrayFromListN" "list length greater than specified size"
-    go 0 ys
-    unsafeFreezeByteArray marr
+-- | Create a 'ByteArray' from a list of a known length. If the length
+-- of the list does not match the given length, this throws an exception.
 
+-- See Note [fromListN] in Data.Primitive.Array
+byteArrayFromListN :: forall a. Prim a => Int -> [a] -> ByteArray
+{-# INLINE byteArrayFromListN #-}
+byteArrayFromListN n ys = createByteArray (n * sizeOfType @a) $ \marr ->
+  let z ix# = if I# ix# == n
+        then return ()
+        else die "byteArrayFromListN" "list length less than specified size"
+      f x k = GHC.Exts.oneShot $ \ix# -> if I# ix# < n
+        then do
+          writeByteArray marr (I# ix#) x
+          k (ix# +# 1#)
+        else die "byteArrayFromListN" "list length greater than specified size"
+  in foldr f z ys 0#
+
 unI# :: Int -> Int#
 unI# (I# n#) = n#
 
 -- | Copy a slice of an immutable byte array to a mutable byte array.
+--
+-- /Note:/ this function does not do bounds or overlap checking.
 copyByteArray
-  :: PrimMonad m => MutableByteArray (PrimState m)
-                                        -- ^ destination array
-                 -> Int                 -- ^ offset into destination array
-                 -> ByteArray           -- ^ source array
-                 -> Int                 -- ^ offset into source array
-                 -> Int                 -- ^ number of bytes to copy
-                 -> m ()
+  :: PrimMonad m
+  => MutableByteArray (PrimState m) -- ^ destination array
+  -> Int                            -- ^ offset into destination array
+  -> ByteArray                      -- ^ source array
+  -> Int                            -- ^ offset into source array
+  -> Int                            -- ^ number of bytes to copy
+  -> m ()
 {-# INLINE copyByteArray #-}
 copyByteArray (MutableByteArray dst#) doff (ByteArray src#) soff sz
   = primitive_ (copyByteArray# src# (unI# soff) dst# (unI# doff) (unI# sz))
 
 -- | Copy a slice of a mutable byte array into another array. The two slices
 -- may not overlap.
+--
+-- /Note:/ this function does not do bounds or overlap checking.
 copyMutableByteArray
-  :: PrimMonad m => MutableByteArray (PrimState m)
-                                        -- ^ destination array
-                 -> Int                 -- ^ offset into destination array
-                 -> MutableByteArray (PrimState m)
-                                        -- ^ source array
-                 -> Int                 -- ^ offset into source array
-                 -> Int                 -- ^ number of bytes to copy
-                 -> m ()
+  :: PrimMonad m
+  => MutableByteArray (PrimState m) -- ^ destination array
+  -> Int                            -- ^ offset into destination array
+  -> MutableByteArray (PrimState m) -- ^ source array
+  -> Int                            -- ^ offset into source array
+  -> Int                            -- ^ number of bytes to copy
+  -> m ()
 {-# INLINE copyMutableByteArray #-}
 copyMutableByteArray (MutableByteArray dst#) doff
                      (MutableByteArray src#) soff sz
-  = primitive_ (copyMutableByteArray# src# (unI# soff) dst# (unI# doff) (unI# sz))
+  = primitive_ (op src# (unI# soff) dst# (unI# doff) (unI# sz))
+  where
+#if MIN_VERSION_base(4,19,0)
+    op = copyMutableByteArrayNonOverlapping#
+#else
+    op = copyMutableByteArray#
+#endif
 
-#if __GLASGOW_HASKELL__ >= 708
+-- | Copy a slice of a byte array to an unmanaged pointer address. These must not
+-- overlap. The offset and length are given in elements, not in bytes.
+--
+-- /Note:/ this function does not do bounds or overlap checking.
+--
+-- @since 0.7.1.0
+copyByteArrayToPtr
+  :: forall m a. (PrimMonad m, Prim a)
+  => Ptr a -- ^ destination
+  -> ByteArray -- ^ source array
+  -> Int -- ^ offset into source array, interpreted as elements of type @a@
+  -> Int -- ^ number of elements to copy
+  -> m ()
+{-# INLINE copyByteArrayToPtr #-}
+copyByteArrayToPtr (Ptr dst#) (ByteArray src#) soff sz
+  = primitive_ (copyByteArrayToAddr# src# (unI# soff *# siz#) dst# (unI# sz *# siz#))
+  where
+  siz# = sizeOfType# (Proxy :: Proxy a)
+
+-- | Copy from an unmanaged pointer address to a byte array. These must not
+-- overlap. The offset and length are given in elements, not in bytes.
+--
+-- /Note:/ this function does not do bounds or overlap checking.
+copyPtrToMutableByteArray :: forall m a. (PrimMonad m, Prim a)
+  => MutableByteArray (PrimState m) -- ^ destination array
+  -> Int   -- ^ destination offset given in elements of type @a@
+  -> Ptr a -- ^ source pointer
+  -> Int   -- ^ number of elements
+  -> m ()
+{-# INLINE copyPtrToMutableByteArray #-}
+copyPtrToMutableByteArray (MutableByteArray ba#) (I# doff#) (Ptr addr#) (I# n#) =
+  primitive_ (copyAddrToByteArray# addr# ba# (doff# *# siz#) (n# *# siz#))
+  where
+  siz# = sizeOfType# (Proxy :: Proxy a)
+
+
+-- | Copy a slice of a mutable byte array to an unmanaged pointer address.
+-- These must not overlap. The offset and length are given in elements, not
+-- in bytes.
+--
+-- /Note:/ this function does not do bounds or overlap checking.
+--
+-- @since 0.7.1.0
+copyMutableByteArrayToPtr
+  :: forall m a. (PrimMonad m, Prim a)
+  => Ptr a -- ^ destination
+  -> MutableByteArray (PrimState m) -- ^ source array
+  -> Int -- ^ offset into source array, interpreted as elements of type @a@
+  -> Int -- ^ number of elements to copy
+  -> m ()
+{-# INLINE copyMutableByteArrayToPtr #-}
+copyMutableByteArrayToPtr (Ptr dst#) (MutableByteArray src#) soff sz
+  = primitive_ (copyMutableByteArrayToAddr# src# (unI# soff *# siz#) dst# (unI# sz *# siz#))
+  where
+  siz# = sizeOfType# (Proxy :: Proxy a)
+
+------
+--- These latter two should be DEPRECATED
+-----
+
 -- | Copy a slice of a byte array to an unmanaged address. These must not
---   overlap. This function is only available when compiling with GHC 7.8
---   or newer.
+-- overlap.
 --
---   @since 0.6.4.0
+-- Note: This function is just 'copyByteArrayToPtr' where @a@ is 'Word8'.
+--
+-- @since 0.6.4.0
 copyByteArrayToAddr
   :: PrimMonad m
-  => Addr -- ^ destination
+  => Ptr Word8 -- ^ destination
   -> ByteArray -- ^ source array
   -> Int -- ^ offset into source array
   -> Int -- ^ number of bytes to copy
   -> m ()
 {-# INLINE copyByteArrayToAddr #-}
-copyByteArrayToAddr (Addr dst#) (ByteArray src#) soff sz
+copyByteArrayToAddr (Ptr dst#) (ByteArray src#) soff sz
   = primitive_ (copyByteArrayToAddr# src# (unI# soff) dst# (unI# sz))
 
 -- | Copy a slice of a mutable byte array to an unmanaged address. These must
---   not overlap. This function is only available when compiling with GHC 7.8
---   or newer.
+-- not overlap.
 --
---   @since 0.6.4.0
+-- Note: This function is just 'copyMutableByteArrayToPtr' where @a@ is 'Word8'.
+--
+-- @since 0.6.4.0
 copyMutableByteArrayToAddr
   :: PrimMonad m
-  => Addr -- ^ destination
+  => Ptr Word8 -- ^ destination
   -> MutableByteArray (PrimState m) -- ^ source array
   -> Int -- ^ offset into source array
   -> Int -- ^ number of bytes to copy
   -> m ()
 {-# INLINE copyMutableByteArrayToAddr #-}
-copyMutableByteArrayToAddr (Addr dst#) (MutableByteArray src#) soff sz
+copyMutableByteArrayToAddr (Ptr dst#) (MutableByteArray src#) soff sz
   = primitive_ (copyMutableByteArrayToAddr# src# (unI# soff) dst# (unI# sz))
-#endif
 
 -- | Copy a slice of a mutable byte array into another, potentially
 -- overlapping array.
 moveByteArray
-  :: PrimMonad m => MutableByteArray (PrimState m)
-                                        -- ^ destination array
-                 -> Int                 -- ^ offset into destination array
-                 -> MutableByteArray (PrimState m)
-                                        -- ^ source array
-                 -> Int                 -- ^ offset into source array
-                 -> Int                 -- ^ number of bytes to copy
-                 -> m ()
+  :: PrimMonad m
+  => MutableByteArray (PrimState m) -- ^ destination array
+  -> Int                            -- ^ offset into destination array
+  -> MutableByteArray (PrimState m) -- ^ source array
+  -> Int                            -- ^ offset into source array
+  -> Int                            -- ^ number of bytes to copy
+  -> m ()
 {-# INLINE moveByteArray #-}
 moveByteArray (MutableByteArray dst#) doff
               (MutableByteArray src#) soff sz
-  = unsafePrimToPrim
-  $ memmove_mba dst# (fromIntegral doff) src# (fromIntegral soff)
-                     (fromIntegral sz)
+  = primitive_ (copyMutableByteArray# src# (unI# soff) dst# (unI# doff) (unI# sz))
 
 -- | Fill a slice of a mutable byte array with a value. The offset and length
 -- are given in elements of type @a@ rather than in bytes.
+--
+-- /Note:/ this function does not do bounds checking.
 setByteArray
-  :: (Prim a, PrimMonad m) => MutableByteArray (PrimState m) -- ^ array to fill
-                           -> Int                 -- ^ offset into array
-                           -> Int                 -- ^ number of values to fill
-                           -> a                   -- ^ value to fill with
-                           -> m ()
+  :: (Prim a, PrimMonad m)
+  => MutableByteArray (PrimState m) -- ^ array to fill
+  -> Int                            -- ^ offset into array
+  -> Int                            -- ^ number of values to fill
+  -> a                              -- ^ value to fill with
+  -> m ()
 {-# INLINE setByteArray #-}
 setByteArray (MutableByteArray dst#) (I# doff#) (I# sz#) x
   = primitive_ (setByteArray# dst# doff# sz# x)
 
 -- | Fill a slice of a mutable byte array with a byte.
+--
+-- /Note:/ this function does not do bounds checking.
 fillByteArray
-  :: PrimMonad m => MutableByteArray (PrimState m)
-                                        -- ^ array to fill
-                 -> Int                 -- ^ offset into array
-                 -> Int                 -- ^ number of bytes to fill
-                 -> Word8               -- ^ byte to fill with
-                 -> m ()
+  :: PrimMonad m
+  => MutableByteArray (PrimState m) -- ^ array to fill
+  -> Int                            -- ^ offset into array
+  -> Int                            -- ^ number of bytes to fill
+  -> Word8                          -- ^ byte to fill with
+  -> m ()
 {-# INLINE fillByteArray #-}
 fillByteArray = setByteArray
 
-foreign import ccall unsafe "primitive-memops.h hsprimitive_memmove"
-  memmove_mba :: MutableByteArray# s -> CInt
-              -> MutableByteArray# s -> CInt
-              -> CSize -> IO ()
-
-instance Data ByteArray where
-  toConstr _ = error "toConstr"
-  gunfold _ _ = error "gunfold"
-  dataTypeOf _ = mkNoRepType "Data.Primitive.ByteArray.ByteArray"
-
-instance Typeable s => Data (MutableByteArray s) where
-  toConstr _ = error "toConstr"
-  gunfold _ _ = error "gunfold"
-  dataTypeOf _ = mkNoRepType "Data.Primitive.ByteArray.MutableByteArray"
-
--- | @since 0.6.3.0
-instance Show ByteArray where
-  showsPrec _ ba =
-      showString "[" . go 0
-    where
-      go i
-        | i < sizeofByteArray ba = comma . showString "0x" . showHex (indexByteArray ba i :: Word8) . go (i+1)
-        | otherwise              = showChar ']'
-        where
-          comma | i == 0    = id
-                | otherwise = showString ", "
-
-
-compareByteArrays :: ByteArray -> ByteArray -> Int -> Ordering
+-- | Lexicographic comparison of equal-length slices into two byte arrays.
+-- This wraps the @compareByteArrays#@ primop, which wraps @memcmp@.
+compareByteArrays
+  :: ByteArray -- ^ array A
+  -> Int       -- ^ offset A, given in bytes
+  -> ByteArray -- ^ array B
+  -> Int       -- ^ offset B, given in bytes
+  -> Int       -- ^ length of the slice, given in bytes
+  -> Ordering
 {-# INLINE compareByteArrays #-}
 #if __GLASGOW_HASKELL__ >= 804
-compareByteArrays (ByteArray ba1#) (ByteArray ba2#) (I# n#) =
-  compare (I# (compareByteArrays# ba1# 0# ba2# 0# n#)) 0
+compareByteArrays (ByteArray ba1#) (I# off1#) (ByteArray ba2#) (I# off2#) (I# n#)
+  = compare (I# (compareByteArrays# ba1# off1# ba2# off2# n#)) 0
 #else
 -- Emulate GHC 8.4's 'GHC.Prim.compareByteArrays#'
-compareByteArrays (ByteArray ba1#) (ByteArray ba2#) (I# n#)
-    = compare (fromCInt (unsafeDupablePerformIO (memcmp_ba ba1# ba2# n))) 0
+compareByteArrays (ByteArray ba1#) (I# off1#) (ByteArray ba2#) (I# off2#) (I# n#)
+  = compare (fromCInt (unsafeDupablePerformIO (memcmp_ba_offs ba1# off1# ba2# off2# n))) 0
   where
     n = fromIntegral (I# n#) :: CSize
     fromCInt = fromIntegral :: CInt -> Int
 
-foreign import ccall unsafe "primitive-memops.h hsprimitive_memcmp"
-  memcmp_ba :: ByteArray# -> ByteArray# -> CSize -> IO CInt
-#endif
-
-
-sameByteArray :: ByteArray# -> ByteArray# -> Bool
-sameByteArray ba1 ba2 =
-    case reallyUnsafePtrEquality# (unsafeCoerce# ba1 :: ()) (unsafeCoerce# ba2 :: ()) of
-#if __GLASGOW_HASKELL__ >= 708
-      r -> isTrue# r
-#else
-      1# -> True
-      0# -> False
+foreign import ccall unsafe "primitive-memops.h hsprimitive_memcmp_offset"
+  memcmp_ba_offs :: ByteArray# -> Int# -> ByteArray# -> Int# -> CSize -> IO CInt
 #endif
 
--- | @since 0.6.3.0
-instance Eq ByteArray where
-  ba1@(ByteArray ba1#) == ba2@(ByteArray ba2#)
-    | sameByteArray ba1# ba2# = True
-    | n1 /= n2 = False
-    | otherwise = compareByteArrays ba1 ba2 n1 == EQ
-    where
-      n1 = sizeofByteArray ba1
-      n2 = sizeofByteArray ba2
-
--- | Non-lexicographic ordering. This compares the lengths of
--- the byte arrays first and uses a lexicographic ordering if
--- the lengths are equal. Subject to change between major versions.
--- 
--- @since 0.6.3.0
-instance Ord ByteArray where
-  ba1@(ByteArray ba1#) `compare` ba2@(ByteArray ba2#)
-    | sameByteArray ba1# ba2# = EQ
-    | n1 /= n2 = n1 `compare` n2
-    | otherwise = compareByteArrays ba1 ba2 n1
-    where
-      n1 = sizeofByteArray ba1
-      n2 = sizeofByteArray ba2
--- Note: On GHC 8.4, the primop compareByteArrays# performs a check for pointer
--- equality as a shortcut, so the check here is actually redundant. However, it
--- is included here because it is likely better to check for pointer equality
--- before checking for length equality. Getting the length requires deferencing
--- the pointers, which could cause accesses to memory that is not in the cache.
--- By contrast, a pointer equality check is always extremely cheap.
+-- | The empty 'ByteArray'.
+emptyByteArray :: ByteArray
+{-# NOINLINE emptyByteArray #-}
+emptyByteArray = runST (newByteArray 0 >>= unsafeFreezeByteArray)
 
-appendByteArray :: ByteArray -> ByteArray -> ByteArray
-appendByteArray a b = runST $ do
-  marr <- newByteArray (sizeofByteArray a + sizeofByteArray b)
-  copyByteArray marr 0 a 0 (sizeofByteArray a)
-  copyByteArray marr (sizeofByteArray a) b 0 (sizeofByteArray b)
-  unsafeFreezeByteArray marr
+emptyByteArray# :: (# #) -> ByteArray#
+{-# NOINLINE emptyByteArray# #-}
+emptyByteArray# _ = case emptyByteArray of ByteArray arr# -> arr#
 
-concatByteArray :: [ByteArray] -> ByteArray
-concatByteArray arrs = runST $ do
-  let len = calcLength arrs 0
-  marr <- newByteArray len
-  pasteByteArrays marr 0 arrs
-  unsafeFreezeByteArray marr
+die :: String -> String -> a
+die fun problem = error $ "Data.Primitive.ByteArray." ++ fun ++ ": " ++ problem
 
-pasteByteArrays :: MutableByteArray s -> Int -> [ByteArray] -> ST s ()
-pasteByteArrays !_ !_ [] = return ()
-pasteByteArrays !marr !ix (x : xs) = do
-  copyByteArray marr ix x 0 (sizeofByteArray x)
-  pasteByteArrays marr (ix + sizeofByteArray x) xs
+-- | Return a newly allocated array with the specified subrange of the
+-- provided array. The provided array should contain the full subrange
+-- specified by the two Ints, but this is not checked.
+cloneByteArray
+  :: ByteArray -- ^ source array
+  -> Int       -- ^ offset into destination array
+  -> Int       -- ^ number of bytes to copy
+  -> ByteArray
+{-# INLINE cloneByteArray #-}
+cloneByteArray src off n = createByteArray n $ \dst ->
+  copyByteArray dst 0 src off n
 
-calcLength :: [ByteArray] -> Int -> Int
-calcLength [] !n = n
-calcLength (x : xs) !n = calcLength xs (sizeofByteArray x + n)
+-- | Return a newly allocated mutable array with the specified subrange of
+-- the provided mutable array. The provided mutable array should contain the
+-- full subrange specified by the two Ints, but this is not checked.
+cloneMutableByteArray :: PrimMonad m
+  => MutableByteArray (PrimState m) -- ^ source array
+  -> Int -- ^ offset into destination array
+  -> Int -- ^ number of bytes to copy
+  -> m (MutableByteArray (PrimState m))
+{-# INLINE cloneMutableByteArray #-}
+cloneMutableByteArray src off n = do
+  dst <- newByteArray n
+  copyMutableByteArray dst 0 src off n
+  return dst
 
-emptyByteArray :: ByteArray
-emptyByteArray = runST (newByteArray 0 >>= unsafeFreezeByteArray)
+-- | Execute the monadic action and freeze the resulting array.
+--
+-- > runByteArray m = runST $ m >>= unsafeFreezeByteArray
+runByteArray
+  :: (forall s. ST s (MutableByteArray s))
+  -> ByteArray
+runByteArray m = ByteArray (runByteArray# m)
 
-replicateByteArray :: Int -> ByteArray -> ByteArray
-replicateByteArray n arr = runST $ do
-  marr <- newByteArray (n * sizeofByteArray arr)
-  let go i = if i < n
-        then do
-          copyByteArray marr (i * sizeofByteArray arr) arr 0 (sizeofByteArray arr)
-          go (i + 1)
-        else return ()
-  go 0
-  unsafeFreezeByteArray marr
+runByteArray#
+  :: (forall s. ST s (MutableByteArray s))
+  -> ByteArray#
+runByteArray# m = case runRW# $ \s ->
+  case unST m s of { (# s', MutableByteArray mary# #) ->
+  unsafeFreezeByteArray# mary# s'} of (# _, ary# #) -> ary#
 
-#if MIN_VERSION_base(4,9,0)
-instance SG.Semigroup ByteArray where
-  (<>) = appendByteArray
-  sconcat = mconcat . F.toList
-  stimes i arr
-    | itgr < 1 = emptyByteArray
-    | itgr <= (fromIntegral (maxBound :: Int)) = replicateByteArray (fromIntegral itgr) arr
-    | otherwise = error "Data.Primitive.ByteArray#stimes: cannot allocate the requested amount of memory"
-    where itgr = toInteger i :: Integer
-#endif
+unST :: ST s a -> State# s -> (# State# s, a #)
+unST (GHCST.ST f) = f
 
-instance Monoid ByteArray where
-  mempty = emptyByteArray
-#if !(MIN_VERSION_base(4,11,0))
-  mappend = appendByteArray
-#endif
-  mconcat = concatByteArray
+-- Create an uninitialized array of the given size in bytes, apply the function
+-- to it, and freeze the result.
+--
+-- /Note:/ this function does not check if the input is non-negative.
+--
+-- @since FIXME
+createByteArray :: Int -> (forall s. MutableByteArray s -> ST s ()) -> ByteArray
+{-# INLINE createByteArray #-}
+createByteArray 0 _ = ByteArray (emptyByteArray# (# #))
+createByteArray n f = runByteArray $ do
+  marr <- newByteArray n
+  f marr
+  pure marr
 
-#if __GLASGOW_HASKELL__ >= 708
--- | @since 0.6.3.0
-instance Exts.IsList ByteArray where
-  type Item ByteArray = Word8
+{- $charElementAccess
+GHC provides two sets of element accessors for 'Char'. One set faithfully
+represents 'Char' as 32-bit words using UTF-32. The other set represents
+'Char' as 8-bit words using Latin-1 (ISO-8859-1), and the write operation
+has undefined behavior for codepoints outside of the ASCII and Latin-1
+blocks. The 'Prim' instance for 'Char' uses the UTF-32 set of operators.
+-}
 
-  toList = foldrByteArray (:) []
-  fromList xs = byteArrayFromListN (length xs) xs
-  fromListN = byteArrayFromListN
-#endif
+-- | Read an 8-bit element from the byte array, interpreting it as a
+-- Latin-1-encoded character. The offset is given in bytes.
+--
+-- /Note:/ this function does not do bounds checking.
+readCharArray :: PrimMonad m => MutableByteArray (PrimState m) -> Int -> m Char
+{-# INLINE readCharArray #-}
+readCharArray (MutableByteArray arr#) (I# i#) = primitive
+  (\s0 -> case readCharArray# arr# i# s0 of
+    (# s1, c #) -> (# s1, C# c #)
+  )
 
-die :: String -> String -> a
-die fun problem = error $ "Data.Primitive.ByteArray." ++ fun ++ ": " ++ problem
+-- | Write a character to the byte array, encoding it with Latin-1 as
+-- a single byte. Behavior is undefined for codepoints outside of the
+-- ASCII and Latin-1 blocks. The offset is given in bytes.
+--
+-- /Note:/ this function does not do bounds checking.
+writeCharArray
+  :: PrimMonad m => MutableByteArray (PrimState m) -> Int -> Char -> m ()
+{-# INLINE writeCharArray #-}
+writeCharArray (MutableByteArray arr#) (I# i#) (C# c)
+  = primitive_ (writeCharArray# arr# i# c)
 
+-- | Read an 8-bit element from the byte array, interpreting it as a
+-- Latin-1-encoded character. The offset is given in bytes.
+--
+-- /Note:/ this function does not do bounds checking.
+indexCharArray :: ByteArray -> Int -> Char
+{-# INLINE indexCharArray #-}
+indexCharArray (ByteArray arr#) (I# i#) = C# (indexCharArray# arr# i#)
diff --git a/Data/Primitive/Internal/Compat.hs b/Data/Primitive/Internal/Compat.hs
deleted file mode 100644
--- a/Data/Primitive/Internal/Compat.hs
+++ /dev/null
@@ -1,38 +0,0 @@
-{-# LANGUAGE CPP, MagicHash #-}
-
--- |
--- Module      : Data.Primitive.Internal.Compat
--- Copyright   : (c) Roman Leshchinskiy 2011-2012
--- License     : BSD-style
---
--- Maintainer  : Roman Leshchinskiy <rl@cse.unsw.edu.au>
--- Portability : non-portable
---
--- Compatibility functions
---
-
-module Data.Primitive.Internal.Compat (
-    isTrue#
-  , mkNoRepType
-  ) where
-
-#if MIN_VERSION_base(4,2,0)
-import Data.Data (mkNoRepType)
-#else
-import Data.Data (mkNorepType)
-#endif
-
-#if MIN_VERSION_base(4,7,0)
-import GHC.Exts (isTrue#)
-#endif
-
-
-
-#if !MIN_VERSION_base(4,2,0)
-mkNoRepType = mkNorepType
-#endif
-
-#if !MIN_VERSION_base(4,7,0)
-isTrue# :: Bool -> Bool
-isTrue# b = b
-#endif
diff --git a/Data/Primitive/Internal/Operations.hs b/Data/Primitive/Internal/Operations.hs
--- a/Data/Primitive/Internal/Operations.hs
+++ b/Data/Primitive/Internal/Operations.hs
@@ -1,4 +1,9 @@
-{-# LANGUAGE MagicHash, UnliftedFFITypes #-}
+{-# LANGUAGE CPP, MagicHash, UnliftedFFITypes, UnboxedTuples #-}
+{-# LANGUAGE RankNTypes, KindSignatures, ScopedTypeVariables #-} 
+{-# LANGUAGE DataKinds #-}
+#if __GLASGOW_HASKELL__ < 806
+{-# LANGUAGE TypeInType #-}
+#endif
 
 -- |
 -- Module      : Data.Primitive.Internal.Operations
@@ -8,50 +13,87 @@
 -- Maintainer  : Roman Leshchinskiy <rl@cse.unsw.edu.au>
 -- Portability : non-portable
 --
--- Internal operations
---
-
+-- Internal operations.
 
 module Data.Primitive.Internal.Operations (
   setWord8Array#, setWord16Array#, setWord32Array#,
   setWord64Array#, setWordArray#,
   setInt8Array#, setInt16Array#, setInt32Array#,
   setInt64Array#, setIntArray#,
-  setAddrArray#, setFloatArray#, setDoubleArray#, setWideCharArray#,
+  setAddrArray#, setStablePtrArray#, setFloatArray#, setDoubleArray#,
+  setWideCharArray#,
 
   setWord8OffAddr#, setWord16OffAddr#, setWord32OffAddr#,
   setWord64OffAddr#, setWordOffAddr#,
   setInt8OffAddr#, setInt16OffAddr#, setInt32OffAddr#,
   setInt64OffAddr#, setIntOffAddr#,
-  setAddrOffAddr#, setFloatOffAddr#, setDoubleOffAddr#, setWideCharOffAddr#
+  setAddrOffAddr#, setFloatOffAddr#, setDoubleOffAddr#, setWideCharOffAddr#,
+  setStablePtrOffAddr#
+
+
+#if defined(HAVE_KEEPALIVE)
+  , keepAliveLiftedLifted#
+  , keepAliveUnliftedLifted#
+#endif
+  , mutableByteArrayContentsShim
+  , UnliftedType
 ) where
 
 import Data.Primitive.MachDeps (Word64_#, Int64_#)
 import Foreign.C.Types
-import GHC.Prim
+import GHC.Exts
 
+#if defined(HAVE_KEEPALIVE)
+import Data.Kind (Type)
+#endif
+
+
+#if __GLASGOW_HASKELL__ >= 902
 foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Word8"
+  setWord8Array# :: MutableByteArray# s -> CPtrdiff -> CSize -> Word8# -> IO ()
+foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Word16"
+  setWord16Array# :: MutableByteArray# s -> CPtrdiff -> CSize -> Word16# -> IO ()
+foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Word32"
+  setWord32Array# :: MutableByteArray# s -> CPtrdiff -> CSize -> Word32# -> IO ()
+#else
+foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Word8"
   setWord8Array# :: MutableByteArray# s -> CPtrdiff -> CSize -> Word# -> IO ()
 foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Word16"
   setWord16Array# :: MutableByteArray# s -> CPtrdiff -> CSize -> Word# -> IO ()
 foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Word32"
   setWord32Array# :: MutableByteArray# s -> CPtrdiff -> CSize -> Word# -> IO ()
+#endif
+
 foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Word64"
   setWord64Array# :: MutableByteArray# s -> CPtrdiff -> CSize -> Word64_# -> IO ()
 foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Word"
   setWordArray# :: MutableByteArray# s -> CPtrdiff -> CSize -> Word# -> IO ()
+
+#if __GLASGOW_HASKELL__ >= 902
 foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Word8"
+  setInt8Array# :: MutableByteArray# s -> CPtrdiff -> CSize -> Int8# -> IO ()
+foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Word16"
+  setInt16Array# :: MutableByteArray# s -> CPtrdiff -> CSize -> Int16# -> IO ()
+foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Word32"
+  setInt32Array# :: MutableByteArray# s -> CPtrdiff -> CSize -> Int32# -> IO ()
+#else
+foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Word8"
   setInt8Array# :: MutableByteArray# s -> CPtrdiff -> CSize -> Int# -> IO ()
 foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Word16"
   setInt16Array# :: MutableByteArray# s -> CPtrdiff -> CSize -> Int# -> IO ()
 foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Word32"
   setInt32Array# :: MutableByteArray# s -> CPtrdiff -> CSize -> Int# -> IO ()
+#endif
+
 foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Word64"
   setInt64Array# :: MutableByteArray# s -> CPtrdiff -> CSize -> Int64_# -> IO ()
 foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Word"
   setIntArray# :: MutableByteArray# s -> CPtrdiff -> CSize -> Int# -> IO ()
+
 foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Ptr"
   setAddrArray# :: MutableByteArray# s -> CPtrdiff -> CSize -> Addr# -> IO ()
+foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Ptr"
+  setStablePtrArray# :: MutableByteArray# s -> CPtrdiff -> CSize -> StablePtr# a -> IO ()
 foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Float"
   setFloatArray# :: MutableByteArray# s -> CPtrdiff -> CSize -> Float# -> IO ()
 foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Double"
@@ -59,32 +101,105 @@
 foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Char"
   setWideCharArray# :: MutableByteArray# s -> CPtrdiff -> CSize -> Char# -> IO ()
 
+#if __GLASGOW_HASKELL__ >= 902
 foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Word8"
+  setWord8OffAddr# :: Addr# -> CPtrdiff -> CSize -> Word8# -> IO ()
+foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Word16"
+  setWord16OffAddr# :: Addr# -> CPtrdiff -> CSize -> Word16# -> IO ()
+foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Word32"
+  setWord32OffAddr# :: Addr# -> CPtrdiff -> CSize -> Word32# -> IO ()
+#else
+foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Word8"
   setWord8OffAddr# :: Addr# -> CPtrdiff -> CSize -> Word# -> IO ()
 foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Word16"
   setWord16OffAddr# :: Addr# -> CPtrdiff -> CSize -> Word# -> IO ()
 foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Word32"
   setWord32OffAddr# :: Addr# -> CPtrdiff -> CSize -> Word# -> IO ()
+#endif
+
 foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Word64"
   setWord64OffAddr# :: Addr# -> CPtrdiff -> CSize -> Word64_# -> IO ()
 foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Word"
   setWordOffAddr# :: Addr# -> CPtrdiff -> CSize -> Word# -> IO ()
+
+#if __GLASGOW_HASKELL__ >= 902
 foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Word8"
+  setInt8OffAddr# :: Addr# -> CPtrdiff -> CSize -> Int8# -> IO ()
+foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Word16"
+  setInt16OffAddr# :: Addr# -> CPtrdiff -> CSize -> Int16# -> IO ()
+foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Word32"
+  setInt32OffAddr# :: Addr# -> CPtrdiff -> CSize -> Int32# -> IO ()
+#else
+foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Word8"
   setInt8OffAddr# :: Addr# -> CPtrdiff -> CSize -> Int# -> IO ()
 foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Word16"
   setInt16OffAddr# :: Addr# -> CPtrdiff -> CSize -> Int# -> IO ()
 foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Word32"
   setInt32OffAddr# :: Addr# -> CPtrdiff -> CSize -> Int# -> IO ()
+#endif
+
 foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Word64"
   setInt64OffAddr# :: Addr# -> CPtrdiff -> CSize -> Int64_# -> IO ()
 foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Word"
   setIntOffAddr# :: Addr# -> CPtrdiff -> CSize -> Int# -> IO ()
+
 foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Ptr"
   setAddrOffAddr# :: Addr# -> CPtrdiff -> CSize -> Addr# -> IO ()
+foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Ptr"
+  setStablePtrOffAddr# :: Addr# -> CPtrdiff -> CSize -> StablePtr# a -> IO ()
 foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Float"
   setFloatOffAddr# :: Addr# -> CPtrdiff -> CSize -> Float# -> IO ()
 foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Double"
   setDoubleOffAddr# :: Addr# -> CPtrdiff -> CSize -> Double# -> IO ()
 foreign import ccall unsafe "primitive-memops.h hsprimitive_memset_Char"
   setWideCharOffAddr# :: Addr# -> CPtrdiff -> CSize -> Char# -> IO ()
+
+#if defined(HAVE_KEEPALIVE)
+keepAliveLiftedLifted# :: forall (s :: Type) (a :: Type) (b :: Type).
+     a
+  -> State# s
+  -> (State# s -> (# State# s, b #))
+  -> (# State# s, b #)
+{-# inline keepAliveLiftedLifted# #-}
+keepAliveLiftedLifted# x s0 f =
+  (unsafeCoerce# :: (# State# RealWorld, b #) -> (# State# s, b #))
+    ( keepAlive# x
+      ((unsafeCoerce# :: State# s -> State# RealWorld) s0)
+      ((unsafeCoerce# ::
+         (State# s -> (# State# s, b #)) ->
+         (State# RealWorld -> (# State# RealWorld, b #))
+       ) f)
+    )
+
+keepAliveUnliftedLifted# :: forall (s :: Type) (a :: UnliftedType) (b :: Type).
+     a
+  -> State# s
+  -> (State# s -> (# State# s, b #))
+  -> (# State# s, b #)
+{-# inline keepAliveUnliftedLifted# #-}
+keepAliveUnliftedLifted# x s0 f =
+  (unsafeCoerce# :: (# State# RealWorld, b #) -> (# State# s, b #))
+    ( keepAlive# x
+      ((unsafeCoerce# :: State# s -> State# RealWorld) s0)
+      ((unsafeCoerce# ::
+         (State# s -> (# State# s, b #)) ->
+         (State# RealWorld -> (# State# RealWorld, b #))
+       ) f)
+    )
+#endif
+
+#if __GLASGOW_HASKELL__ < 802
+type UnliftedType = TYPE 'PtrRepUnlifted
+#elif __GLASGOW_HASKELL__ < 902
+type UnliftedType = TYPE 'UnliftedRep
+#endif
+
+mutableByteArrayContentsShim :: MutableByteArray# s -> Addr#
+{-# INLINE mutableByteArrayContentsShim #-}
+mutableByteArrayContentsShim x =
+#if __GLASGOW_HASKELL__ >= 902
+  mutableByteArrayContents# x
+#else
+  byteArrayContents# (unsafeCoerce# x)
+#endif
 
diff --git a/Data/Primitive/Internal/Read.hs b/Data/Primitive/Internal/Read.hs
new file mode 100644
--- /dev/null
+++ b/Data/Primitive/Internal/Read.hs
@@ -0,0 +1,27 @@
+module Data.Primitive.Internal.Read
+  ( Tag(..)
+  , lexTag
+  ) where
+
+import Data.Char (isDigit)
+import Text.ParserCombinators.ReadP
+
+data Tag = FromListTag | FromListNTag
+
+-- Why don't we just use lexP? The general problem with lexP is that
+-- it doesn't always fail as fast as we might like. It will
+-- happily read to the end of an absurdly long lexeme (e.g., a 200MB string
+-- literal) before returning, at which point we'll immediately discard
+-- the result because it's not an identifier. Doing the job ourselves, we
+-- can see very quickly when we've run into a problem. We should also get
+-- a slight efficiency boost by going through the string just once.
+lexTag :: ReadP Tag
+lexTag = do
+  _ <- string "fromList"
+  s <- look
+  case s of
+    'N':c:_
+      | isDigit c
+      -> fail "" -- We have fromListN3 or similar
+      | otherwise -> FromListNTag <$ get -- Skip the 'N'
+    _ -> return FromListTag
diff --git a/Data/Primitive/MVar.hs b/Data/Primitive/MVar.hs
--- a/Data/Primitive/MVar.hs
+++ b/Data/Primitive/MVar.hs
@@ -1,4 +1,3 @@
-{-# LANGUAGE BangPatterns #-}
 {-# LANGUAGE CPP #-}
 {-# LANGUAGE MagicHash #-}
 {-# LANGUAGE UnboxedTuples #-}
@@ -8,14 +7,17 @@
 -- License     : BSD2
 -- Portability : non-portable
 --
--- Primitive operations on @MVar@. This module provides a similar interface
+-- Primitive operations on 'MVar'. This module provides a similar interface
 -- to "Control.Concurrent.MVar". However, the functions are generalized to
 -- work in any 'PrimMonad' instead of only working in 'IO'. Note that all
 -- of the functions here are completely deterministic. Users of 'MVar' are
 -- responsible for designing abstractions that guarantee determinism in
 -- the presence of multi-threading.
 --
+-- For a more detailed explanation, see "Control.Concurrent.MVar".
+--
 -- @since 0.6.4.0
+
 module Data.Primitive.MVar
   ( MVar(..)
   , newMVar
@@ -30,14 +32,12 @@
   ) where
 
 import Control.Monad.Primitive
-import Data.Primitive.Internal.Compat (isTrue#)
-import GHC.Exts (MVar#,newMVar#,takeMVar#,sameMVar#,putMVar#,tryTakeMVar#,
-  isEmptyMVar#,tryPutMVar#,(/=#))
-
-#if __GLASGOW_HASKELL__ >= 708
-import GHC.Exts (readMVar#,tryReadMVar#)
-#endif
+import GHC.Exts
+  ( MVar#, newMVar#, takeMVar#, sameMVar#, putMVar#, tryTakeMVar#, isEmptyMVar#, tryPutMVar#, (/=#)
+  , readMVar#, tryReadMVar#, isTrue# )
 
+-- | A synchronizing variable, used for communication between concurrent threads.
+-- It can be thought of as a box, which may be empty or full.
 data MVar s a = MVar (MVar# s a)
 
 instance Eq (MVar s a) where
@@ -49,54 +49,62 @@
   case newMVar# s# of
     (# s2#, svar# #) -> (# s2#, MVar svar# #)
 
-
 -- | Create a new 'MVar' that holds the supplied argument.
 newMVar :: PrimMonad m => a -> m (MVar (PrimState m) a)
-newMVar value =
-  newEmptyMVar >>= \ mvar ->
-  putMVar mvar value >>
+newMVar value = do
+  mvar <- newEmptyMVar
+  putMVar mvar value
   return mvar
 
--- | Return the contents of the 'MVar'.  If the 'MVar' is currently
--- empty, 'takeMVar' will wait until it is full.  After a 'takeMVar',
+-- | Return the contents of the 'MVar'. If the 'MVar' is currently
+-- empty, 'takeMVar' will wait until it is full. After a 'takeMVar',
 -- the 'MVar' is left empty.
+--
+-- There are two further important properties of 'takeMVar':
+--
+-- * 'takeMVar' is single-wakeup. That is, if there are multiple
+--   threads blocked in 'takeMVar', and the 'MVar' becomes full,
+--   only one thread will be woken up. The runtime guarantees that
+--   the woken thread completes its 'takeMVar' operation.
+-- * When multiple threads are blocked on an 'MVar', they are
+--   woken up in FIFO order. This is useful for providing
+--   fairness properties of abstractions built using 'MVar's.
 takeMVar :: PrimMonad m => MVar (PrimState m) a -> m a
 takeMVar (MVar mvar#) = primitive $ \ s# -> takeMVar# mvar# s#
 
--- | Atomically read the contents of an 'MVar'.  If the 'MVar' is
+-- | Atomically read the contents of an 'MVar'. If the 'MVar' is
 -- currently empty, 'readMVar' will wait until it is full.
 -- 'readMVar' is guaranteed to receive the next 'putMVar'.
 --
 -- /Multiple Wakeup:/ 'readMVar' is multiple-wakeup, so when multiple readers
 -- are blocked on an 'MVar', all of them are woken up at the same time.
 --
--- /Compatibility note:/ On GHCs prior to 7.8, 'readMVar' is a combination
--- of 'takeMVar' and 'putMVar'. Consequently, its behavior differs in the
--- following ways:
---
 -- * It is single-wakeup instead of multiple-wakeup.
 -- * It might not receive the value from the next call to 'putMVar' if
 --   there is already a pending thread blocked on 'takeMVar'.
 -- * If another thread puts a value in the 'MVar' in between the
 --   calls to 'takeMVar' and 'putMVar', that value may be overridden.
 readMVar :: PrimMonad m => MVar (PrimState m) a -> m a
-#if __GLASGOW_HASKELL__ >= 708
 readMVar (MVar mvar#) = primitive $ \ s# -> readMVar# mvar# s#
-#else
-readMVar mv = do
-  a <- takeMVar mv
-  putMVar mv a
-  return a
-#endif
 
--- |Put a value into an 'MVar'.  If the 'MVar' is currently full,
+-- | Put a value into an 'MVar'. If the 'MVar' is currently full,
 -- 'putMVar' will wait until it becomes empty.
+--
+-- There are two further important properties of 'putMVar':
+--
+-- * 'putMVar' is single-wakeup. That is, if there are multiple
+--   threads blocked in 'putMVar', and the 'MVar' becomes empty,
+--   only one thread will be woken up. The runtime guarantees that
+--   the woken thread completes its 'putMVar' operation.
+-- * When multiple threads are blocked on an 'MVar', they are
+--   woken up in FIFO order. This is useful for providing
+--   fairness properties of abstractions built using 'MVar's.
 putMVar :: PrimMonad m => MVar (PrimState m) a -> a -> m ()
 putMVar (MVar mvar#) x = primitive_ (putMVar# mvar# x)
 
--- |A non-blocking version of 'takeMVar'.  The 'tryTakeMVar' function
+-- | A non-blocking version of 'takeMVar'. The 'tryTakeMVar' function
 -- returns immediately, with 'Nothing' if the 'MVar' was empty, or
--- @'Just' a@ if the 'MVar' was full with contents @a@.  After 'tryTakeMVar',
+-- @'Just' a@ if the 'MVar' was full with contents @a@. After 'tryTakeMVar',
 -- the 'MVar' is left empty.
 tryTakeMVar :: PrimMonad m => MVar (PrimState m) a -> m (Maybe a)
 tryTakeMVar (MVar m) = primitive $ \ s ->
@@ -104,8 +112,7 @@
     (# s', 0#, _ #) -> (# s', Nothing #) -- MVar is empty
     (# s', _,  a #) -> (# s', Just a  #) -- MVar is full
 
-
--- |A non-blocking version of 'putMVar'.  The 'tryPutMVar' function
+-- | A non-blocking version of 'putMVar'. The 'tryPutMVar' function
 -- attempts to put the value @a@ into the 'MVar', returning 'True' if
 -- it was successful, or 'False' otherwise.
 tryPutMVar :: PrimMonad m => MVar (PrimState m) a -> a -> m Bool
@@ -114,41 +121,27 @@
         (# s, 0# #) -> (# s, False #)
         (# s, _  #) -> (# s, True #)
 
--- | A non-blocking version of 'readMVar'.  The 'tryReadMVar' function
+-- | A non-blocking version of 'readMVar'. The 'tryReadMVar' function
 -- returns immediately, with 'Nothing' if the 'MVar' was empty, or
 -- @'Just' a@ if the 'MVar' was full with contents @a@.
 --
--- /Compatibility note:/ On GHCs prior to 7.8, 'tryReadMVar' is a combination
--- of 'tryTakeMVar' and 'putMVar'. Consequently, its behavior differs in the
--- following ways:
---
 -- * It is single-wakeup instead of multiple-wakeup.
 -- * In the presence of other threads calling 'putMVar', 'tryReadMVar'
 --   may block.
 -- * If another thread puts a value in the 'MVar' in between the
 --   calls to 'tryTakeMVar' and 'putMVar', that value may be overridden.
 tryReadMVar :: PrimMonad m => MVar (PrimState m) a -> m (Maybe a)
-#if __GLASGOW_HASKELL__ >= 708
 tryReadMVar (MVar m) = primitive $ \ s ->
     case tryReadMVar# m s of
         (# s', 0#, _ #) -> (# s', Nothing #)      -- MVar is empty
         (# s', _,  a #) -> (# s', Just a  #)      -- MVar is full
-#else
-tryReadMVar mv = do
-  ma <- tryTakeMVar mv
-  case ma of
-    Just a -> do
-      putMVar mv a
-      return (Just a)
-    Nothing -> return Nothing
-#endif
 
 -- | Check whether a given 'MVar' is empty.
 --
--- Notice that the boolean value returned  is just a snapshot of
--- the state of the MVar. By the time you get to react on its result,
--- the MVar may have been filled (or emptied) - so be extremely
--- careful when using this operation.   Use 'tryTakeMVar' instead if possible.
+-- Notice that the boolean value returned is just a snapshot of
+-- the state of the 'MVar'. By the time you get to react on its result,
+-- the 'MVar' may have been filled (or emptied) - so be extremely
+-- careful when using this operation. Use 'tryTakeMVar' instead if possible.
 isEmptyMVar :: PrimMonad m => MVar (PrimState m) a -> m Bool
 isEmptyMVar (MVar mv#) = primitive $ \ s# ->
   case isEmptyMVar# mv# s# of
diff --git a/Data/Primitive/MachDeps.hs b/Data/Primitive/MachDeps.hs
--- a/Data/Primitive/MachDeps.hs
+++ b/Data/Primitive/MachDeps.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE CPP, MagicHash #-}
+
 -- |
 -- Module      : Data.Primitive.MachDeps
 -- Copyright   : (c) Roman Leshchinskiy 2009-2012
@@ -7,14 +8,13 @@
 -- Maintainer  : Roman Leshchinskiy <rl@cse.unsw.edu.au>
 -- Portability : non-portable
 --
--- Machine-dependent constants
---
+-- Machine-dependent constants.
 
 module Data.Primitive.MachDeps where
 
 #include "MachDeps.h"
 
-import GHC.Prim
+import GHC.Exts
 
 sIZEOF_CHAR,
  aLIGNMENT_CHAR,
@@ -113,11 +113,10 @@
 sIZEOF_WORD64 = SIZEOF_WORD64
 aLIGNMENT_WORD64 = ALIGNMENT_WORD64
 
-#if WORD_SIZE_IN_BITS == 32
+#if WORD_SIZE_IN_BITS == 32 || __GLASGOW_HASKELL__ >= 903
 type Word64_# = Word64#
 type Int64_# = Int64#
 #else
 type Word64_# = Word#
 type Int64_# = Int#
 #endif
-
diff --git a/Data/Primitive/MutVar.hs b/Data/Primitive/MutVar.hs
--- a/Data/Primitive/MutVar.hs
+++ b/Data/Primitive/MutVar.hs
@@ -1,4 +1,4 @@
-{-# LANGUAGE MagicHash, UnboxedTuples, DeriveDataTypeable #-}
+{-# LANGUAGE MagicHash, UnboxedTuples, DeriveDataTypeable, CPP #-}
 
 -- |
 -- Module      : Data.Primitive.MutVar
@@ -8,8 +8,9 @@
 -- Maintainer  : Roman Leshchinskiy <rl@cse.unsw.edu.au>
 -- Portability : non-portable
 --
--- Primitive boxed mutable variables
---
+-- Primitive boxed mutable variables. This is a generalization of
+-- "Data.IORef", "Data.STRef" and "Data.STRef.Lazy" to work in
+-- any 'PrimMonad'.
 
 module Data.Primitive.MutVar (
   MutVar(..),
@@ -18,16 +19,24 @@
   readMutVar,
   writeMutVar,
 
+  -- * Modify
   atomicModifyMutVar,
   atomicModifyMutVar',
   modifyMutVar,
-  modifyMutVar'
+  modifyMutVar',
+  -- * Interop with STRef and IORef
+  mutVarFromIORef,
+  mutVarToIORef,
+  mutVarFromSTRef,
+  mutVarToSTRef
 ) where
 
 import Control.Monad.Primitive ( PrimMonad(..), primitive_ )
-import GHC.Prim ( MutVar#, sameMutVar#, newMutVar#,
-                  readMutVar#, writeMutVar#, atomicModifyMutVar# )
-import Data.Primitive.Internal.Compat ( isTrue# )
+import GHC.IORef (IORef(IORef))
+import GHC.STRef (STRef(STRef))
+import GHC.Exts ( MutVar#, sameMutVar#, newMutVar#
+                , readMutVar#, writeMutVar#, atomicModifyMutVar#
+                , isTrue#, RealWorld)
 import Data.Typeable ( Typeable )
 
 -- | A 'MutVar' behaves like a single-element mutable array associated
@@ -38,25 +47,38 @@
 instance Eq (MutVar s a) where
   MutVar mva# == MutVar mvb# = isTrue# (sameMutVar# mva# mvb#)
 
--- | Create a new 'MutVar' with the specified initial value
+-- | Create a new 'MutVar' with the specified initial value.
 newMutVar :: PrimMonad m => a -> m (MutVar (PrimState m) a)
 {-# INLINE newMutVar #-}
 newMutVar initialValue = primitive $ \s# ->
   case newMutVar# initialValue s# of
     (# s'#, mv# #) -> (# s'#, MutVar mv# #)
 
--- | Read the value of a 'MutVar'
+-- | Read the value of a 'MutVar'.
 readMutVar :: PrimMonad m => MutVar (PrimState m) a -> m a
 {-# INLINE readMutVar #-}
 readMutVar (MutVar mv#) = primitive (readMutVar# mv#)
 
--- | Write a new value into a 'MutVar'
+-- | Write a new value into a 'MutVar'.
 writeMutVar :: PrimMonad m => MutVar (PrimState m) a -> a -> m ()
 {-# INLINE writeMutVar #-}
 writeMutVar (MutVar mv#) newValue = primitive_ (writeMutVar# mv# newValue)
 
--- | Atomically mutate the contents of a 'MutVar'
-atomicModifyMutVar :: PrimMonad m => MutVar (PrimState m) a -> (a -> (a,b)) -> m b
+-- | Atomically mutate the contents of a 'MutVar'.
+--
+-- This function is useful for using 'MutVar' in a safe way in a multithreaded program.
+-- If you only have one 'MutVar', then using 'atomicModifyMutVar' to access and modify
+-- it will prevent race conditions.
+--
+-- Extending the atomicity to multiple 'MutVar's is problematic,
+-- so if you need to do anything more complicated,
+-- using 'Data.Primitive.MVar.MVar' instead is a good idea.
+--
+-- 'atomicModifyMutVar' does not apply the function strictly. This means if a program
+-- calls 'atomicModifyMutVar' many times, but seldom uses the value, thunks will pile up
+-- in memory resulting in a space leak.
+-- To avoid this problem, use 'atomicModifyMutVar'' instead.
+atomicModifyMutVar :: PrimMonad m => MutVar (PrimState m) a -> (a -> (a, b)) -> m b
 {-# INLINE atomicModifyMutVar #-}
 atomicModifyMutVar (MutVar mv#) f = primitive $ atomicModifyMutVar# mv# f
 
@@ -68,19 +90,44 @@
   b <- atomicModifyMutVar mv force
   b `seq` return b
   where
-    force x = let (a, b) = f x in (a, a `seq` b)
+    force x = case f x of
+                v@(x', _) -> x' `seq` v
 
--- | Mutate the contents of a 'MutVar'
+-- | Mutate the contents of a 'MutVar'.
+--
+-- 'modifyMutVar' does not apply the function strictly. This means if a program
+-- calls 'modifyMutVar' many times, but seldom uses the value, thunks will pile up
+-- in memory resulting in a space leak.
+-- To avoid this problem, use 'modifyMutVar'' instead.
 modifyMutVar :: PrimMonad m => MutVar (PrimState m) a -> (a -> a) -> m ()
 {-# INLINE modifyMutVar #-}
 modifyMutVar (MutVar mv#) g = primitive_ $ \s# ->
   case readMutVar# mv# s# of
     (# s'#, a #) -> writeMutVar# mv# (g a) s'#
 
--- | Strict version of 'modifyMutVar'
+-- | Strict version of 'modifyMutVar'.
 modifyMutVar' :: PrimMonad m => MutVar (PrimState m) a -> (a -> a) -> m ()
 {-# INLINE modifyMutVar' #-}
 modifyMutVar' (MutVar mv#) g = primitive_ $ \s# ->
   case readMutVar# mv# s# of
     (# s'#, a #) -> let a' = g a in a' `seq` writeMutVar# mv# a' s'#
 
+-- | Convert 'MutVar' to 'IORef'
+mutVarToIORef :: MutVar RealWorld a -> IORef a
+{-# INLINE mutVarToIORef #-}
+mutVarToIORef (MutVar mv#) = IORef (STRef mv#)
+
+-- | Convert 'MutVar' to 'IORef'
+mutVarFromIORef :: IORef a -> MutVar RealWorld a
+{-# INLINE mutVarFromIORef #-}
+mutVarFromIORef (IORef (STRef mv#)) = MutVar mv#
+
+-- | Convert 'MutVar' to 'STRef'
+mutVarToSTRef :: MutVar s a -> STRef s a
+{-# INLINE mutVarToSTRef #-}
+mutVarToSTRef (MutVar mv#) = STRef mv#
+
+-- | Convert 'MutVar' to 'STRef'
+mutVarFromSTRef :: STRef s a -> MutVar s a
+{-# INLINE mutVarFromSTRef #-}
+mutVarFromSTRef (STRef mv#) = MutVar mv#
diff --git a/Data/Primitive/PrimArray.hs b/Data/Primitive/PrimArray.hs
--- a/Data/Primitive/PrimArray.hs
+++ b/Data/Primitive/PrimArray.hs
@@ -4,9 +4,10 @@
 {-# LANGUAGE RankNTypes #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE TypeApplications #-}
 {-# LANGUAGE UnboxedTuples #-}
-
-{-# OPTIONS_GHC -Wall #-}
+{-# LANGUAGE TemplateHaskellQuotes #-}
+{-# LANGUAGE RoleAnnotations #-}
 
 -- |
 -- Module      : Data.Primitive.PrimArray
@@ -16,46 +17,60 @@
 -- Maintainer  : Roman Leshchinskiy <rl@cse.unsw.edu.au>
 -- Portability : non-portable
 --
--- Arrays of unboxed primitive types. The function provided by this module
--- match the behavior of those provided by @Data.Primitive.ByteArray@, and
+-- Arrays of unboxed primitive types. The functions provided by this module
+-- match the behavior of those provided by "Data.Primitive.ByteArray", and
 -- the underlying types and primops that back them are the same.
 -- However, the type constructors 'PrimArray' and 'MutablePrimArray' take one additional
--- argument than their respective counterparts 'ByteArray' and 'MutableByteArray'.
+-- argument compared to their respective counterparts 'ByteArray' and 'Data.Primitive.ByteArray.MutableByteArray'.
 -- This argument is used to designate the type of element in the array.
--- Consequently, all function this modules accepts length and incides in
+-- Consequently, all functions in this module accept length and indices in
 -- terms of elements, not bytes.
 --
 -- @since 0.6.4.0
+
 module Data.Primitive.PrimArray
   ( -- * Types
     PrimArray(..)
   , MutablePrimArray(..)
     -- * Allocation
   , newPrimArray
+  , newPinnedPrimArray
+  , newAlignedPinnedPrimArray
   , resizeMutablePrimArray
-#if __GLASGOW_HASKELL__ >= 710
   , shrinkMutablePrimArray
-#endif
     -- * Element Access
   , readPrimArray
   , writePrimArray
   , indexPrimArray
     -- * Freezing and Thawing
+  , freezePrimArray
+  , thawPrimArray
+  , runPrimArray
+  , createPrimArray
   , unsafeFreezePrimArray
   , unsafeThawPrimArray
     -- * Block Operations
   , copyPrimArray
   , copyMutablePrimArray
-#if __GLASGOW_HASKELL__ >= 708
   , copyPrimArrayToPtr
   , copyMutablePrimArrayToPtr
-#endif
+  , copyPtrToMutablePrimArray
+  , clonePrimArray
+  , cloneMutablePrimArray
   , setPrimArray
     -- * Information
   , sameMutablePrimArray
   , getSizeofMutablePrimArray
   , sizeofMutablePrimArray
   , sizeofPrimArray
+  , primArrayContents
+  , withPrimArrayContents
+  , mutablePrimArrayContents
+  , withMutablePrimArrayContents
+#if __GLASGOW_HASKELL__ >= 802
+  , isPrimArrayPinned
+  , isMutablePrimArrayPinned
+#endif
     -- * List Conversion
   , primArrayToList
   , primArrayFromList
@@ -70,6 +85,7 @@
   , traversePrimArray_
   , itraversePrimArray_
     -- * Map/Create
+  , emptyPrimArray
   , mapPrimArray
   , imapPrimArray
   , generatePrimArray
@@ -78,6 +94,7 @@
   , mapMaybePrimArray
     -- * Effectful Map/Create
     -- $effectfulMapCreate
+
     -- ** Lazy Applicative
   , traversePrimArray
   , itraversePrimArray
@@ -94,62 +111,78 @@
   , mapMaybePrimArrayP
   ) where
 
-import GHC.Prim
-import GHC.Base ( Int(..) )
-import GHC.Exts (build)
-import GHC.Ptr
-import Data.Primitive.Internal.Compat (isTrue#)
+import GHC.Exts
 import Data.Primitive.Types
 import Data.Primitive.ByteArray (ByteArray(..))
-import Data.Monoid (Monoid(..),(<>))
-import Control.Applicative
+import Data.Proxy
+#if !MIN_VERSION_base(4,18,0)
+import Control.Applicative (liftA2)
+#endif
+import Control.DeepSeq
+import Control.Monad (when)
 import Control.Monad.Primitive
 import Control.Monad.ST
 import qualified Data.List as L
 import qualified Data.Primitive.ByteArray as PB
 import qualified Data.Primitive.Types as PT
+import qualified GHC.ST as GHCST
+import Language.Haskell.TH.Syntax (Lift (..))
 
-#if MIN_VERSION_base(4,7,0)
-import GHC.Exts (IsList(..))
-#endif
+import Data.Semigroup
 
-#if MIN_VERSION_base(4,9,0)
-import Data.Semigroup (Semigroup)
-import qualified Data.Semigroup as SG
+#if __GLASGOW_HASKELL__ >= 802
+import qualified GHC.Exts as Exts
 #endif
 
+import Data.Primitive.Internal.Operations (mutableByteArrayContentsShim)
+
 -- | Arrays of unboxed elements. This accepts types like 'Double', 'Char',
--- 'Int', and 'Word', as well as their fixed-length variants ('Word8',
--- 'Word16', etc.). Since the elements are unboxed, a 'PrimArray' is strict
--- in its elements. This differs from the behavior of 'Array', which is lazy
--- in its elements.
+-- 'Int' and 'Word', as well as their fixed-length variants ('Data.Word.Word8',
+-- 'Data.Word.Word16', etc.). Since the elements are unboxed, a 'PrimArray' is
+-- strict in its elements. This differs from the behavior of
+-- 'Data.Primitive.Array.Array', which is lazy in its elements.
 data PrimArray a = PrimArray ByteArray#
 
+type role PrimArray nominal
+
+instance Lift (PrimArray a) where
+#if MIN_VERSION_template_haskell(2,16,0)
+  liftTyped ary = [|| byteArrayToPrimArray ba ||]
+#else
+  lift ary = [| byteArrayToPrimArray ba |]
+#endif
+    where
+      ba = primArrayToByteArray ary
+
+instance NFData (PrimArray a) where
+  rnf (PrimArray _) = ()
+
 -- | Mutable primitive arrays associated with a primitive state token.
 -- These can be written to and read from in a monadic context that supports
--- sequencing such as 'IO' or 'ST'. Typically, a mutable primitive array will
--- be built and then convert to an immutable primitive array using
+-- sequencing, such as 'IO' or 'ST'. Typically, a mutable primitive array will
+-- be built and then converted to an immutable primitive array using
 -- 'unsafeFreezePrimArray'. However, it is also acceptable to simply discard
 -- a mutable primitive array since it lives in managed memory and will be
 -- garbage collected when no longer referenced.
 data MutablePrimArray s a = MutablePrimArray (MutableByteArray# s)
 
+instance Eq (MutablePrimArray s a) where
+  (==) = sameMutablePrimArray
+
+instance NFData (MutablePrimArray s a) where
+  rnf (MutablePrimArray _) = ()
+
 sameByteArray :: ByteArray# -> ByteArray# -> Bool
 sameByteArray ba1 ba2 =
     case reallyUnsafePtrEquality# (unsafeCoerce# ba1 :: ()) (unsafeCoerce# ba2 :: ()) of
-#if __GLASGOW_HASKELL__ >= 708
       r -> isTrue# r
-#else
-      1# -> True
-      _ -> False
-#endif
 
 -- | @since 0.6.4.0
 instance (Eq a, Prim a) => Eq (PrimArray a) where
   a1@(PrimArray ba1#) == a2@(PrimArray ba2#)
     | sameByteArray ba1# ba2# = True
     | sz1 /= sz2 = False
-    | otherwise = loop (quot sz1 (sizeOf (undefined :: a)) - 1)
+    | otherwise = loop (quot sz1 (sizeOfType @a) - 1)
     where
     -- Here, we take the size in bytes, not in elements. We do this
     -- since it allows us to defer performing the division to
@@ -158,11 +191,12 @@
     sz2 = PB.sizeofByteArray (ByteArray ba2#)
     loop !i
       | i < 0 = True
-      | otherwise = indexPrimArray a1 i == indexPrimArray a2 i && loop (i-1)
+      | otherwise = indexPrimArray a1 i == indexPrimArray a2 i && loop (i - 1)
+  {-# INLINE (==) #-}
 
 -- | Lexicographic ordering. Subject to change between major versions.
--- 
---   @since 0.6.4.0
+--
+-- @since 0.6.4.0
 instance (Ord a, Prim a) => Ord (PrimArray a) where
   compare a1@(PrimArray ba1#) a2@(PrimArray ba2#)
     | sameByteArray ba1# ba2# = EQ
@@ -170,50 +204,50 @@
     where
     sz1 = PB.sizeofByteArray (ByteArray ba1#)
     sz2 = PB.sizeofByteArray (ByteArray ba2#)
-    sz = quot (min sz1 sz2) (sizeOf (undefined :: a))
+    sz = quot (min sz1 sz2) (sizeOfType @a)
     loop !i
-      | i < sz = compare (indexPrimArray a1 i) (indexPrimArray a2 i) <> loop (i+1)
+      | i < sz = compare (indexPrimArray a1 i) (indexPrimArray a2 i) <> loop (i + 1)
       | otherwise = compare sz1 sz2
+  {-# INLINE compare #-}
 
-#if MIN_VERSION_base(4,7,0)
 -- | @since 0.6.4.0
 instance Prim a => IsList (PrimArray a) where
   type Item (PrimArray a) = a
   fromList = primArrayFromList
   fromListN = primArrayFromListN
   toList = primArrayToList
-#endif
 
 -- | @since 0.6.4.0
 instance (Show a, Prim a) => Show (PrimArray a) where
-  showsPrec p a = showParen (p > 10) $
-    showString "fromListN " . shows (sizeofPrimArray a) . showString " "
-      . shows (primArrayToList a)
+  showsPrec _ a = shows (primArrayToList a)
 
 die :: String -> String -> a
 die fun problem = error $ "Data.Primitive.PrimArray." ++ fun ++ ": " ++ problem
 
+-- | Create a 'PrimArray' from a list.
+--
+-- @primArrayFromList vs = `primArrayFromListN` (length vs) vs@
 primArrayFromList :: Prim a => [a] -> PrimArray a
 primArrayFromList vs = primArrayFromListN (L.length vs) vs
 
+-- | Create a 'PrimArray' from a list of a known length. If the length
+-- of the list does not match the given length, this throws an exception.
+
+-- See Note [fromListN] in Data.Primitive.Array
 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 [] !ix = if ix == len
-          then return ()
-          else die "fromListN" "list length less than specified size"
-        go (a : as) !ix = if ix < len
-          then do
-            writePrimArray arr ix a
-            go as (ix + 1)
-          else die "fromListN" "list length greater than specified size"
-    go vs 0
-    unsafeFreezePrimArray arr
+{-# INLINE primArrayFromListN #-}
+primArrayFromListN len vs = createPrimArray len $ \arr ->
+  let z ix# = if I# ix# == len
+        then return ()
+        else die "fromListN" "list length less than specified size"
+      f a k = GHC.Exts.oneShot $ \ix# -> if I# ix# < len
+        then do
+          writePrimArray arr (I# ix#) a
+          k (ix# +# 1#)
+        else die "fromListN" "list length greater than specified size"
+  in foldr f z vs 0#
 
--- | Convert the primitive array to a list.
+-- | Convert a 'PrimArray' to a list.
 {-# INLINE primArrayToList #-}
 primArrayToList :: forall a. Prim a => PrimArray a -> [a]
 primArrayToList xs = build (\c n -> foldrPrimArray c n xs)
@@ -224,36 +258,40 @@
 byteArrayToPrimArray :: ByteArray -> PrimArray a
 byteArrayToPrimArray (PB.ByteArray x) = PrimArray x
 
-#if MIN_VERSION_base(4,9,0)
 -- | @since 0.6.4.0
 instance Semigroup (PrimArray a) where
-  x <> y = byteArrayToPrimArray (primArrayToByteArray x SG.<> primArrayToByteArray y)
-  sconcat = byteArrayToPrimArray . SG.sconcat . fmap primArrayToByteArray
-  stimes i arr = byteArrayToPrimArray (SG.stimes i (primArrayToByteArray arr))
-#endif
+  x <> y = byteArrayToPrimArray (primArrayToByteArray x <> primArrayToByteArray y)
+  sconcat = byteArrayToPrimArray . sconcat . fmap primArrayToByteArray
+  stimes i arr = byteArrayToPrimArray (stimes i (primArrayToByteArray arr))
 
 -- | @since 0.6.4.0
 instance Monoid (PrimArray a) where
   mempty = emptyPrimArray
 #if !(MIN_VERSION_base(4,11,0))
-  mappend x y = byteArrayToPrimArray (mappend (primArrayToByteArray x) (primArrayToByteArray y))
+  mappend = (<>)
 #endif
   mconcat = byteArrayToPrimArray . mconcat . map primArrayToByteArray
 
--- | The empty primitive array.
+-- | The empty 'PrimArray'.
 emptyPrimArray :: PrimArray a
 {-# NOINLINE emptyPrimArray #-}
 emptyPrimArray = runST $ primitive $ \s0# -> case newByteArray# 0# s0# of
   (# s1#, arr# #) -> case unsafeFreezeByteArray# arr# s1# of
     (# s2#, arr'# #) -> (# s2#, PrimArray arr'# #)
 
+emptyPrimArray# :: (# #) -> ByteArray#
+{-# NOINLINE emptyPrimArray# #-}
+emptyPrimArray# _ = case emptyPrimArray of PrimArray arr# -> arr#
+
 -- | Create a new mutable primitive array of the given length. The
 -- underlying memory is left uninitialized.
+--
+-- /Note:/ this function does not check if the input is non-negative.
 newPrimArray :: forall m a. (PrimMonad m, Prim a) => Int -> m (MutablePrimArray (PrimState m) a)
 {-# INLINE newPrimArray #-}
 newPrimArray (I# n#)
-  = primitive (\s# -> 
-      case newByteArray# (n# *# sizeOf# (undefined :: a)) s# of
+  = primitive (\s# ->
+      case newByteArray# (n# *# sizeOfType# (Proxy :: Proxy a)) s# of
         (# s'#, arr# #) -> (# s'#, MutablePrimArray arr# #)
     )
 
@@ -272,40 +310,33 @@
   -> Int -- ^ new size
   -> m (MutablePrimArray (PrimState m) a)
 {-# INLINE resizeMutablePrimArray #-}
-#if __GLASGOW_HASKELL__ >= 710
 resizeMutablePrimArray (MutablePrimArray arr#) (I# n#)
-  = primitive (\s# -> case resizeMutableByteArray# arr# (n# *# sizeOf# (undefined :: a)) s# of
+  = primitive (\s# -> case resizeMutableByteArray# arr# (n# *# sizeOfType# (Proxy :: Proxy a)) s# of
                         (# s'#, arr'# #) -> (# s'#, MutablePrimArray arr'# #))
-#else
-resizeMutablePrimArray arr n
-  = do arr' <- newPrimArray n
-       copyMutablePrimArray arr' 0 arr 0 (min (sizeofMutablePrimArray arr) n)
-       return arr'
-#endif
 
--- Although it is possible to shim resizeMutableByteArray for old GHCs, this
--- is not the case with shrinkMutablePrimArray.
-#if __GLASGOW_HASKELL__ >= 710
 -- | Shrink a mutable primitive array. The new size is given in elements.
 -- It must be smaller than the old size. The array will be resized in place.
--- This function is only available when compiling with GHC 7.10 or newer.
 shrinkMutablePrimArray :: forall m a. (PrimMonad m, Prim a)
   => MutablePrimArray (PrimState m) a
   -> Int -- ^ new size
   -> m ()
 {-# INLINE shrinkMutablePrimArray #-}
 shrinkMutablePrimArray (MutablePrimArray arr#) (I# n#)
-  = primitive_ (shrinkMutableByteArray# arr# (n# *# sizeOf# (undefined :: a)))
-#endif
+  = primitive_ (shrinkMutableByteArray# arr# (n# *# sizeOfType# (Proxy :: Proxy a)))
 
+-- | Read a value from the array at the given index.
+--
+-- /Note:/ this function does not do bounds checking.
 readPrimArray :: (Prim a, PrimMonad m) => MutablePrimArray (PrimState m) a -> Int -> m a
 {-# INLINE readPrimArray #-}
 readPrimArray (MutablePrimArray arr#) (I# i#)
   = primitive (readByteArray# arr# i#)
 
 -- | Write an element to the given index.
-writePrimArray ::
-     (Prim a, PrimMonad m)
+--
+-- /Note:/ this function does not do bounds checking.
+writePrimArray
+  :: (Prim a, PrimMonad m)
   => MutablePrimArray (PrimState m) a -- ^ array
   -> Int -- ^ index
   -> a -- ^ element
@@ -315,8 +346,10 @@
   = primitive_ (writeByteArray# arr# i# x)
 
 -- | Copy part of a mutable array into another mutable array.
---   In the case that the destination and
---   source arrays are the same, the regions may overlap.
+-- In the case that the destination and
+-- source arrays are the same, the regions may overlap.
+--
+-- /Note:/ this function does not do bounds or overlap checking.
 copyMutablePrimArray :: forall m a.
      (PrimMonad m, Prim a)
   => MutablePrimArray (PrimState m) a -- ^ destination array
@@ -328,14 +361,16 @@
 {-# INLINE copyMutablePrimArray #-}
 copyMutablePrimArray (MutablePrimArray dst#) (I# doff#) (MutablePrimArray src#) (I# soff#) (I# n#)
   = primitive_ (copyMutableByteArray#
-      src# 
-      (soff# *# (sizeOf# (undefined :: a)))
+      src#
+      (soff# *# sizeOfType# (Proxy :: Proxy a))
       dst#
-      (doff# *# (sizeOf# (undefined :: a)))
-      (n# *# (sizeOf# (undefined :: a)))
+      (doff# *# sizeOfType# (Proxy :: Proxy a))
+      (n# *# sizeOfType# (Proxy :: Proxy a))
     )
 
 -- | Copy part of an array into another mutable array.
+--
+-- /Note:/ this function does not do bounds or overlap checking.
 copyPrimArray :: forall m a.
      (PrimMonad m, Prim a)
   => MutablePrimArray (PrimState m) a -- ^ destination array
@@ -347,52 +382,72 @@
 {-# INLINE copyPrimArray #-}
 copyPrimArray (MutablePrimArray dst#) (I# doff#) (PrimArray src#) (I# soff#) (I# n#)
   = primitive_ (copyByteArray#
-      src# 
-      (soff# *# (sizeOf# (undefined :: a)))
+      src#
+      (soff# *# sizeOfType# (Proxy :: Proxy a))
       dst#
-      (doff# *# (sizeOf# (undefined :: a)))
-      (n# *# (sizeOf# (undefined :: a)))
+      (doff# *# sizeOfType# (Proxy :: Proxy a))
+      (n# *# sizeOfType# (Proxy :: Proxy a))
     )
 
-#if __GLASGOW_HASKELL__ >= 708
--- | Copy a slice of an immutable primitive array to an address.
---   The offset and length are given in elements of type @a@.
---   This function assumes that the 'Prim' instance of @a@
---   agrees with the 'Storable' instance. This function is only
---   available when building with GHC 7.8 or newer.
+-- | Copy a slice of an immutable primitive array to a pointer.
+-- The offset and length are given in elements of type @a@.
+-- This function assumes that the 'Prim' instance of @a@
+-- agrees with the 'Foreign.Storable.Storable' instance.
+--
+-- /Note:/ this function does not do bounds or overlap checking.
 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
+  -> Int -- ^ number of elements to copy
   -> m ()
 {-# INLINE copyPrimArrayToPtr #-}
 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)
+  where siz# = sizeOfType# (Proxy :: Proxy a)
 
--- | Copy a slice of an immutable primitive array to an address.
---   The offset and length are given in elements of type @a@.
---   This function assumes that the 'Prim' instance of @a@
---   agrees with the 'Storable' instance. This function is only
---   available when building with GHC 7.8 or newer.
+-- | Copy a slice of a mutable primitive array to a pointer.
+-- The offset and length are given in elements of type @a@.
+-- This function assumes that the 'Prim' instance of @a@
+-- agrees with the 'Foreign.Storable.Storable' instance.
+--
+-- /Note:/ this function does not do bounds or overlap checking.
 copyMutablePrimArrayToPtr :: forall m a. (PrimMonad m, Prim a)
   => Ptr a -- ^ destination pointer
   -> MutablePrimArray (PrimState m) a -- ^ source array
   -> Int -- ^ offset into source array
-  -> Int -- ^ number of prims to copy
+  -> Int -- ^ number of elements to copy
   -> m ()
 {-# INLINE copyMutablePrimArrayToPtr #-}
 copyMutablePrimArrayToPtr (Ptr addr#) (MutablePrimArray mba#) (I# soff#) (I# n#) =
     primitive (\ s# ->
         let s'# = copyMutableByteArrayToAddr# mba# (soff# *# siz#) addr# (n# *# siz#) s#
         in (# s'#, () #))
-  where siz# = sizeOf# (undefined :: a)
-#endif
+  where siz# = sizeOfType# (Proxy :: Proxy a)
 
+-- | Copy from a pointer to a mutable primitive array.
+-- The offset and length are given in elements of type @a@.
+-- This function assumes that the 'Prim' instance of @a@
+-- agrees with the 'Foreign.Storable.Storable' instance.
+--
+-- /Note:/ this function does not do bounds or overlap checking.
+copyPtrToMutablePrimArray :: forall m a. (PrimMonad m, Prim a)
+  => MutablePrimArray (PrimState m) a -- ^ destination array
+  -> Int -- ^ destination offset
+  -> Ptr a -- ^ source pointer
+  -> Int -- ^ number of elements
+  -> m ()
+{-# INLINE copyPtrToMutablePrimArray #-}
+copyPtrToMutablePrimArray (MutablePrimArray ba#) (I# doff#) (Ptr addr#) (I# n#) =
+  primitive_ (copyAddrToByteArray# addr# ba# (doff# *# siz#) (n# *# siz#))
+  where
+  siz# = sizeOfType# (Proxy :: Proxy a)
+
 -- | Fill a slice of a mutable primitive array with a value.
+--
+-- /Note:/ this function does not do bounds checking.
 setPrimArray
   :: (Prim a, PrimMonad m)
   => MutablePrimArray (PrimState m) a -- ^ array to fill
@@ -412,9 +467,9 @@
 {-# INLINE getSizeofMutablePrimArray #-}
 #if __GLASGOW_HASKELL__ >= 801
 getSizeofMutablePrimArray (MutablePrimArray arr#)
-  = primitive (\s# -> 
+  = primitive (\s# ->
       case getSizeofMutableByteArray# arr# s# of
-        (# s'#, sz# #) -> (# s'#, I# (quotInt# sz# (sizeOf# (undefined :: a))) #)
+        (# s'#, sz# #) -> (# s'#, I# (quotInt# sz# (sizeOfType# (Proxy :: Proxy a))) #)
     )
 #else
 -- On older GHCs, it is not possible to resize a byte array, so
@@ -425,12 +480,15 @@
 #endif
 
 -- | Size of the mutable primitive array in elements. This function shall not
---   be used on primitive arrays that are an argument to or a result of
---   'resizeMutablePrimArray' or 'shrinkMutablePrimArray'.
+-- be used on primitive arrays that are an argument to or a result of
+-- 'resizeMutablePrimArray' or 'shrinkMutablePrimArray'.
+--
+-- This function is deprecated and will be removed.
 sizeofMutablePrimArray :: forall s a. Prim a => MutablePrimArray s a -> Int
 {-# INLINE sizeofMutablePrimArray #-}
+{-# DEPRECATED sizeofMutablePrimArray "use getSizeofMutablePrimArray instead" #-}
 sizeofMutablePrimArray (MutablePrimArray arr#) =
-  I# (quotInt# (sizeofMutableByteArray# arr#) (sizeOf# (undefined :: a)))
+  I# (quotInt# (sizeofMutableByteArray# arr#) (sizeOfType# (Proxy :: Proxy a)))
 
 -- | Check if the two arrays refer to the same memory block.
 sameMutablePrimArray :: MutablePrimArray s a -> MutablePrimArray s a -> Bool
@@ -438,7 +496,49 @@
 sameMutablePrimArray (MutablePrimArray arr#) (MutablePrimArray brr#)
   = isTrue# (sameMutableByteArray# arr# brr#)
 
--- | Convert a mutable byte array to an immutable one without copying. The
+-- | Create an immutable copy of a slice of a primitive array. The offset and
+-- length are given in elements.
+--
+-- This operation makes a copy of the specified section, so it is safe to
+-- continue using the mutable array afterward.
+--
+-- /Note:/ The provided array should contain the full subrange
+-- specified by the two Ints, but this is not checked.
+freezePrimArray
+  :: (PrimMonad m, Prim a)
+  => MutablePrimArray (PrimState m) a -- ^ source
+  -> Int                              -- ^ offset in elements
+  -> Int                              -- ^ length in elements
+  -> m (PrimArray a)
+{-# INLINE freezePrimArray #-}
+freezePrimArray !src !off !len = do
+  dst <- newPrimArray len
+  copyMutablePrimArray dst 0 src off len
+  unsafeFreezePrimArray dst
+
+-- | Create a mutable primitive array from a slice of an immutable primitive array.
+-- The offset and length are given in elements.
+--
+-- This operation makes a copy of the specified slice, so it is safe to
+-- use the immutable array afterward.
+--
+-- /Note:/ The provided array should contain the full subrange
+-- specified by the two Ints, but this is not checked.
+--
+-- @since 0.7.2.0
+thawPrimArray
+  :: (PrimMonad m, Prim a)
+  => PrimArray a -- ^ source
+  -> Int         -- ^ offset in elements
+  -> Int         -- ^ length in elements
+  -> m (MutablePrimArray (PrimState m) a)
+{-# INLINE thawPrimArray #-}
+thawPrimArray !src !off !len = do
+  dst <- newPrimArray len
+  copyPrimArray dst 0 src off len
+  return dst
+
+-- | Convert a mutable primitive array to an immutable one without copying. The
 -- array should not be modified after the conversion.
 unsafeFreezePrimArray
   :: PrimMonad m => MutablePrimArray (PrimState m) a -> m (PrimArray a)
@@ -456,6 +556,8 @@
   = primitive (\s# -> (# s#, MutablePrimArray (unsafeCoerce# arr#) #))
 
 -- | Read a primitive value from the primitive array.
+--
+-- /Note:/ this function does not do bounds checking.
 indexPrimArray :: forall a. Prim a => PrimArray a -> Int -> a
 {-# INLINE indexPrimArray #-}
 indexPrimArray (PrimArray arr#) (I# i#) = indexByteArray# arr# i#
@@ -463,8 +565,28 @@
 -- | Get the size, in elements, of the primitive array.
 sizeofPrimArray :: forall a. Prim a => PrimArray a -> Int
 {-# INLINE sizeofPrimArray #-}
-sizeofPrimArray (PrimArray arr#) = I# (quotInt# (sizeofByteArray# arr#) (sizeOf# (undefined :: a)))
+sizeofPrimArray (PrimArray arr#) = I# (quotInt# (sizeofByteArray# arr#) (sizeOfType# (Proxy :: Proxy a)))
 
+#if __GLASGOW_HASKELL__ >= 802
+-- | Check whether or not the primitive array is pinned. Pinned primitive arrays cannot
+-- be moved by the garbage collector. It is safe to use 'primArrayContents'
+-- on such arrays. This function is only available when compiling with
+-- GHC 8.2 or newer.
+--
+-- @since 0.7.1.0
+isPrimArrayPinned :: PrimArray a -> Bool
+{-# INLINE isPrimArrayPinned #-}
+isPrimArrayPinned (PrimArray arr#) = isTrue# (Exts.isByteArrayPinned# arr#)
+
+-- | Check whether or not the mutable primitive array is pinned. This function is
+-- only available when compiling with GHC 8.2 or newer.
+--
+-- @since 0.7.1.0
+isMutablePrimArrayPinned :: MutablePrimArray s a -> Bool
+{-# INLINE isMutablePrimArrayPinned #-}
+isMutablePrimArrayPinned (MutablePrimArray marr#) = isTrue# (Exts.isMutableByteArrayPinned# marr#)
+#endif
+
 -- | Lazy right-associated fold over the elements of a 'PrimArray'.
 {-# INLINE foldrPrimArray #-}
 foldrPrimArray :: forall a b. Prim a => (a -> b -> b) -> b -> PrimArray a -> b
@@ -472,7 +594,7 @@
   where
     !sz = sizeofPrimArray arr
     go !i
-      | sz > i = f (indexPrimArray arr i) (go (i+1))
+      | i < sz = f (indexPrimArray arr i) (go (i + 1))
       | otherwise = z
 
 -- | Strict right-associated fold over the elements of a 'PrimArray'.
@@ -539,7 +661,7 @@
 -- > incrPositiveB xs = runST $ runMaybeT $ traversePrimArrayP
 -- >   (\x -> bool (MaybeT (return Nothing)) (MaybeT (return (Just (x + 1)))) (x > 0))
 -- >   xs
--- 
+--
 -- Benchmarks demonstrate that the second implementation runs 150 times
 -- faster than the first. It also results in fewer allocations.
 {-# INLINE traversePrimArrayP #-}
@@ -550,17 +672,15 @@
 traversePrimArrayP f arr = do
   let !sz = sizeofPrimArray arr
   marr <- newPrimArray sz
-  let go !ix = if ix < sz
-        then do
-          b <- f (indexPrimArray arr ix)
-          writePrimArray marr ix b
-          go (ix + 1)
-        else return ()
+  let go !ix = when (ix < sz) $ do
+        b <- f (indexPrimArray arr ix)
+        writePrimArray marr ix b
+        go (ix + 1)
   go 0
   unsafeFreezePrimArray marr
 
 -- | Filter the primitive array, keeping the elements for which the monadic
--- predicate evaluates true.
+-- predicate evaluates to true.
 {-# INLINE filterPrimArrayP #-}
 filterPrimArrayP :: (PrimMonad m, Prim a)
   => (a -> m Bool)
@@ -616,12 +736,10 @@
   -> m (PrimArray a)
 generatePrimArrayP sz f = do
   marr <- newPrimArray sz
-  let go !ix = if ix < sz
-        then do
-          b <- f ix
-          writePrimArray marr ix b
-          go (ix + 1)
-        else return ()
+  let go !ix = when (ix < sz) $ do
+        b <- f ix
+        writePrimArray marr ix b
+        go (ix + 1)
   go 0
   unsafeFreezePrimArray marr
 
@@ -634,33 +752,27 @@
   -> m (PrimArray a)
 replicatePrimArrayP sz f = do
   marr <- newPrimArray sz
-  let go !ix = if ix < sz
-        then do
-          b <- f
-          writePrimArray marr ix b
-          go (ix + 1)
-        else return ()
+  let go !ix = when (ix < sz) $ do
+        b <- f
+        writePrimArray marr ix b
+        go (ix + 1)
   go 0
   unsafeFreezePrimArray marr
 
-
 -- | Map over the elements of a primitive array.
 {-# INLINE mapPrimArray #-}
 mapPrimArray :: (Prim a, Prim b)
   => (a -> b)
   -> PrimArray a
   -> PrimArray b
-mapPrimArray f arr = runST $ do
-  let !sz = sizeofPrimArray arr
-  marr <- newPrimArray sz
-  let go !ix = if ix < sz
-        then do
-          let b = f (indexPrimArray arr ix)
-          writePrimArray marr ix b
-          go (ix + 1)
-        else return ()
-  go 0
-  unsafeFreezePrimArray marr
+mapPrimArray f arr = createPrimArray sz $ \marr ->
+  let go !ix = when (ix < sz) $ do
+        let b = f (indexPrimArray arr ix)
+        writePrimArray marr ix b
+        go (ix + 1)
+  in go 0
+  where
+    !sz = sizeofPrimArray arr
 
 -- | Indexed map over the elements of a primitive array.
 {-# INLINE imapPrimArray #-}
@@ -668,17 +780,14 @@
   => (Int -> a -> b)
   -> PrimArray a
   -> PrimArray b
-imapPrimArray f arr = runST $ do
-  let !sz = sizeofPrimArray arr
-  marr <- newPrimArray sz
-  let go !ix = if ix < sz
-        then do
-          let b = f ix (indexPrimArray arr ix)
-          writePrimArray marr ix b
-          go (ix + 1)
-        else return ()
-  go 0
-  unsafeFreezePrimArray marr
+imapPrimArray f arr = createPrimArray sz $ \marr ->
+  let go !ix = when (ix < sz) $ do
+        let b = f ix (indexPrimArray arr ix)
+        writePrimArray marr ix b
+        go (ix + 1)
+  in go 0
+  where
+    !sz = sizeofPrimArray arr
 
 -- | Filter elements of a primitive array according to a predicate.
 {-# INLINE filterPrimArray #-}
@@ -704,8 +813,8 @@
 
 -- | Filter the primitive array, keeping the elements for which the monadic
 -- predicate evaluates true.
-filterPrimArrayA ::
-     (Applicative f, Prim a)
+filterPrimArrayA
+  :: (Applicative f, Prim a)
   => (a -> f Bool) -- ^ mapping function
   -> PrimArray a -- ^ primitive array
   -> f (PrimArray a)
@@ -728,8 +837,8 @@
 
 -- | Map over the primitive array, keeping the elements for which the applicative
 -- predicate provides a 'Just'.
-mapMaybePrimArrayA ::
-     (Applicative f, Prim a, Prim b)
+mapMaybePrimArrayA
+  :: (Applicative f, Prim a, Prim b)
   => (a -> f (Maybe b)) -- ^ mapping function
   -> PrimArray a -- ^ primitive array
   -> f (PrimArray b)
@@ -773,7 +882,6 @@
   marr' <- resizeMutablePrimArray marr dstLen
   unsafeFreezePrimArray marr'
 
-
 -- | Traverse a primitive array. The traversal performs all of the applicative
 -- effects /before/ forcing the resulting values and writing them to the new
 -- primitive array. Consequently:
@@ -785,10 +893,10 @@
 -- *** Exception: Prelude.undefined
 --
 -- The function 'traversePrimArrayP' always outperforms this function, but it
--- requires a 'PrimAffineMonad' constraint, and it forces the values as
+-- requires a 'PrimMonad' constraint, and it forces the values as
 -- it performs the effects.
-traversePrimArray ::
-     (Applicative f, Prim a, Prim b)
+traversePrimArray
+  :: (Applicative f, Prim a, Prim b)
   => (a -> f b) -- ^ mapping function
   -> PrimArray a -- ^ primitive array
   -> f (PrimArray b)
@@ -806,8 +914,8 @@
      else runSTA len <$> go 0
 
 -- | Traverse a primitive array with the index of each element.
-itraversePrimArray ::
-     (Applicative f, Prim a, Prim b)
+itraversePrimArray
+  :: (Applicative f, Prim a, Prim b)
   => (Int -> a -> f b)
   -> PrimArray a
   -> f (PrimArray b)
@@ -849,15 +957,11 @@
   => Int -- ^ length
   -> (Int -> a) -- ^ element from index
   -> PrimArray a
-generatePrimArray len f = runST $ do
-  marr <- newPrimArray len
-  let go !ix = if ix < len
-        then do
-          writePrimArray marr ix (f ix)
-          go (ix + 1)
-        else return ()
-  go 0
-  unsafeFreezePrimArray marr
+generatePrimArray len f = createPrimArray len $ \marr ->
+  let go !ix = when (ix < len) $ do
+        writePrimArray marr ix (f ix)
+        go (ix + 1)
+  in go 0
 
 -- | Create a primitive array by copying the element the given
 -- number of times.
@@ -866,16 +970,14 @@
   => Int -- ^ length
   -> a -- ^ element
   -> PrimArray a
-replicatePrimArray len a = runST $ do
-  marr <- newPrimArray len
+replicatePrimArray len a = createPrimArray len $ \marr ->
   setPrimArray marr 0 len a
-  unsafeFreezePrimArray marr
 
 -- | Generate a primitive array by evaluating the applicative generator
 -- function at each index.
 {-# INLINE generatePrimArrayA #-}
-generatePrimArrayA ::
-     (Applicative f, Prim a)
+generatePrimArrayA
+  :: (Applicative f, Prim a)
   => Int -- ^ length
   -> (Int -> f a) -- ^ element from index
   -> f (PrimArray a)
@@ -892,10 +994,10 @@
      else runSTA len <$> go 0
 
 -- | Execute the applicative action the given number of times and store the
--- results in a vector.
+-- results in a 'PrimArray'.
 {-# INLINE replicatePrimArrayA #-}
-replicatePrimArrayA ::
-     (Applicative f, Prim a)
+replicatePrimArrayA
+  :: (Applicative f, Prim a)
   => Int -- ^ length
   -> f a -- ^ applicative element producer
   -> f (PrimArray a)
@@ -912,32 +1014,30 @@
      else runSTA len <$> go 0
 
 -- | Traverse the primitive array, discarding the results. There
--- is no 'PrimMonad' variant of this function since it would not provide
+-- is no 'PrimMonad' variant of this function, since it would not provide
 -- any performance benefit.
-traversePrimArray_ ::
-     (Applicative f, Prim a)
+traversePrimArray_
+  :: (Applicative f, Prim a)
   => (a -> f b)
   -> PrimArray a
   -> f ()
 traversePrimArray_ f a = go 0 where
   !sz = sizeofPrimArray a
-  go !ix = if ix < sz
-    then f (indexPrimArray a ix) *> go (ix + 1)
-    else pure ()
+  go !ix = when (ix < sz) $
+    f (indexPrimArray a ix) *> go (ix + 1)
 
 -- | Traverse the primitive array with the indices, discarding the results.
--- There is no 'PrimMonad' variant of this function since it would not
+-- There is no 'PrimMonad' variant of this function, since it would not
 -- provide any performance benefit.
-itraversePrimArray_ ::
-     (Applicative f, Prim a)
+itraversePrimArray_
+  :: (Applicative f, Prim a)
   => (Int -> a -> f b)
   -> PrimArray a
   -> f ()
 itraversePrimArray_ f a = go 0 where
   !sz = sizeofPrimArray a
-  go !ix = if ix < sz
-    then f ix (indexPrimArray a ix) *> go (ix + 1)
-    else pure ()
+  go !ix = when (ix < sz) $
+    f ix (indexPrimArray a ix) *> go (ix + 1)
 
 newtype IxSTA a = IxSTA {_runIxSTA :: forall s. Int -> MutableByteArray# s -> ST s Int}
 
@@ -966,4 +1066,127 @@
 documentation of the @Data.Primitive@ module.
 -}
 
+-- | Create a /pinned/ primitive array of the specified size (in elements). The garbage
+-- collector is guaranteed not to move it. The underlying memory is left uninitialized.
+--
+-- @since 0.7.1.0
+newPinnedPrimArray :: forall m a. (PrimMonad m, Prim a)
+  => Int -> m (MutablePrimArray (PrimState m) a)
+{-# INLINE newPinnedPrimArray #-}
+newPinnedPrimArray (I# n#)
+  = primitive (\s# -> case newPinnedByteArray# (n# *# sizeOfType# (Proxy :: Proxy a)) s# of
+                        (# s'#, arr# #) -> (# s'#, MutablePrimArray arr# #))
 
+-- | Create a /pinned/ primitive array of the specified size (in elements) and
+-- with the alignment given by its 'Prim' instance. The garbage collector is
+-- guaranteed not to move it. The underlying memory is left uninitialized.
+--
+-- @since 0.7.0.0
+newAlignedPinnedPrimArray :: forall m a. (PrimMonad m, Prim a)
+  => Int -> m (MutablePrimArray (PrimState m) a)
+{-# INLINE newAlignedPinnedPrimArray #-}
+newAlignedPinnedPrimArray (I# n#)
+  = primitive (\s# -> case newAlignedPinnedByteArray# (n# *# sizeOfType# (Proxy :: Proxy a)) (alignmentOfType# (Proxy :: Proxy a)) s# of
+                        (# s'#, arr# #) -> (# s'#, MutablePrimArray arr# #))
+
+-- | Yield a pointer to the array's data. This operation is only safe on
+-- /pinned/ prim arrays allocated by
+-- 'Data.Primitive.ByteArray.newPinnedByteArray' or
+-- 'Data.Primitive.ByteArray.newAlignedPinnedByteArray'.
+--
+-- @since 0.7.1.0
+primArrayContents :: PrimArray a -> Ptr a
+{-# INLINE primArrayContents #-}
+primArrayContents (PrimArray arr#) = Ptr (byteArrayContents# arr#)
+
+-- | Yield a pointer to the array's data. This operation is only safe on
+-- /pinned/ byte arrays allocated by
+-- 'Data.Primitive.ByteArray.newPinnedByteArray' or
+-- 'Data.Primitive.ByteArray.newAlignedPinnedByteArray'.
+--
+-- @since 0.7.1.0
+mutablePrimArrayContents :: MutablePrimArray s a -> Ptr a
+{-# INLINE mutablePrimArrayContents #-}
+mutablePrimArrayContents (MutablePrimArray arr#) =
+  Ptr (mutableByteArrayContentsShim arr#)
+
+-- | Return a newly allocated array with the specified subrange of the
+-- provided array. The provided array should contain the full subrange
+-- specified by the two Ints, but this is not checked.
+clonePrimArray :: Prim a
+  => PrimArray a -- ^ source array
+  -> Int     -- ^ offset into destination array
+  -> Int     -- ^ number of elements to copy
+  -> PrimArray a
+{-# INLINE clonePrimArray #-}
+clonePrimArray src off n = createPrimArray n $ \dst ->
+  copyPrimArray dst 0 src off n
+
+-- | Return a newly allocated mutable array with the specified subrange of
+-- the provided mutable array. The provided mutable array should contain the
+-- full subrange specified by the two Ints, but this is not checked.
+cloneMutablePrimArray :: (PrimMonad m, Prim a)
+  => MutablePrimArray (PrimState m) a -- ^ source array
+  -> Int -- ^ offset into destination array
+  -> Int -- ^ number of elements to copy
+  -> m (MutablePrimArray (PrimState m) a)
+{-# INLINE cloneMutablePrimArray #-}
+cloneMutablePrimArray src off n = do
+  dst <- newPrimArray n
+  copyMutablePrimArray dst 0 src off n
+  return dst
+
+-- | Execute the monadic action and freeze the resulting array.
+--
+-- > runPrimArray m = runST $ m >>= unsafeFreezePrimArray
+runPrimArray
+  :: (forall s. ST s (MutablePrimArray s a))
+  -> PrimArray a
+runPrimArray m = PrimArray (runPrimArray# m)
+
+runPrimArray#
+  :: (forall s. ST s (MutablePrimArray s a))
+  -> ByteArray#
+runPrimArray# m = case runRW# $ \s ->
+  case unST m s of { (# s', MutablePrimArray mary# #) ->
+  unsafeFreezeByteArray# mary# s'} of (# _, ary# #) -> ary#
+
+unST :: ST s a -> State# s -> (# State# s, a #)
+unST (GHCST.ST f) = f
+
+-- | Create an uninitialized array of the given length, apply the function to
+-- it, and freeze the result.
+--
+-- /Note:/ this function does not check if the input is non-negative.
+--
+-- @since FIXME
+createPrimArray
+  :: Prim a => Int -> (forall s. MutablePrimArray s a -> ST s ()) -> PrimArray a
+{-# INLINE createPrimArray #-}
+createPrimArray 0 _ = PrimArray (emptyPrimArray# (# #))
+createPrimArray n f = runPrimArray $ do
+  marr <- newPrimArray n
+  f marr
+  pure marr
+
+-- | A composition of 'primArrayContents' and 'keepAliveUnlifted'.
+-- The callback function must not return the pointer. The argument
+-- array must be /pinned/. See 'primArrayContents' for an explanation
+-- of which primitive arrays are pinned.
+--
+-- Note: This could be implemented with 'keepAlive' instead of
+-- 'keepAliveUnlifted', but 'keepAlive' here would cause GHC to materialize
+-- the wrapper data constructor on the heap.
+withPrimArrayContents :: PrimBase m => PrimArray a -> (Ptr a -> m a) -> m a
+{-# INLINE withPrimArrayContents #-}
+withPrimArrayContents (PrimArray arr#) f =
+  keepAliveUnlifted arr# (f (Ptr (byteArrayContents# arr#)))
+
+-- | A composition of 'mutablePrimArrayContents' and 'keepAliveUnlifted'.
+-- The callback function must not return the pointer. The argument
+-- array must be /pinned/. See 'primArrayContents' for an explanation
+-- of which primitive arrays are pinned.
+withMutablePrimArrayContents :: PrimBase m => MutablePrimArray (PrimState m) a -> (Ptr a -> m a) -> m a
+{-# INLINE withMutablePrimArrayContents #-}
+withMutablePrimArrayContents (MutablePrimArray arr#) f =
+  keepAliveUnlifted arr# (f (Ptr (mutableByteArrayContentsShim arr#)))
diff --git a/Data/Primitive/PrimVar.hs b/Data/Primitive/PrimVar.hs
new file mode 100644
--- /dev/null
+++ b/Data/Primitive/PrimVar.hs
@@ -0,0 +1,167 @@
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE MagicHash #-}
+{-# LANGUAGE RoleAnnotations #-}
+{-# LANGUAGE UnboxedTuples #-}
+{-# LANGUAGE Unsafe #-}
+
+-- | Variant of @MutVar@ that has one less indirection for primitive types.
+-- The difference is illustrated by comparing @MutVar Int@ and @PrimVar Int@:
+--
+-- * @MutVar Int@: @MutVar# --> I#@
+-- * @PrimVar Int@: @MutableByteArray#@
+--
+-- This module is adapted from a module in Edward Kmett\'s @prim-ref@ library.
+module Data.Primitive.PrimVar
+  (
+  -- * Primitive References
+    PrimVar(..)
+  , newPrimVar
+  , newPinnedPrimVar
+  , newAlignedPinnedPrimVar
+  , readPrimVar
+  , writePrimVar
+  , modifyPrimVar
+  , primVarContents
+  , primVarToMutablePrimArray
+  -- * Atomic Operations
+  -- $atomic
+  , casInt
+  , fetchAddInt
+  , fetchSubInt
+  , fetchAndInt
+  , fetchNandInt
+  , fetchOrInt
+  , fetchXorInt
+  , atomicReadInt
+  , atomicWriteInt
+  ) where
+
+import Control.Monad.Primitive
+import Data.Primitive
+import GHC.Exts
+import GHC.Ptr (castPtr)
+
+--------------------------------------------------------------------------------
+-- * Primitive References
+--------------------------------------------------------------------------------
+
+-- | A 'PrimVar' behaves like a single-element mutable primitive array.
+newtype PrimVar s a = PrimVar (MutablePrimArray s a)
+
+type role PrimVar nominal nominal
+
+-- | Create a primitive reference.
+newPrimVar :: (PrimMonad m, Prim a) => a -> m (PrimVar (PrimState m) a)
+newPrimVar a = do
+  m <- newPrimArray 1
+  writePrimArray m 0 a
+  return (PrimVar m)
+{-# INLINE newPrimVar #-}
+
+-- | Create a pinned primitive reference.
+newPinnedPrimVar :: (PrimMonad m, Prim a) => a -> m (PrimVar (PrimState m) a)
+newPinnedPrimVar a = do
+  m <- newPinnedPrimArray 1
+  writePrimArray m 0 a
+  return (PrimVar m)
+{-# INLINE newPinnedPrimVar #-}
+
+-- | Create a pinned primitive reference with the appropriate alignment for its contents.
+newAlignedPinnedPrimVar :: (PrimMonad m, Prim a) => a -> m (PrimVar (PrimState m) a)
+newAlignedPinnedPrimVar a = do
+  m <- newAlignedPinnedPrimArray 1
+  writePrimArray m 0 a
+  return (PrimVar m)
+{-# INLINE newAlignedPinnedPrimVar #-}
+
+-- | Read a value from the 'PrimVar'.
+readPrimVar :: (PrimMonad m, Prim a) => PrimVar (PrimState m) a -> m a
+readPrimVar (PrimVar m) = readPrimArray m 0
+{-# INLINE readPrimVar #-}
+
+-- | Write a value to the 'PrimVar'.
+writePrimVar :: (PrimMonad m, Prim a) => PrimVar (PrimState m) a -> a -> m ()
+writePrimVar (PrimVar m) a = writePrimArray m 0 a
+{-# INLINE writePrimVar #-}
+
+-- | Mutate the contents of a 'PrimVar'.
+modifyPrimVar :: (PrimMonad m, Prim a) => PrimVar (PrimState m) a -> (a -> a) -> m ()
+modifyPrimVar pv f = do
+    x <- readPrimVar pv
+    writePrimVar pv (f x)
+{-# INLINE modifyPrimVar #-}
+
+instance Eq (PrimVar s a) where
+  PrimVar m == PrimVar n = sameMutablePrimArray m n
+  {-# INLINE (==) #-}
+
+-- | Yield a pointer to the data of a 'PrimVar'. This operation is only safe on pinned byte arrays allocated by
+-- 'newPinnedPrimVar' or 'newAlignedPinnedPrimVar'.
+primVarContents :: PrimVar s a -> Ptr a
+primVarContents (PrimVar m) = castPtr $ mutablePrimArrayContents m
+{-# INLINE primVarContents #-}
+
+-- | Convert a 'PrimVar' to a one-elment 'MutablePrimArray'.
+primVarToMutablePrimArray :: PrimVar s a -> MutablePrimArray s a
+primVarToMutablePrimArray (PrimVar m) = m
+{-# INLINE primVarToMutablePrimArray #-}
+
+--------------------------------------------------------------------------------
+-- * Atomic Operations
+--------------------------------------------------------------------------------
+
+-- $atomic
+-- Atomic operations on `PrimVar s Int`. All atomic operations imply a full memory barrier.
+
+-- | Given a primitive reference, the expected old value, and the new value, perform an atomic compare and swap i.e. write the new value if the current value matches the provided old value. Returns the value of the element before the operation.
+casInt :: PrimMonad m => PrimVar (PrimState m) Int -> Int -> Int -> m Int
+casInt (PrimVar (MutablePrimArray m)) (I# old) (I# new) = primitive $ \s -> case casIntArray# m 0# old new s of
+  (# s', result #) -> (# s', I# result #)
+{-# INLINE casInt #-}
+
+-- | Given a reference, and a value to add, atomically add the value to the element. Returns the value of the element before the operation.
+fetchAddInt :: PrimMonad m => PrimVar (PrimState m) Int -> Int -> m Int
+fetchAddInt (PrimVar (MutablePrimArray m)) (I# x) = primitive $ \s -> case fetchAddIntArray# m 0# x s of
+  (# s', result #) -> (# s', I# result #)
+{-# INLINE fetchAddInt #-}
+
+-- | Given a reference, and a value to subtract, atomically subtract the value from the element. Returns the value of the element before the operation.
+fetchSubInt :: PrimMonad m => PrimVar (PrimState m) Int -> Int -> m Int
+fetchSubInt (PrimVar (MutablePrimArray m)) (I# x) = primitive $ \s -> case fetchSubIntArray# m 0# x s of
+  (# s', result #) -> (# s', I# result #)
+{-# INLINE fetchSubInt #-}
+
+-- | Given a reference, and a value to bitwise and, atomically and the value with the element. Returns the value of the element before the operation.
+fetchAndInt :: PrimMonad m => PrimVar (PrimState m) Int -> Int -> m Int
+fetchAndInt (PrimVar (MutablePrimArray m)) (I# x) = primitive $ \s -> case fetchAndIntArray# m 0# x s of
+  (# s', result #) -> (# s', I# result #)
+{-# INLINE fetchAndInt #-}
+
+-- | Given a reference, and a value to bitwise nand, atomically nand the value with the element. Returns the value of the element before the operation.
+fetchNandInt :: PrimMonad m => PrimVar (PrimState m) Int -> Int -> m Int
+fetchNandInt (PrimVar (MutablePrimArray m)) (I# x) = primitive $ \s -> case fetchNandIntArray# m 0# x s of
+  (# s', result #) -> (# s', I# result #)
+{-# INLINE fetchNandInt #-}
+
+-- | Given a reference, and a value to bitwise or, atomically or the value with the element. Returns the value of the element before the operation.
+fetchOrInt :: PrimMonad m => PrimVar (PrimState m) Int -> Int -> m Int
+fetchOrInt (PrimVar (MutablePrimArray m)) (I# x) = primitive $ \s -> case fetchOrIntArray# m 0# x s of
+  (# s', result #) -> (# s', I# result #)
+{-# INLINE fetchOrInt #-}
+
+-- | Given a reference, and a value to bitwise xor, atomically xor the value with the element. Returns the value of the element before the operation.
+fetchXorInt :: PrimMonad m => PrimVar (PrimState m) Int -> Int -> m Int
+fetchXorInt (PrimVar (MutablePrimArray m)) (I# x) = primitive $ \s -> case fetchXorIntArray# m 0# x s of
+  (# s', result #) -> (# s', I# result #)
+{-# INLINE fetchXorInt #-}
+
+-- | Given a reference, atomically read an element.
+atomicReadInt :: PrimMonad m => PrimVar (PrimState m) Int -> m Int
+atomicReadInt (PrimVar (MutablePrimArray m)) = primitive $ \s -> case atomicReadIntArray# m 0# s of
+  (# s', result #) -> (# s', I# result #)
+{-# INLINE atomicReadInt #-}
+
+-- | Given a reference, atomically write an element.
+atomicWriteInt :: PrimMonad m => PrimVar (PrimState m) Int -> Int -> m ()
+atomicWriteInt (PrimVar (MutablePrimArray m)) (I# x) = primitive_ $ \s -> atomicWriteIntArray# m 0# x s
+{-# INLINE atomicWriteInt #-}
diff --git a/Data/Primitive/Ptr.hs b/Data/Primitive/Ptr.hs
--- a/Data/Primitive/Ptr.hs
+++ b/Data/Primitive/Ptr.hs
@@ -1,7 +1,7 @@
 {-# LANGUAGE CPP #-}
 {-# LANGUAGE MagicHash #-}
-{-# LANGUAGE UnboxedTuples #-}
 {-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TypeApplications #-}
 
 -- |
 -- Module      : Data.Primitive.Ptr
@@ -11,7 +11,7 @@
 -- Maintainer  : Roman Leshchinskiy <rl@cse.unsw.edu.au>
 -- Portability : non-portable
 --
--- Primitive operations on machine addresses
+-- Primitive operations on machine addresses.
 --
 -- @since 0.6.4.0
 
@@ -28,20 +28,17 @@
   -- * Block operations
   copyPtr, movePtr, setPtr
 
-#if __GLASGOW_HASKELL__ >= 708
   , copyPtrToMutablePrimArray
-#endif
+  , copyPtrToMutableByteArray
 ) where
 
 import Control.Monad.Primitive
 import Data.Primitive.Types
-#if __GLASGOW_HASKELL__ >= 708
-import Data.Primitive.PrimArray (MutablePrimArray(..))
-#endif
-
-import GHC.Base ( Int(..) )
-import GHC.Prim
+import Data.Primitive.PrimArray (copyPtrToMutablePrimArray)
+import Data.Primitive.ByteArray (copyPtrToMutableByteArray)
 
+import Data.Proxy
+import GHC.Exts
 import GHC.Ptr
 import Foreign.Marshal.Utils
 
@@ -49,14 +46,14 @@
 -- | Offset a pointer by the given number of elements.
 advancePtr :: forall a. Prim a => Ptr a -> Int -> Ptr a
 {-# INLINE advancePtr #-}
-advancePtr (Ptr a#) (I# i#) = Ptr (plusAddr# a# (i# *# sizeOf# (undefined :: a)))
+advancePtr (Ptr a#) (I# i#) = Ptr (plusAddr# a# (i# *# sizeOfType# (Proxy :: Proxy a)))
 
 -- | Subtract a pointer from another pointer. The result represents
---   the number of elements of type @a@ that fit in the contiguous
---   memory range bounded by these two pointers.
+-- the number of elements of type @a@ that fit in the contiguous
+-- memory range bounded by these two pointers.
 subtractPtr :: forall a. Prim a => Ptr a -> Ptr a -> Int
 {-# INLINE subtractPtr #-}
-subtractPtr (Ptr a#) (Ptr b#) = I# (quotInt# (minusAddr# a# b#) (sizeOf# (undefined :: a)))
+subtractPtr (Ptr a#) (Ptr b#) = I# (quotInt# (minusAddr# a# b#) (sizeOfType# (Proxy :: Proxy a)))
 
 -- | Read a value from a memory position given by a pointer and an offset.
 -- The memory block the address refers to must be immutable. The offset is in
@@ -86,40 +83,21 @@
   -> m ()
 {-# INLINE copyPtr #-}
 copyPtr (Ptr dst#) (Ptr src#) n
-  = unsafePrimToPrim $ copyBytes (Ptr dst#) (Ptr src#) (n * sizeOf (undefined :: a))
+  = unsafePrimToPrim $ copyBytes (Ptr dst#) (Ptr src#) (n * sizeOfType @a)
 
 -- | Copy the given number of elements from the second 'Ptr' to the first. The
 -- areas may overlap.
 movePtr :: forall m a. (PrimMonad m, Prim a)
-  => Ptr a -- ^ destination address
-  -> Ptr a -- ^ source address
+  => Ptr a -- ^ destination pointer
+  -> Ptr a -- ^ source pointer
   -> Int -- ^ number of elements
   -> m ()
 {-# INLINE movePtr #-}
 movePtr (Ptr dst#) (Ptr src#) n
-  = unsafePrimToPrim $ moveBytes (Ptr dst#) (Ptr src#) (n * sizeOf (undefined :: a))
+  = unsafePrimToPrim $ moveBytes (Ptr dst#) (Ptr src#) (n * sizeOfType @a)
 
 -- | Fill a memory block with the given value. The length is in
 -- elements of type @a@ rather than in bytes.
 setPtr :: (Prim a, PrimMonad m) => Ptr a -> Int -> a -> m ()
 {-# INLINE setPtr #-}
 setPtr (Ptr addr#) (I# n#) x = primitive_ (setOffAddr# addr# 0# n# x)
-
-
-#if __GLASGOW_HASKELL__ >= 708
--- | Copy from a pointer to a mutable primitive array.
--- The offset and length are given in elements of type @a@.
--- This function is only available when building with GHC 7.8
--- or newer.
-copyPtrToMutablePrimArray :: forall m a. (PrimMonad m, Prim a)
-  => MutablePrimArray (PrimState m) a -- ^ destination array
-  -> Int -- ^ destination offset
-  -> Ptr a -- ^ source pointer
-  -> Int -- ^ number of elements
-  -> m ()
-{-# INLINE copyPtrToMutablePrimArray #-}
-copyPtrToMutablePrimArray (MutablePrimArray ba#) (I# doff#) (Ptr addr#) (I# n#) = 
-  primitive_ (copyAddrToByteArray# addr# ba# (doff# *# siz#) (n# *# siz#))
-  where
-  siz# = sizeOf# (undefined :: a)
-#endif
diff --git a/Data/Primitive/SmallArray.hs b/Data/Primitive/SmallArray.hs
--- a/Data/Primitive/SmallArray.hs
+++ b/Data/Primitive/SmallArray.hs
@@ -7,6 +7,7 @@
 {-# LANGUAGE DeriveDataTypeable #-}
 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
 {-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE TemplateHaskellQuotes #-}
 
 -- |
 -- Module : Data.Primitive.SmallArray
@@ -18,7 +19,7 @@
 --
 -- Small arrays are boxed (im)mutable arrays.
 --
--- The underlying structure of the 'Array' type contains a card table, allowing
+-- The underlying structure of the 'Data.Primitive.Array.Array' type contains a card table, allowing
 -- segments of the array to be marked as having been mutated. This allows the
 -- garbage collector to only re-traverse segments of the array that have been
 -- marked during certain phases, rather than having to traverse the entire
@@ -30,11 +31,8 @@
 -- entire array. These advantages make them suitable for use as arrays that are
 -- known to be small.
 --
--- The card size is 128, so for uses much larger than that, 'Array' would likely
--- be superior.
---
--- The underlying type, 'SmallArray#', was introduced in GHC 7.10, so prior to
--- that version, this module simply implements small arrays as 'Array'.
+-- The card size is 128, so for uses much larger than that,
+-- 'Data.Primitive.Array.Array' would likely be superior.
 
 module Data.Primitive.SmallArray
   ( SmallArray(..)
@@ -52,28 +50,30 @@
   , freezeSmallArray
   , unsafeFreezeSmallArray
   , thawSmallArray
-  , runSmallArray
   , unsafeThawSmallArray
+  , runSmallArray
+  , createSmallArray
   , sizeofSmallArray
+  , getSizeofSmallMutableArray
   , sizeofSmallMutableArray
+#if MIN_VERSION_base(4,14,0)
+  , shrinkSmallMutableArray
+  , resizeSmallMutableArray
+#endif
+  , emptySmallArray
   , smallArrayFromList
   , smallArrayFromListN
   , mapSmallArray'
   , traverseSmallArrayP
   ) where
 
-
-#if (__GLASGOW_HASKELL__ >= 710)
-#define HAVE_SMALL_ARRAY 1
-#endif
-
-#if MIN_VERSION_base(4,7,0)
 import GHC.Exts hiding (toList)
 import qualified GHC.Exts
-#endif
 
 import Control.Applicative
+import Control.DeepSeq
 import Control.Monad
+import qualified Control.Monad.Fail as Fail
 import Control.Monad.Fix
 import Control.Monad.Primitive
 import Control.Monad.ST
@@ -81,122 +81,102 @@
 import Data.Data
 import Data.Foldable as Foldable
 import Data.Functor.Identity
-#if !(MIN_VERSION_base(4,10,0))
-import Data.Monoid
-#endif
-#if MIN_VERSION_base(4,9,0)
+import Data.Primitive.Internal.Read (Tag(..),lexTag)
+import Text.Read (Read (..), parens, prec)
 import qualified GHC.ST as GHCST
-import qualified Data.Semigroup as Sem
-#endif
+import Data.Semigroup
 import Text.ParserCombinators.ReadP
-#if MIN_VERSION_base(4,10,0)
-import GHC.Exts (runRW#)
-#elif MIN_VERSION_base(4,9,0)
-import GHC.Base (runRW#)
-#endif
-
-#if !(HAVE_SMALL_ARRAY)
-import Data.Primitive.Array
-import Data.Traversable
-import qualified Data.Primitive.Array as Array
-#endif
+import Text.ParserCombinators.ReadPrec (ReadPrec)
+import qualified Text.ParserCombinators.ReadPrec as RdPrc
 
-#if MIN_VERSION_base(4,9,0) || MIN_VERSION_transformers(0,4,0)
-import Data.Functor.Classes (Eq1(..),Ord1(..),Show1(..),Read1(..))
-#endif
+import Data.Functor.Classes (Eq1(..), Ord1(..), Show1(..), Read1(..))
+import Language.Haskell.TH.Syntax (Lift(..))
 
-#if HAVE_SMALL_ARRAY
 data SmallArray a = SmallArray (SmallArray# a)
   deriving Typeable
-#else
-newtype SmallArray a = SmallArray (Array a) deriving
-  ( Eq
-  , Ord
-  , Show
-  , Read
-  , Foldable
-  , Traversable
-  , Functor
-  , Applicative
-  , Alternative
-  , Monad
-  , MonadPlus
-  , MonadZip
-  , MonadFix
-  , Monoid
-  , Typeable
-#if MIN_VERSION_base(4,9,0) || MIN_VERSION_transformers(0,4,0)
-  , Eq1
-  , Ord1
-  , Show1
-  , Read1
-#endif
-  )
 
-#if MIN_VERSION_base(4,7,0)
-instance IsList (SmallArray a) where
-  type Item (SmallArray a) = a
-  fromListN n l = SmallArray (fromListN n l)
-  fromList l = SmallArray (fromList l)
-  toList a = Foldable.toList a
-#endif
+#if MIN_VERSION_deepseq(1,4,3)
+instance NFData1 SmallArray where
+  liftRnf r = foldl' (\_ -> r) ()
 #endif
 
-#if HAVE_SMALL_ARRAY
+instance NFData a => NFData (SmallArray a) where
+  rnf = foldl' (\_ -> rnf) ()
+
 data SmallMutableArray s a = SmallMutableArray (SmallMutableArray# s a)
   deriving Typeable
+
+instance Lift a => Lift (SmallArray a) where
+#if MIN_VERSION_template_haskell(2,16,0)
+  liftTyped ary = case lst of
+    [] -> [|| SmallArray (emptySmallArray# (##)) ||]
+    [x] -> [|| pure $! x ||]
+    x : xs -> [|| unsafeSmallArrayFromListN' len x xs ||]
 #else
-newtype SmallMutableArray s a = SmallMutableArray (MutableArray s a)
-  deriving (Eq, Typeable)
+  lift ary = case lst of
+    [] -> [| SmallArray (emptySmallArray# (##)) |]
+    [x] -> [| pure $! x |]
+    x : xs -> [| unsafeSmallArrayFromListN' len x xs |]
 #endif
+    where
+      len = length ary
+      lst = toList ary
 
+-- | Strictly create an array from a nonempty list (represented as
+-- a first element and a list of the rest) of a known length. If the length
+-- of the list does not match the given length, this makes demons fly
+-- out of your nose. We use it in the 'Lift' instance. If you edit the
+-- splice and break it, you get to keep both pieces.
+unsafeSmallArrayFromListN' :: Int -> a -> [a] -> SmallArray a
+unsafeSmallArrayFromListN' n y ys =
+  createSmallArray n y $ \sma ->
+    let go !_ix [] = return ()
+        go !ix (!x : xs) = do
+            writeSmallArray sma ix x
+            go (ix+1) xs
+    in go 1 ys
+
 -- | Create a new small mutable array.
+--
+-- /Note:/ this function does not check if the input is non-negative.
 newSmallArray
   :: PrimMonad m
   => Int -- ^ size
   -> a   -- ^ initial contents
   -> m (SmallMutableArray (PrimState m) a)
-#if HAVE_SMALL_ARRAY
 newSmallArray (I# i#) x = primitive $ \s ->
   case newSmallArray# i# x s of
     (# s', sma# #) -> (# s', SmallMutableArray sma# #)
-#else
-newSmallArray n e = SmallMutableArray `liftM` newArray n e
-#endif
 {-# INLINE newSmallArray #-}
 
 -- | Read the element at a given index in a mutable array.
+--
+-- /Note:/ this function does not do bounds checking.
 readSmallArray
   :: PrimMonad m
   => SmallMutableArray (PrimState m) a -- ^ array
   -> Int                               -- ^ index
   -> m a
-#if HAVE_SMALL_ARRAY
 readSmallArray (SmallMutableArray sma#) (I# i#) =
   primitive $ readSmallArray# sma# i#
-#else
-readSmallArray (SmallMutableArray a) = readArray a
-#endif
 {-# INLINE readSmallArray #-}
 
 -- | Write an element at the given idex in a mutable array.
+--
+-- /Note:/ this function does not do bounds checking.
 writeSmallArray
   :: PrimMonad m
   => SmallMutableArray (PrimState m) a -- ^ array
   -> Int                               -- ^ index
   -> a                                 -- ^ new element
   -> m ()
-#if HAVE_SMALL_ARRAY
 writeSmallArray (SmallMutableArray sma#) (I# i#) x =
   primitive_ $ writeSmallArray# sma# i# x
-#else
-writeSmallArray (SmallMutableArray a) = writeArray a
-#endif
 {-# INLINE writeSmallArray #-}
 
 -- | Look up an element in an immutable array.
 --
--- The purpose of returning a result using a monad is to allow the caller to
+-- The purpose of returning a result using an applicative is to allow the caller to
 -- avoid retaining references to the array. Evaluating the return value will
 -- cause the array lookup to be performed, even though it may not require the
 -- element of the array to be evaluated (which could throw an exception). For
@@ -208,100 +188,89 @@
 -- > f sa = case indexSmallArrayM sa 0 of
 -- >   Box x -> ...
 --
--- 'x' is not a closure that references 'sa' as it would be if we instead
+-- @x@ is not a closure that references @sa@ as it would be if we instead
 -- wrote:
 --
 -- > let x = indexSmallArray sa 0
 --
--- And does not prevent 'sa' from being garbage collected.
+-- It also does not prevent @sa@ from being garbage collected.
 --
 -- Note that 'Identity' is not adequate for this use, as it is a newtype, and
 -- cannot be evaluated without evaluating the element.
+--
+-- /Note:/ this function does not do bounds checking.
 indexSmallArrayM
-  :: Monad m
+  :: Applicative m
   => SmallArray a -- ^ array
   -> Int          -- ^ index
   -> m a
-#if HAVE_SMALL_ARRAY
 indexSmallArrayM (SmallArray sa#) (I# i#) =
   case indexSmallArray# sa# i# of
     (# x #) -> pure x
-#else
-indexSmallArrayM (SmallArray a) = indexArrayM a
-#endif
 {-# INLINE indexSmallArrayM #-}
 
 -- | Look up an element in an immutable array.
+--
+-- /Note:/ this function does not do bounds checking.
 indexSmallArray
   :: SmallArray a -- ^ array
   -> Int          -- ^ index
   -> a
-#if HAVE_SMALL_ARRAY
 indexSmallArray sa i = runIdentity $ indexSmallArrayM sa i
-#else
-indexSmallArray (SmallArray a) = indexArray a
-#endif
 {-# INLINE indexSmallArray #-}
 
 -- | Read a value from the immutable array at the given index, returning
 -- the result in an unboxed unary tuple. This is currently used to implement
 -- folds.
+--
+-- /Note:/ this function does not do bounds checking.
 indexSmallArray## :: SmallArray a -> Int -> (# a #)
-#if HAVE_SMALL_ARRAY
 indexSmallArray## (SmallArray ary) (I# i) = indexSmallArray# ary i
-#else
-indexSmallArray## (SmallArray a) = indexArray## a
-#endif
 {-# INLINE indexSmallArray## #-}
 
 -- | Create a copy of a slice of an immutable array.
+--
+-- /Note:/ The provided array should contain the full subrange
+-- specified by the two Ints, but this is not checked.
 cloneSmallArray
   :: SmallArray a -- ^ source
   -> Int          -- ^ offset
   -> Int          -- ^ length
   -> SmallArray a
-#if HAVE_SMALL_ARRAY
 cloneSmallArray (SmallArray sa#) (I# i#) (I# j#) =
   SmallArray (cloneSmallArray# sa# i# j#)
-#else
-cloneSmallArray (SmallArray a) i j = SmallArray $ cloneArray a i j
-#endif
 {-# INLINE cloneSmallArray #-}
 
 -- | Create a copy of a slice of a mutable array.
+--
+-- /Note:/ The provided array should contain the full subrange
+-- specified by the two Ints, but this is not checked.
 cloneSmallMutableArray
   :: PrimMonad m
   => SmallMutableArray (PrimState m) a -- ^ source
   -> Int                               -- ^ offset
   -> Int                               -- ^ length
   -> m (SmallMutableArray (PrimState m) a)
-#if HAVE_SMALL_ARRAY
 cloneSmallMutableArray (SmallMutableArray sma#) (I# o#) (I# l#) =
   primitive $ \s -> case cloneSmallMutableArray# sma# o# l# s of
     (# s', smb# #) -> (# s', SmallMutableArray smb# #)
-#else
-cloneSmallMutableArray (SmallMutableArray ma) i j =
-  SmallMutableArray `liftM` cloneMutableArray ma i j
-#endif
 {-# INLINE cloneSmallMutableArray #-}
 
 -- | Create an immutable array corresponding to a slice of a mutable array.
 --
 -- This operation copies the portion of the array to be frozen.
+--
+-- /Note:/ The provided array should contain the full subrange
+-- specified by the two Ints, but this is not checked.
 freezeSmallArray
   :: PrimMonad m
   => SmallMutableArray (PrimState m) a -- ^ source
   -> Int                               -- ^ offset
   -> Int                               -- ^ length
   -> m (SmallArray a)
-#if HAVE_SMALL_ARRAY
 freezeSmallArray (SmallMutableArray sma#) (I# i#) (I# j#) =
   primitive $ \s -> case freezeSmallArray# sma# i# j# s of
     (# s', sa# #) -> (# s', SmallArray sa# #)
-#else
-freezeSmallArray (SmallMutableArray ma) i j =
-  SmallArray `liftM` freezeArray ma i j
-#endif
 {-# INLINE freezeSmallArray #-}
 
 -- | Render a mutable array immutable.
@@ -310,33 +279,26 @@
 -- input array after freezing.
 unsafeFreezeSmallArray
   :: PrimMonad m => SmallMutableArray (PrimState m) a -> m (SmallArray a)
-#if HAVE_SMALL_ARRAY
 unsafeFreezeSmallArray (SmallMutableArray sma#) =
   primitive $ \s -> case unsafeFreezeSmallArray# sma# s of
     (# s', sa# #) -> (# s', SmallArray sa# #)
-#else
-unsafeFreezeSmallArray (SmallMutableArray ma) =
-  SmallArray `liftM` unsafeFreezeArray ma
-#endif
 {-# INLINE unsafeFreezeSmallArray #-}
 
 -- | Create a mutable array corresponding to a slice of an immutable array.
 --
 -- This operation copies the portion of the array to be thawed.
+--
+-- /Note:/ The provided array should contain the full subrange
+-- specified by the two Ints, but this is not checked.
 thawSmallArray
   :: PrimMonad m
   => SmallArray a -- ^ source
   -> Int          -- ^ offset
   -> Int          -- ^ length
   -> m (SmallMutableArray (PrimState m) a)
-#if HAVE_SMALL_ARRAY
 thawSmallArray (SmallArray sa#) (I# o#) (I# l#) =
   primitive $ \s -> case thawSmallArray# sa# o# l# s of
     (# s', sma# #) -> (# s', SmallMutableArray sma# #)
-#else
-thawSmallArray (SmallArray a) off len =
-  SmallMutableArray `liftM` thawArray a off len
-#endif
 {-# INLINE thawSmallArray #-}
 
 -- | Render an immutable array mutable.
@@ -344,16 +306,14 @@
 -- This operation performs no copying, so care must be taken with its use.
 unsafeThawSmallArray
   :: PrimMonad m => SmallArray a -> m (SmallMutableArray (PrimState m) a)
-#if HAVE_SMALL_ARRAY
 unsafeThawSmallArray (SmallArray sa#) =
   primitive $ \s -> case unsafeThawSmallArray# sa# s of
     (# s', sma# #) -> (# s', SmallMutableArray sma# #)
-#else
-unsafeThawSmallArray (SmallArray a) = SmallMutableArray `liftM` unsafeThawArray a
-#endif
 {-# INLINE unsafeThawSmallArray #-}
 
 -- | Copy a slice of an immutable array into a mutable array.
+--
+-- /Note:/ this function does not do bounds or overlap checking.
 copySmallArray
   :: PrimMonad m
   => SmallMutableArray (PrimState m) a -- ^ destination
@@ -362,16 +322,14 @@
   -> Int                               -- ^ source offset
   -> Int                               -- ^ length
   -> m ()
-#if HAVE_SMALL_ARRAY
 copySmallArray
   (SmallMutableArray dst#) (I# do#) (SmallArray src#) (I# so#) (I# l#) =
     primitive_ $ copySmallArray# src# so# dst# do# l#
-#else
-copySmallArray (SmallMutableArray dst) i (SmallArray src) = copyArray dst i src
-#endif
 {-# INLINE copySmallArray #-}
 
 -- | Copy a slice of one mutable array into another.
+--
+-- /Note:/ this function does not do bounds or overlap checking.
 copySmallMutableArray
   :: PrimMonad m
   => SmallMutableArray (PrimState m) a -- ^ destination
@@ -380,46 +338,78 @@
   -> Int                               -- ^ source offset
   -> Int                               -- ^ length
   -> m ()
-#if HAVE_SMALL_ARRAY
 copySmallMutableArray
   (SmallMutableArray dst#) (I# do#)
   (SmallMutableArray src#) (I# so#)
   (I# l#) =
     primitive_ $ copySmallMutableArray# src# so# dst# do# l#
-#else
-copySmallMutableArray (SmallMutableArray dst) i (SmallMutableArray src) =
-  copyMutableArray dst i src
-#endif
 {-# INLINE copySmallMutableArray #-}
 
+-- | The number of elements in an immutable array.
 sizeofSmallArray :: SmallArray a -> Int
-#if HAVE_SMALL_ARRAY
 sizeofSmallArray (SmallArray sa#) = I# (sizeofSmallArray# sa#)
+{-# INLINE sizeofSmallArray #-}
+
+-- | Get the number of elements in a mutable array. Unlike
+-- 'sizeofSmallMutableArray', this function will be sure to produce the correct
+-- result if 'SmallMutableArray' has been shrunk in place. Consider the following:
+--
+-- @
+-- do
+--   sa <- 'newSmallArray' 10 x
+--   print $ 'sizeofSmallMutableArray' sa
+--   'shrinkSmallMutableArray' sa 5
+--   print $ sizeofSmallMutableArray sa
+-- @
+--
+-- The compiler is well within its rights to eliminate the second size check
+-- and print @10@ twice. However, 'getSizeofSmallMutableArray' will check
+-- the size each time it's /executed/ (not /evaluated/), so it won't have this
+-- problem:
+--
+-- @
+-- do
+--   sa <- 'newSmallArray' 10 x
+--   print =<< getSizeofSmallMutableArray sa
+--   'shrinkSmallMutableArray' sa 5
+--   print =<< getSizeofSmallMutableArray sa
+-- @
+--
+-- will certainly print @10@ and then @5@.
+getSizeofSmallMutableArray
+  :: PrimMonad m
+  => SmallMutableArray (PrimState m) a
+  -> m Int
+#if MIN_VERSION_base(4,14,0)
+getSizeofSmallMutableArray (SmallMutableArray sa#) = primitive $ \s ->
+  case getSizeofSmallMutableArray# sa# s of
+    (# s', sz# #) -> (# s', I# sz# #)
 #else
-sizeofSmallArray (SmallArray a) = sizeofArray a
+getSizeofSmallMutableArray sa = pure $! sizeofSmallMutableArray sa
 #endif
-{-# INLINE sizeofSmallArray #-}
+{-# INLINE getSizeofSmallMutableArray #-}
 
+-- | The number of elements in a mutable array. This should only be used
+-- for arrays that are not shrunk in place.
+--
+-- This is deprecated and will be removed in a future release. Use
+-- 'getSizeofSmallMutableArray' instead.
 sizeofSmallMutableArray :: SmallMutableArray s a -> Int
-#if HAVE_SMALL_ARRAY
 sizeofSmallMutableArray (SmallMutableArray sa#) =
   I# (sizeofSmallMutableArray# sa#)
-#else
-sizeofSmallMutableArray (SmallMutableArray ma) = sizeofMutableArray ma
-#endif
+{-# DEPRECATED sizeofSmallMutableArray "use getSizeofSmallMutableArray instead" #-}
 {-# INLINE sizeofSmallMutableArray #-}
 
 -- | This is the fastest, most straightforward way to traverse
 -- an array, but it only works correctly with a sufficiently
 -- "affine" 'PrimMonad' instance. In particular, it must only produce
--- *one* result array. 'Control.Monad.Trans.List.ListT'-transformed
+-- /one/ result array. 'Control.Monad.Trans.List.ListT'-transformed
 -- monads, for example, will not work right at all.
 traverseSmallArrayP
   :: PrimMonad m
   => (a -> m b)
   -> SmallArray a
   -> m (SmallArray b)
-#if HAVE_SMALL_ARRAY
 traverseSmallArrayP f = \ !ary ->
   let
     !sz = sizeofSmallArray ary
@@ -435,39 +425,24 @@
   in do
     mary <- newSmallArray sz badTraverseValue
     go 0 mary
-#else
-traverseSmallArrayP f (SmallArray ar) = SmallArray `liftM` traverseArrayP f ar
-#endif
 {-# INLINE traverseSmallArrayP #-}
 
 -- | Strict map over the elements of the array.
 mapSmallArray' :: (a -> b) -> SmallArray a -> SmallArray b
-#if HAVE_SMALL_ARRAY
 mapSmallArray' f sa = createSmallArray (length sa) (die "mapSmallArray'" "impossible") $ \smb ->
   fix ? 0 $ \go i ->
     when (i < length sa) $ do
       x <- indexSmallArrayM sa i
       let !y = f x
-      writeSmallArray smb i y *> go (i+1)
-#else
-mapSmallArray' f (SmallArray ar) = SmallArray (mapArray' f ar)
-#endif
+      writeSmallArray smb i y *> go (i + 1)
 {-# INLINE mapSmallArray' #-}
 
-#ifndef HAVE_SMALL_ARRAY
-runSmallArray
-  :: (forall s. ST s (SmallMutableArray s a))
-  -> SmallArray a
-runSmallArray m = SmallArray $ runArray $
-  m >>= \(SmallMutableArray mary) -> return mary
-
-#elif !MIN_VERSION_base(4,9,0)
+-- | Execute the monadic action and freeze the resulting array.
+--
+-- > runSmallArray m = runST $ m >>= unsafeFreezeSmallArray
 runSmallArray
   :: (forall s. ST s (SmallMutableArray s a))
   -> SmallArray a
-runSmallArray m = runST $ m >>= unsafeFreezeSmallArray
-
-#else
 -- This low-level business is designed to work with GHC's worker-wrapper
 -- transformation. A lot of the time, we don't actually need an Array
 -- constructor. By putting it on the outside, and being careful about
@@ -475,9 +450,6 @@
 -- The only downside is that separately created 0-length arrays won't share
 -- their Array constructors, although they'll share their underlying
 -- Array#s.
-runSmallArray
-  :: (forall s. ST s (SmallMutableArray s a))
-  -> SmallArray a
 runSmallArray m = SmallArray (runSmallArray# m)
 
 runSmallArray#
@@ -490,15 +462,21 @@
 unST :: ST s a -> State# s -> (# State# s, a #)
 unST (GHCST.ST f) = f
 
-#endif
-
-#if HAVE_SMALL_ARRAY
--- See the comment on runSmallArray for why we use emptySmallArray#.
+-- | Create an array of the given size with a default value,
+-- apply the monadic function and freeze the result. If the
+-- size is 0, return 'emptySmallArray' (rather than a new copy thereof).
+--
+-- > createSmallArray 0 _ _ = emptySmallArray
+-- > createSmallArray n x f = runSmallArray $ do
+-- >   mary <- newSmallArray n x
+-- >   f mary
+-- >   pure mary
 createSmallArray
   :: Int
   -> a
   -> (forall s. SmallMutableArray s a -> ST s ())
   -> SmallArray a
+-- See the comment on runSmallArray for why we use emptySmallArray#.
 createSmallArray 0 _ _ = SmallArray (emptySmallArray# (# #))
 createSmallArray n x f = runSmallArray $ do
   mary <- newSmallArray n x
@@ -512,6 +490,7 @@
 die :: String -> String -> a
 die fun problem = error $ "Data.Primitive.SmallArray." ++ fun ++ ": " ++ problem
 
+-- | The empty 'SmallArray'.
 emptySmallArray :: SmallArray a
 emptySmallArray =
   runST $ newSmallArray 0 (die "emptySmallArray" "impossible")
@@ -535,17 +514,11 @@
     = True
     | (# x #) <- indexSmallArray## sa1 i
     , (# y #) <- indexSmallArray## sa2 i
-    = p x y && loop (i-1)
+    = p x y && loop (i - 1)
 
-#if MIN_VERSION_base(4,9,0) || MIN_VERSION_transformers(0,4,0)
 -- | @since 0.6.4.0
 instance Eq1 SmallArray where
-#if MIN_VERSION_base(4,9,0) || MIN_VERSION_transformers(0,5,0)
   liftEq = smallArrayLiftEq
-#else
-  eq1 = smallArrayLiftEq (==)
-#endif
-#endif
 
 instance Eq a => Eq (SmallArray a) where
   sa1 == sa2 = smallArrayLiftEq (==) sa1 sa2
@@ -562,18 +535,12 @@
     | i < mn
     , (# x1 #) <- indexSmallArray## a1 i
     , (# x2 #) <- indexSmallArray## a2 i
-    = elemCompare x1 x2 `mappend` loop (i+1)
+    = elemCompare x1 x2 `mappend` loop (i + 1)
     | otherwise = compare (length a1) (length a2)
 
-#if MIN_VERSION_base(4,9,0) || MIN_VERSION_transformers(0,4,0)
 -- | @since 0.6.4.0
 instance Ord1 SmallArray where
-#if MIN_VERSION_base(4,9,0) || MIN_VERSION_transformers(0,5,0)
   liftCompare = smallArrayLiftCompare
-#else
-  compare1 = smallArrayLiftCompare compare
-#endif
-#endif
 
 -- | Lexicographic ordering. Subject to change between major versions.
 instance Ord a => Ord (SmallArray a) where
@@ -589,7 +556,7 @@
       go i
         | i == sz = z
         | (# x #) <- indexSmallArray## ary i
-        = f x (go (i+1))
+        = f x (go (i + 1))
     in go 0
   {-# INLINE foldr #-}
   foldl f = \z !ary ->
@@ -597,7 +564,7 @@
       go i
         | i < 0 = z
         | (# x #) <- indexSmallArray## ary i
-        = f (go (i-1)) x
+        = f (go (i - 1)) x
     in go (sizeofSmallArray ary - 1)
   {-# INLINE foldl #-}
   foldr1 f = \ !ary ->
@@ -606,7 +573,7 @@
       go i =
         case indexSmallArray## ary i of
           (# x #) | i == sz -> x
-                  | otherwise -> f x (go (i+1))
+                  | otherwise -> f x (go (i + 1))
     in if sz < 0
        then die "foldr1" "Empty SmallArray"
        else go 0
@@ -627,7 +594,7 @@
       go i !acc
         | i == -1 = acc
         | (# x #) <- indexSmallArray## ary i
-        = go (i-1) (f x acc)
+        = go (i - 1) (f x acc)
     in go (sizeofSmallArray ary - 1) z
   {-# INLINE foldr' #-}
   foldl' f = \z !ary ->
@@ -636,7 +603,7 @@
       go i !acc
         | i == sz = acc
         | (# x #) <- indexSmallArray## ary i
-        = go (i+1) (f acc x)
+        = go (i + 1) (f acc x)
     in go 0 z
   {-# INLINE foldl' #-}
   null a = sizeofSmallArray a == 0
@@ -651,7 +618,7 @@
      go i !e
        | i == sz = e
        | (# x #) <- indexSmallArray## ary i
-       = go (i+1) (max e x)
+       = go (i + 1) (max e x)
   {-# INLINE maximum #-}
   minimum ary | sz == 0   = die "minimum" "Empty SmallArray"
               | (# frst #) <- indexSmallArray## ary 0
@@ -660,14 +627,14 @@
          go i !e
            | i == sz = e
            | (# x #) <- indexSmallArray## ary i
-           = go (i+1) (min e x)
+           = go (i + 1) (min e x)
   {-# INLINE minimum #-}
   sum = foldl' (+) 0
   {-# INLINE sum #-}
   product = foldl' (*) 1
   {-# INLINE product #-}
 
-newtype STA a = STA {_runSTA :: forall s. SmallMutableArray# s a -> ST s (SmallArray a)}
+newtype STA a = STA { _runSTA :: forall s. SmallMutableArray# s a -> ST s (SmallArray a) }
 
 runSTA :: Int -> STA a -> SmallArray a
 runSTA !sz = \ (STA m) -> runST $ newSmallArray_ sz >>=
@@ -699,8 +666,8 @@
                   writeSmallArray (SmallMutableArray mary) i b >> m mary)
                (f x) (go (i + 1))
   in if len == 0
-     then pure emptySmallArray
-     else runSTA len <$> go 0
+    then pure emptySmallArray
+    else runSTA len <$> go 0
 {-# INLINE [1] traverseSmallArray #-}
 
 {-# RULES
@@ -717,7 +684,7 @@
     fix ? 0 $ \go i ->
       when (i < length sa) $ do
         x <- indexSmallArrayM sa i
-        writeSmallArray smb i (f x) *> go (i+1)
+        writeSmallArray smb i (f x) *> go (i + 1)
   {-# INLINE fmap #-}
 
   x <$ sa = createSmallArray (length sa) x noOp
@@ -725,36 +692,36 @@
 instance Applicative SmallArray where
   pure x = createSmallArray 1 x noOp
 
-  sa *> sb = createSmallArray (la*lb) (die "*>" "impossible") $ \smb ->
+  sa *> sb = createSmallArray (la * lb) (die "*>" "impossible") $ \smb ->
     fix ? 0 $ \go i ->
       when (i < la) $
-        copySmallArray smb 0 sb 0 lb *> go (i+1)
+        copySmallArray smb (i * lb) sb 0 lb *> go (i + 1)
    where
-   la = length sa ; lb = length sb
+    la = length sa; lb = length sb
 
-  a <* b = createSmallArray (sza*szb) (die "<*" "impossible") $ \ma ->
+  a <* b = createSmallArray (sza * szb) (die "<*" "impossible") $ \ma ->
     let fill off i e = when (i < szb) $
-                         writeSmallArray ma (off+i) e >> fill off (i+1) e
+                         writeSmallArray ma (off + i) e >> fill off (i + 1) e
         go i = when (i < sza) $ do
                  x <- indexSmallArrayM a i
-                 fill (i*szb) 0 x
-                 go (i+1)
+                 fill (i * szb) 0 x
+                 go (i + 1)
      in go 0
-   where sza = sizeofSmallArray a ; szb = sizeofSmallArray b
+   where sza = sizeofSmallArray a; szb = sizeofSmallArray b
 
-  ab <*> a = createSmallArray (szab*sza) (die "<*>" "impossible") $ \mb ->
+  ab <*> a = createSmallArray (szab * sza) (die "<*>" "impossible") $ \mb ->
     let go1 i = when (i < szab) $
             do
               f <- indexSmallArrayM ab i
-              go2 (i*sza) f 0
-              go1 (i+1)
+              go2 (i * sza) f 0
+              go1 (i + 1)
         go2 off f j = when (j < sza) $
             do
               x <- indexSmallArrayM a j
               writeSmallArray mb (off + j) (f x)
               go2 off f (j + 1)
     in go1 0
-   where szab = sizeofSmallArray ab ; sza = sizeofSmallArray a
+   where szab = sizeofSmallArray ab; sza = sizeofSmallArray a
 
 instance Alternative SmallArray where
   empty = emptySmallArray
@@ -788,26 +755,31 @@
   return = pure
   (>>) = (*>)
 
-  sa >>= f = collect 0 EmptyStack (la-1)
+  sa >>= f = collect 0 EmptyStack (la - 1)
    where
-   la = length sa
-   collect sz stk i
-     | i < 0 = createSmallArray sz (die ">>=" "impossible") $ fill 0 stk
-     | (# x #) <- indexSmallArray## sa i
-     , let sb = f x
-           lsb = length sb
-       -- If we don't perform this check, we could end up allocating
-       -- a stack full of empty arrays if someone is filtering most
-       -- things out. So we refrain from pushing empty arrays.
-     = if lsb == 0
-       then collect sz stk (i-1)
-       else collect (sz + lsb) (PushArray sb stk) (i-1)
+    la = length sa
+    collect sz stk i
+      | i < 0 = createSmallArray sz (die ">>=" "impossible") $ fill 0 stk
+      | (# x #) <- indexSmallArray## sa i
+      , let sb = f x
+            lsb = length sb
+        -- If we don't perform this check, we could end up allocating
+        -- a stack full of empty arrays if someone is filtering most
+        -- things out. So we refrain from pushing empty arrays.
+      = if lsb == 0
+        then collect sz stk (i - 1)
+        else collect (sz + lsb) (PushArray sb stk) (i - 1)
 
-   fill _ EmptyStack _ = return ()
-   fill off (PushArray sb sbs) smb =
-     copySmallArray smb off sb 0 (length sb)
-       *> fill (off + length sb) sbs smb
+    fill _ EmptyStack _ = return ()
+    fill off (PushArray sb sbs) smb =
+      copySmallArray smb off sb 0 (length sb)
+        *> fill (off + length sb) sbs smb
 
+#if !(MIN_VERSION_base(4,13,0))
+  fail = Fail.fail
+#endif
+
+instance Fail.MonadFail SmallArray where
   fail _ = emptySmallArray
 
 instance MonadPlus SmallArray where
@@ -821,7 +793,7 @@
       x <- indexSmallArrayM sa i
       y <- indexSmallArrayM sb i
       writeSmallArray mc i (f x y)
-      go (i+1)
+      go (i + 1)
 {-# INLINE zipW #-}
 
 instance MonadZip SmallArray where
@@ -836,13 +808,13 @@
       when (i < sz) $ case indexSmallArray sab i of
         (x, y) -> do writeSmallArray sma i x
                      writeSmallArray smb i y
-                     go $ i+1
+                     go (i + 1)
     (,) <$> unsafeFreezeSmallArray sma
         <*> unsafeFreezeSmallArray smb
 
 instance MonadFix SmallArray where
   mfix f = createSmallArray (sizeofSmallArray (f err))
-                            (die "mfix" "impossible") $ flip fix 0 $
+                            (die "mfix" "impossible") $ fix ? 0 $
     \r !i !mary -> when (i < sz) $ do
                       writeSmallArray mary i (fix (\xi -> f xi `indexSmallArray` i))
                       r (i + 1) mary
@@ -850,24 +822,31 @@
       sz = sizeofSmallArray (f err)
       err = error "mfix for Data.Primitive.SmallArray applied to strict function."
 
-#if MIN_VERSION_base(4,9,0)
 -- | @since 0.6.3.0
-instance Sem.Semigroup (SmallArray a) where
+instance Semigroup (SmallArray a) where
   (<>) = (<|>)
   sconcat = mconcat . toList
-#endif
+  stimes n arr = case compare n 0 of
+    LT -> die "stimes" "negative multiplier"
+    EQ -> empty
+    GT -> createSmallArray (n' * sizeofSmallArray arr) (die "stimes" "impossible") $ \sma ->
+      let go i = when (i < n') $ do
+            copySmallArray sma (i * sizeofSmallArray arr) arr 0 (sizeofSmallArray arr)
+            go (i + 1)
+      in go 0
+    where n' = fromIntegral n :: Int
 
 instance Monoid (SmallArray a) where
   mempty = empty
 #if !(MIN_VERSION_base(4,11,0))
-  mappend = (<|>)
+  mappend = (<>)
 #endif
   mconcat l = createSmallArray n (die "mconcat" "impossible") $ \ma ->
     let go !_  [    ] = return ()
         go off (a:as) =
           copySmallArray ma off a 0 (sizeofSmallArray a) >> go (off + sizeofSmallArray a) as
      in go 0 l
-   where n = sum . fmap length $ l
+   where n = sum (fmap length l)
 
 instance IsList (SmallArray a) where
   type Item (SmallArray a) = a
@@ -876,9 +855,8 @@
   toList = Foldable.toList
 
 smallArrayLiftShowsPrec :: (Int -> a -> ShowS) -> ([a] -> ShowS) -> Int -> SmallArray a -> ShowS
-smallArrayLiftShowsPrec elemShowsPrec elemListShowsPrec p sa = showParen (p > 10) $
-  showString "fromListN " . shows (length sa) . showString " "
-    . listLiftShowsPrec elemShowsPrec elemListShowsPrec 11 (toList sa)
+smallArrayLiftShowsPrec elemShowsPrec elemListShowsPrec _ sa =
+  listLiftShowsPrec elemShowsPrec elemListShowsPrec 11 (toList sa)
 
 -- this need to be included for older ghcs
 listLiftShowsPrec :: (Int -> a -> ShowS) -> ([a] -> ShowS) -> Int -> [a] -> ShowS
@@ -887,39 +865,31 @@
 instance Show a => Show (SmallArray a) where
   showsPrec p sa = smallArrayLiftShowsPrec showsPrec showList p sa
 
-#if MIN_VERSION_base(4,9,0) || MIN_VERSION_transformers(0,4,0)
 -- | @since 0.6.4.0
 instance Show1 SmallArray where
-#if MIN_VERSION_base(4,9,0) || MIN_VERSION_transformers(0,5,0)
   liftShowsPrec = smallArrayLiftShowsPrec
-#else
-  showsPrec1 = smallArrayLiftShowsPrec showsPrec showList
-#endif
-#endif
 
-smallArrayLiftReadsPrec :: (Int -> ReadS a) -> ReadS [a] -> Int -> ReadS (SmallArray a)
-smallArrayLiftReadsPrec _ listReadsPrec p = readParen (p > 10) . readP_to_S $ do
-  () <$ string "fromListN"
-  skipSpaces
-  n <- readS_to_P reads
-  skipSpaces
-  l <- readS_to_P listReadsPrec
-  return $ smallArrayFromListN n l
+-- See Note [Forgiving Array Read Instance]
+smallArrayLiftReadPrec :: ReadPrec a -> ReadPrec [a] -> ReadPrec (SmallArray a)
+smallArrayLiftReadPrec _ read_list =
+  ( RdPrc.lift skipSpaces >> fmap fromList read_list )
+  RdPrc.+++
+  ( parens $ prec app_prec $ do
+      RdPrc.lift skipSpaces
+      tag <- RdPrc.lift lexTag
+      case tag of
+        FromListTag -> fromList <$> read_list
+        FromListNTag -> liftM2 fromListN readPrec read_list
+  )
+  where
+  app_prec = 10
 
 instance Read a => Read (SmallArray a) where
-  readsPrec = smallArrayLiftReadsPrec readsPrec readList
+  readPrec = smallArrayLiftReadPrec readPrec readListPrec
 
-#if MIN_VERSION_base(4,9,0) || MIN_VERSION_transformers(0,4,0)
 -- | @since 0.6.4.0
 instance Read1 SmallArray where
-#if MIN_VERSION_base(4,9,0) || MIN_VERSION_transformers(0,5,0)
-  liftReadsPrec = smallArrayLiftReadsPrec
-#else
-  readsPrec1 = smallArrayLiftReadsPrec readsPrec readList
-#endif
-#endif
-
-
+  liftReadPrec = smallArrayLiftReadPrec
 
 smallArrayDataType :: DataType
 smallArrayDataType =
@@ -940,28 +910,60 @@
   toConstr _ = die "toConstr" "SmallMutableArray"
   gunfold _ _ = die "gunfold" "SmallMutableArray"
   dataTypeOf _ = mkNoRepType "Data.Primitive.SmallArray.SmallMutableArray"
-#endif
 
 -- | Create a 'SmallArray' from a list of a known length. If the length
---   of the list does not match the given length, this throws an exception.
+-- of the list does not match the given length, this throws an exception.
 smallArrayFromListN :: Int -> [a] -> SmallArray a
-#if HAVE_SMALL_ARRAY
+{-# INLINE smallArrayFromListN #-}
 smallArrayFromListN n l =
   createSmallArray n
       (die "smallArrayFromListN" "uninitialized element") $ \sma ->
-  let go !ix [] = if ix == n
+  let z ix# = if I# ix# == n
         then return ()
         else die "smallArrayFromListN" "list length less than specified size"
-      go !ix (x : xs) = if ix < n
+      f x k = GHC.Exts.oneShot $ \ix# -> if I# ix# < n
         then do
-          writeSmallArray sma ix x
-          go (ix+1) xs
+          writeSmallArray sma (I# ix#) x
+          k (ix# +# 1#)
         else die "smallArrayFromListN" "list length greater than specified size"
-  in go 0 l
-#else
-smallArrayFromListN n l = SmallArray (Array.fromListN n l)
-#endif
+  in foldr f z l 0#
 
 -- | Create a 'SmallArray' from a list.
 smallArrayFromList :: [a] -> SmallArray a
 smallArrayFromList l = smallArrayFromListN (length l) l
+
+#if MIN_VERSION_base(4,14,0)
+-- | Shrink the mutable array in place. The size given must be equal to
+-- or less than the current size of the array. This is not checked.
+shrinkSmallMutableArray :: PrimMonad m
+  => SmallMutableArray (PrimState m) a
+  -> Int
+  -> m ()
+{-# inline shrinkSmallMutableArray #-}
+shrinkSmallMutableArray (SmallMutableArray x) (I# n) = primitive
+  (\s0 -> case GHC.Exts.shrinkSmallMutableArray# x n s0 of
+    s1 -> (# s1, () #)
+  )
+
+-- | Resize a mutable array to new specified size. The returned
+-- 'SmallMutableArray' is either the original 'SmallMutableArray'
+-- resized in-place or, if not possible, a newly allocated
+-- 'SmallMutableArray' with the original content copied over.
+--
+-- To avoid undefined behaviour, the original 'SmallMutableArray'
+-- shall not be accessed anymore after a 'resizeSmallMutableArray' has
+-- been performed. Moreover, no reference to the old one should be
+-- kept in order to allow garbage collection of the original
+-- 'SmallMutableArray' in case a new 'SmallMutableArray' had to be
+-- allocated.
+resizeSmallMutableArray :: PrimMonad m
+  => SmallMutableArray (PrimState m) a
+  -> Int -- ^ New size
+  -> a   -- ^ Newly created slots initialized to this element. Only used when array is grown.
+  -> m (SmallMutableArray (PrimState m) a)
+resizeSmallMutableArray (SmallMutableArray arr) (I# n) x = primitive
+  (\s0 -> case GHC.Exts.resizeSmallMutableArray# arr n x s0 of
+    (# s1, arr' #) -> (# s1, SmallMutableArray arr' #)
+  )
+{-# INLINE resizeSmallMutableArray #-}
+#endif
diff --git a/Data/Primitive/Types.hs b/Data/Primitive/Types.hs
--- a/Data/Primitive/Types.hs
+++ b/Data/Primitive/Types.hs
@@ -1,7 +1,13 @@
-{-# LANGUAGE CPP, UnboxedTuples, MagicHash, DeriveDataTypeable #-}
-{-# LANGUAGE GeneralizedNewtypeDeriving, StandaloneDeriving #-}
+{-# LANGUAGE AllowAmbiguousTypes #-}
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE UnboxedTuples #-}
+{-# LANGUAGE MagicHash #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE StandaloneDeriving #-}
 {-# LANGUAGE ScopedTypeVariables #-}
-#if __GLASGOW_HASKELL__ >= 800
+{-# LANGUAGE TypeApplications #-}
+
+#if __GLASGOW_HASKELL__ < 906
 {-# LANGUAGE TypeInType #-}
 #endif
 
@@ -15,84 +21,78 @@
 -- Maintainer  : Roman Leshchinskiy <rl@cse.unsw.edu.au>
 -- Portability : non-portable
 --
--- Basic types and classes for primitive array operations
---
-
-module Data.Primitive.Types (
-  Prim(..),
-  sizeOf, alignment, defaultSetByteArray#, defaultSetOffAddr#,
+-- Basic types and classes for primitive array operations.
 
-  Addr(..),
-  PrimStorable(..)
-) where
+module Data.Primitive.Types
+  ( Prim(..)
+  , sizeOf, sizeOfType, alignment, alignmentOfType, defaultSetByteArray#, defaultSetOffAddr#
+  , PrimStorable(..)
+  , Ptr(..)
+  ) where
 
 import Control.Monad.Primitive
 import Data.Primitive.MachDeps
 import Data.Primitive.Internal.Operations
+import Foreign.Ptr (IntPtr, intPtrToPtr, ptrToIntPtr, WordPtr, wordPtrToPtr, ptrToWordPtr)
 import Foreign.C.Types
 import System.Posix.Types
+import Data.Complex
 
-import GHC.Base (
-    Int(..), Char(..),
-  )
-import GHC.Float (
-    Float(..), Double(..)
-  )
-import GHC.Word (
-    Word(..), Word8(..), Word16(..), Word32(..), Word64(..)
-  )
-import GHC.Int (
-    Int8(..), Int16(..), Int32(..), Int64(..)
-  )
+import GHC.Word (Word8(..), Word16(..), Word32(..), Word64(..))
+import GHC.Int (Int8(..), Int16(..), Int32(..), Int64(..))
 
-import GHC.Ptr (
-    Ptr(..), FunPtr(..)
-  )
+import GHC.Stable (StablePtr(..))
 
-import GHC.Prim
-#if __GLASGOW_HASKELL__ >= 706
-    hiding (setByteArray#)
-#endif
+import GHC.Exts hiding (setByteArray#)
 
-import Data.Typeable ( Typeable )
-import Data.Data ( Data(..) )
-import Data.Primitive.Internal.Compat ( isTrue#, mkNoRepType )
 import Foreign.Storable (Storable)
-import Numeric
 
-import qualified Foreign.Storable as FS
 
--- | A machine address
-data Addr = Addr Addr# deriving ( Typeable )
+import qualified Foreign.Storable as FS
 
-instance Show Addr where
-  showsPrec _ (Addr a) =
-    showString "0x" . showHex (fromIntegral (I# (addr2Int# a)) :: Word)
+import GHC.IO (IO(..))
+import qualified GHC.Exts
 
-instance Eq Addr where
-  Addr a# == Addr b# = isTrue# (eqAddr# a# b#)
-  Addr a# /= Addr b# = isTrue# (neAddr# a# b#)
+import Control.Applicative (Const(..))
+import Data.Functor.Identity (Identity(..))
+import qualified Data.Monoid as Monoid
+import qualified Data.Semigroup as Semigroup
+import Data.Proxy
 
-instance Ord Addr where
-  Addr a# > Addr b# = isTrue# (gtAddr# a# b#)
-  Addr a# >= Addr b# = isTrue# (geAddr# a# b#)
-  Addr a# < Addr b# = isTrue# (ltAddr# a# b#)
-  Addr a# <= Addr b# = isTrue# (leAddr# a# b#)
+#if !MIN_VERSION_base(4,13,0)
+import Data.Ord (Down(..))
+#endif
 
-instance Data Addr where
-  toConstr _ = error "toConstr"
-  gunfold _ _ = error "gunfold"
-  dataTypeOf _ = mkNoRepType "Data.Primitive.Types.Addr"
+-- | Class of types supporting primitive array operations. This includes
+-- interfacing with GC-managed memory (functions suffixed with @ByteArray#@)
+-- and interfacing with unmanaged memory (functions suffixed with @Addr#@).
+-- Endianness is platform-dependent.
+class Prim a where
+  -- We use `Proxy` instead of `Proxy#`, since the latter doesn't work with GND for GHC <= 8.8.
 
+  -- | The size of values of type @a@ in bytes. This has to be used with TypeApplications: @sizeOfType \@a@.
+  --
+  -- @since 0.9.0.0
+  sizeOfType# :: Proxy a -> Int#
+  sizeOfType# _ = sizeOf# (dummy :: a)
 
--- | Class of types supporting primitive array operations
-class Prim a where
+  -- | The size of values of type @a@ in bytes. The argument is not used.
+  --
+  -- It is recommended to use 'sizeOfType#' instead.
+  sizeOf# :: a -> Int#
+  sizeOf# _ = sizeOfType# (Proxy :: Proxy a)
 
-  -- | Size of values of type @a@. The argument is not used.
-  sizeOf#    :: a -> Int#
+  -- | The alignment of values of type @a@ in bytes. This has to be used with TypeApplications: @alignmentOfType \@a@.
+  --
+  -- @since 0.9.0.0
+  alignmentOfType# :: Proxy a -> Int#
+  alignmentOfType# _ = alignment# (dummy :: a)
 
-  -- | Alignment of values of type @a@. The argument is not used.
+  -- | The alignment of values of type @a@ in bytes. The argument is not used.
+  --
+  -- It is recommended to use 'alignmentOfType#' instead.
   alignment# :: a -> Int#
+  alignment# _ = alignmentOfType# (Proxy :: Proxy a)
 
   -- | Read a value from the array. The offset is in elements of type
   -- @a@ rather than in bytes.
@@ -108,7 +108,14 @@
 
   -- | Fill a slice of the mutable array with a value. The offset and length
   -- of the chunk are in elements of type @a@ rather than in bytes.
-  setByteArray# :: MutableByteArray# s -> Int# -> Int# -> a -> State# s -> State# s
+  setByteArray#
+    :: MutableByteArray# s
+    -> Int# -- ^ offset
+    -> Int# -- ^ length
+    -> a
+    -> State# s
+    -> State# s
+  setByteArray# = defaultSetByteArray#
 
   -- | Read a value from a memory position given by an address and an offset.
   -- The memory block the address refers to must be immutable. The offset is in
@@ -125,33 +132,106 @@
 
   -- | Fill a memory block given by an address, an offset and a length.
   -- The offset and length are in elements of type @a@ rather than in bytes.
-  setOffAddr# :: Addr# -> Int# -> Int# -> a -> State# s -> State# s
+  setOffAddr#
+    :: Addr#
+    -> Int# -- ^ offset
+    -> Int# -- ^ length
+    -> a
+    -> State# s
+    -> State# s
+  setOffAddr# = defaultSetOffAddr#
 
--- | Size of values of type @a@. The argument is not used.
+  {-# MINIMAL (sizeOfType# | sizeOf#), (alignmentOfType# | alignment#), indexByteArray#, readByteArray#, writeByteArray#,
+    indexOffAddr#, readOffAddr#, writeOffAddr# #-}
+
+-- | A dummy value of type @a@.
+dummy :: a
+dummy = errorWithoutStackTrace "Data.Primitive.Types: implementation mistake in `Prim` instance"
+{-# NOINLINE dummy #-}
+
+-- | The size of values of type @a@ in bytes. This has to be used with TypeApplications: @sizeOfType \@a@.
 --
+-- >>> :set -XTypeApplications
+-- >>> import Data.Int (Int32)
+-- >>> sizeOfType @Int32
+-- 4
+--
+-- @since 0.9.0.0
+sizeOfType :: forall a. Prim a => Int
+sizeOfType = I# (sizeOfType# (Proxy :: Proxy a))
+
+-- | The size of values of type @a@ in bytes. The argument is not used.
+--
+-- It is recommended to use 'sizeOfType' instead.
+--
 -- This function has existed since 0.1, but was moved from 'Data.Primitive'
--- to 'Data.Primitive.Types' in version 0.6.3.0
+-- to 'Data.Primitive.Types' in version 0.6.3.0.
 sizeOf :: Prim a => a -> Int
 sizeOf x = I# (sizeOf# x)
 
--- | Alignment of values of type @a@. The argument is not used.
+-- | The alignment of values of type @a@ in bytes. This has to be used with TypeApplications: @alignmentOfType \@a@.
 --
+-- @since 0.9.0.0
+alignmentOfType :: forall a. Prim a => Int
+alignmentOfType = I# (alignmentOfType# (Proxy :: Proxy a))
+
+-- | The alignment of values of type @a@ in bytes. The argument is not used.
+--
+-- It is recommended to use 'alignmentOfType' instead.
+--
 -- This function has existed since 0.1, but was moved from 'Data.Primitive'
--- to 'Data.Primitive.Types' in version 0.6.3.0
+-- to 'Data.Primitive.Types' in version 0.6.3.0.
 alignment :: Prim a => a -> Int
 alignment x = I# (alignment# x)
 
+-- | @since 0.9.0.0
+instance Prim a => Prim (Complex a) where
+  sizeOf# _ = 2# *# sizeOf# (undefined :: a)
+  alignment# _ = alignment# (undefined :: a)
+  indexByteArray# arr# i# =
+    let x = indexByteArray# arr# (2# *# i#)
+        y = indexByteArray# arr# (2# *# i# +# 1#)
+    in x :+ y
+  readByteArray# arr# i# =
+    \s0 -> case readByteArray# arr# (2# *# i#) s0 of
+       (# s1#, x #) -> case readByteArray# arr# (2# *# i# +# 1#) s1# of
+          (# s2#, y #) -> (# s2#, x :+ y #)
+  writeByteArray# arr# i# (a :+ b) =
+    \s0 -> case writeByteArray# arr# (2# *# i#) a s0 of
+       s1 -> case writeByteArray# arr# (2# *# i# +# 1#) b s1 of
+         s2 -> s2
+  indexOffAddr# addr# i# =
+    let x = indexOffAddr# addr# (2# *# i#)
+        y = indexOffAddr# addr# (2# *# i# +# 1#)
+    in x :+ y
+  readOffAddr# addr# i# =
+    \s0 -> case readOffAddr# addr# (2# *# i#) s0 of
+       (# s1, x #) -> case readOffAddr# addr# (2# *# i# +# 1#) s1 of
+         (# s2, y #) -> (# s2, x :+ y #)
+  writeOffAddr# addr# i# (a :+ b) =
+    \s0 -> case writeOffAddr# addr# (2# *# i#) a s0 of
+       s1 -> case writeOffAddr# addr# (2# *# i# +# 1#) b s1 of
+         s2 -> s2
+  {-# INLINE sizeOf# #-}
+  {-# INLINE alignment# #-}
+  {-# INLINE indexByteArray# #-}
+  {-# INLINE readByteArray# #-}
+  {-# INLINE writeByteArray# #-}
+  {-# INLINE indexOffAddr# #-}
+  {-# INLINE readOffAddr# #-}
+  {-# INLINE writeOffAddr# #-}
+
 -- | An implementation of 'setByteArray#' that calls 'writeByteArray#'
 -- to set each element. This is helpful when writing a 'Prim' instance
--- for a multi-word data type for which there is no cpu-accelerated way
+-- for a multi-word data type for which there is no CPU-accelerated way
 -- to broadcast a value to contiguous memory. It is typically used
 -- alongside 'defaultSetOffAddr#'. For example:
 --
 -- > data Trip = Trip Int Int Int
 -- >
 -- > instance Prim Trip
--- >   sizeOf# _ = 3# *# sizeOf# (undefined :: Int)
--- >   alignment# _ = alignment# (undefined :: Int)
+-- >   sizeOfType# _ = 3# *# sizeOfType# (proxy# :: Proxy# Int)
+-- >   alignmentOfType# _ = alignmentOfType# (proxy# :: Proxy# Int)
 -- >   indexByteArray# arr# i# = ...
 -- >   readByteArray# arr# i# = ...
 -- >   writeByteArray# arr# i# (Trip a b c) =
@@ -201,8 +281,8 @@
 newtype PrimStorable a = PrimStorable { getPrimStorable :: a }
 
 instance Prim a => Storable (PrimStorable a) where
-  sizeOf _ = sizeOf (undefined :: a)
-  alignment _ = alignment (undefined :: a)
+  sizeOf _ = sizeOfType @a
+  alignment _ = alignmentOfType @a
   peekElemOff (Ptr addr#) (I# i#) =
     primitive $ \s0# -> case readOffAddr# addr# i# s0# of
       (# s1, x #) -> (# s1, PrimStorable x #)
@@ -210,42 +290,58 @@
     writeOffAddr# addr# i# a s#
 
 #define derivePrim(ty, ctr, sz, align, idx_arr, rd_arr, wr_arr, set_arr, idx_addr, rd_addr, wr_addr, set_addr) \
-instance Prim (ty) where {                                      \
-  sizeOf# _ = unI# sz                                           \
-; alignment# _ = unI# align                                     \
-; indexByteArray# arr# i# = ctr (idx_arr arr# i#)               \
-; readByteArray#  arr# i# s# = case rd_arr arr# i# s# of        \
-                        { (# s1#, x# #) -> (# s1#, ctr x# #) }  \
-; writeByteArray# arr# i# (ctr x#) s# = wr_arr arr# i# x# s#    \
-; setByteArray# arr# i# n# (ctr x#) s#                          \
-    = let { i = fromIntegral (I# i#)                            \
-          ; n = fromIntegral (I# n#)                            \
-          } in                                                  \
-      case unsafeCoerce# (internal (set_arr arr# i n x#)) s# of \
-        { (# s1#, _ #) -> s1# }                                 \
-                                                                \
-; indexOffAddr# addr# i# = ctr (idx_addr addr# i#)              \
-; readOffAddr#  addr# i# s# = case rd_addr addr# i# s# of       \
-                        { (# s1#, x# #) -> (# s1#, ctr x# #) }  \
-; writeOffAddr# addr# i# (ctr x#) s# = wr_addr addr# i# x# s#   \
-; setOffAddr# addr# i# n# (ctr x#) s#                           \
-    = let { i = fromIntegral (I# i#)                            \
-          ; n = fromIntegral (I# n#)                            \
-          } in                                                  \
+instance Prim (ty) where {                                        \
+  sizeOfType# _ = unI# sz                                         \
+; alignmentOfType# _ = unI# align                                 \
+; indexByteArray# arr# i# = ctr (idx_arr arr# i#)                 \
+; readByteArray#  arr# i# s# = case rd_arr arr# i# s# of          \
+                        { (# s1#, x# #) -> (# s1#, ctr x# #) }    \
+; writeByteArray# arr# i# (ctr x#) s# = wr_arr arr# i# x# s#      \
+; setByteArray# arr# i# n# (ctr x#) s#                            \
+    = let { i = fromIntegral (I# i#)                              \
+          ; n = fromIntegral (I# n#)                              \
+          } in                                                    \
+      case unsafeCoerce# (internal (set_arr arr# i n x#)) s# of   \
+        { (# s1#, _ #) -> s1# }                                   \
+                                                                  \
+; indexOffAddr# addr# i# = ctr (idx_addr addr# i#)                \
+; readOffAddr#  addr# i# s# = case rd_addr addr# i# s# of         \
+                        { (# s1#, x# #) -> (# s1#, ctr x# #) }    \
+; writeOffAddr# addr# i# (ctr x#) s# = wr_addr addr# i# x# s#     \
+; setOffAddr# addr# i# n# (ctr x#) s#                             \
+    = let { i = fromIntegral (I# i#)                              \
+          ; n = fromIntegral (I# n#)                              \
+          } in                                                    \
       case unsafeCoerce# (internal (set_addr addr# i n x#)) s# of \
-        { (# s1#, _ #) -> s1# }                                 \
-; {-# INLINE sizeOf# #-}                                        \
-; {-# INLINE alignment# #-}                                     \
-; {-# INLINE indexByteArray# #-}                                \
-; {-# INLINE readByteArray# #-}                                 \
-; {-# INLINE writeByteArray# #-}                                \
-; {-# INLINE setByteArray# #-}                                  \
-; {-# INLINE indexOffAddr# #-}                                  \
-; {-# INLINE readOffAddr# #-}                                   \
-; {-# INLINE writeOffAddr# #-}                                  \
-; {-# INLINE setOffAddr# #-}                                    \
+        { (# s1#, _ #) -> s1# }                                   \
+; {-# INLINE sizeOfType# #-}                                      \
+; {-# INLINE alignmentOfType# #-}                                 \
+; {-# INLINE indexByteArray# #-}                                  \
+; {-# INLINE readByteArray# #-}                                   \
+; {-# INLINE writeByteArray# #-}                                  \
+; {-# INLINE setByteArray# #-}                                    \
+; {-# INLINE indexOffAddr# #-}                                    \
+; {-# INLINE readOffAddr# #-}                                     \
+; {-# INLINE writeOffAddr# #-}                                    \
+; {-# INLINE setOffAddr# #-}                                      \
 }
 
+#if __GLASGOW_HASKELL__ >= 902
+liberate# :: State# s -> State# r
+liberate# = unsafeCoerce#
+shimmedSetWord8Array# :: MutableByteArray# s -> Int -> Int -> Word8# -> IO ()
+shimmedSetWord8Array# m (I# off) (I# len) w = IO (\s -> (# liberate# (GHC.Exts.setByteArray# m off len (GHC.Exts.word2Int# (GHC.Exts.word8ToWord# w)) (liberate# s)), () #))
+shimmedSetInt8Array# :: MutableByteArray# s -> Int -> Int -> Int8# -> IO ()
+shimmedSetInt8Array# m (I# off) (I# len) i = IO (\s -> (# liberate# (GHC.Exts.setByteArray# m off len (GHC.Exts.int8ToInt# i) (liberate# s)), () #))
+#else
+liberate# :: State# s -> State# r
+liberate# = unsafeCoerce#
+shimmedSetWord8Array# :: MutableByteArray# s -> Int -> Int -> Word# -> IO ()
+shimmedSetWord8Array# m (I# off) (I# len) w = IO (\s -> (# liberate# (GHC.Exts.setByteArray# m off len (GHC.Exts.word2Int# w) (liberate# s)), () #))
+shimmedSetInt8Array# :: MutableByteArray# s -> Int -> Int -> Int# -> IO ()
+shimmedSetInt8Array# m (I# off) (I# len) i = IO (\s -> (# liberate# (GHC.Exts.setByteArray# m off len i (liberate# s)), () #))
+#endif
+
 unI# :: Int -> Int#
 unI# (I# n#) = n#
 
@@ -253,7 +349,7 @@
            indexWordArray#, readWordArray#, writeWordArray#, setWordArray#,
            indexWordOffAddr#, readWordOffAddr#, writeWordOffAddr#, setWordOffAddr#)
 derivePrim(Word8, W8#, sIZEOF_WORD8, aLIGNMENT_WORD8,
-           indexWord8Array#, readWord8Array#, writeWord8Array#, setWord8Array#,
+           indexWord8Array#, readWord8Array#, writeWord8Array#, shimmedSetWord8Array#,
            indexWord8OffAddr#, readWord8OffAddr#, writeWord8OffAddr#, setWord8OffAddr#)
 derivePrim(Word16, W16#, sIZEOF_WORD16, aLIGNMENT_WORD16,
            indexWord16Array#, readWord16Array#, writeWord16Array#, setWord16Array#,
@@ -268,7 +364,7 @@
            indexIntArray#, readIntArray#, writeIntArray#, setIntArray#,
            indexIntOffAddr#, readIntOffAddr#, writeIntOffAddr#, setIntOffAddr#)
 derivePrim(Int8, I8#, sIZEOF_INT8, aLIGNMENT_INT8,
-           indexInt8Array#, readInt8Array#, writeInt8Array#, setInt8Array#,
+           indexInt8Array#, readInt8Array#, writeInt8Array#, shimmedSetInt8Array#,
            indexInt8OffAddr#, readInt8OffAddr#, writeInt8OffAddr#, setInt8OffAddr#)
 derivePrim(Int16, I16#, sIZEOF_INT16, aLIGNMENT_INT16,
            indexInt16Array#, readInt16Array#, writeInt16Array#, setInt16Array#,
@@ -288,12 +384,12 @@
 derivePrim(Char, C#, sIZEOF_CHAR, aLIGNMENT_CHAR,
            indexWideCharArray#, readWideCharArray#, writeWideCharArray#, setWideCharArray#,
            indexWideCharOffAddr#, readWideCharOffAddr#, writeWideCharOffAddr#, setWideCharOffAddr#)
-derivePrim(Addr, Addr, sIZEOF_PTR, aLIGNMENT_PTR,
-           indexAddrArray#, readAddrArray#, writeAddrArray#, setAddrArray#,
-           indexAddrOffAddr#, readAddrOffAddr#, writeAddrOffAddr#, setAddrOffAddr#)
 derivePrim(Ptr a, Ptr, sIZEOF_PTR, aLIGNMENT_PTR,
            indexAddrArray#, readAddrArray#, writeAddrArray#, setAddrArray#,
            indexAddrOffAddr#, readAddrOffAddr#, writeAddrOffAddr#, setAddrOffAddr#)
+derivePrim(StablePtr a, StablePtr, sIZEOF_PTR, aLIGNMENT_PTR,
+           indexStablePtrArray#, readStablePtrArray#, writeStablePtrArray#, setStablePtrArray#,
+           indexStablePtrOffAddr#, readStablePtrOffAddr#, writeStablePtrOffAddr#, setStablePtrOffAddr#)
 derivePrim(FunPtr a, FunPtr, sIZEOF_PTR, aLIGNMENT_PTR,
            indexAddrArray#, readAddrArray#, writeAddrArray#, setAddrArray#,
            indexAddrOffAddr#, readAddrOffAddr#, writeAddrOffAddr#, setAddrOffAddr#)
@@ -314,9 +410,7 @@
 deriving instance Prim CSigAtomic
 deriving instance Prim CLLong
 deriving instance Prim CULLong
-#if MIN_VERSION_base(4,10,0)
 deriving instance Prim CBool
-#endif
 deriving instance Prim CIntPtr
 deriving instance Prim CUIntPtr
 deriving instance Prim CIntMax
@@ -393,3 +487,61 @@
 deriving instance Prim CTimer
 #endif
 deriving instance Prim Fd
+
+-- Andrew Martin: The instances for WordPtr and IntPtr are written out by
+-- hand in a tedious way. We cannot use GND because the data constructors for
+-- these types were not available before GHC 8.2. The CPP for generating code
+-- for the Int and Word types does not work here. There is a way to clean this
+-- up a little with CPP, and if anyone wants to do that, go for it. In the
+-- meantime, I am going to ship this with the instances written out by hand.
+
+-- | @since 0.7.1.0
+instance Prim WordPtr where
+  sizeOfType# _ = sizeOfType# (Proxy :: Proxy (Ptr ()))
+  alignmentOfType# _ = alignmentOfType# (Proxy :: Proxy (Ptr ()))
+  indexByteArray# a i = ptrToWordPtr (indexByteArray# a i)
+  readByteArray# a i s0 = case readByteArray# a i s0 of
+    (# s1, p #) -> (# s1, ptrToWordPtr p #)
+  writeByteArray# a i wp = writeByteArray# a i (wordPtrToPtr wp)
+  setByteArray# a i n wp = setByteArray# a i n (wordPtrToPtr wp)
+  indexOffAddr# a i = ptrToWordPtr (indexOffAddr# a i)
+  readOffAddr# a i s0 = case readOffAddr# a i s0 of
+    (# s1, p #) -> (# s1, ptrToWordPtr p #)
+  writeOffAddr# a i wp = writeOffAddr# a i (wordPtrToPtr wp)
+  setOffAddr# a i n wp = setOffAddr# a i n (wordPtrToPtr wp)
+
+-- | @since 0.7.1.0
+instance Prim IntPtr where
+  sizeOfType# _ = sizeOfType# (Proxy :: Proxy (Ptr ()))
+  alignmentOfType# _ = alignmentOfType# (Proxy :: Proxy (Ptr ()))
+  indexByteArray# a i = ptrToIntPtr (indexByteArray# a i)
+  readByteArray# a i s0 = case readByteArray# a i s0 of
+    (# s1, p #) -> (# s1, ptrToIntPtr p #)
+  writeByteArray# a i wp = writeByteArray# a i (intPtrToPtr wp)
+  setByteArray# a i n wp = setByteArray# a i n (intPtrToPtr wp)
+  indexOffAddr# a i = ptrToIntPtr (indexOffAddr# a i)
+  readOffAddr# a i s0 = case readOffAddr# a i s0 of
+    (# s1, p #) -> (# s1, ptrToIntPtr p #)
+  writeOffAddr# a i wp = writeOffAddr# a i (intPtrToPtr wp)
+  setOffAddr# a i n wp = setOffAddr# a i n (intPtrToPtr wp)
+
+-- | @since 0.6.5.0
+deriving instance Prim a => Prim (Const a b)
+-- | @since 0.6.5.0
+deriving instance Prim a => Prim (Down a)
+-- | @since 0.6.5.0
+deriving instance Prim a => Prim (Identity a)
+-- | @since 0.6.5.0
+deriving instance Prim a => Prim (Monoid.Dual a)
+-- | @since 0.6.5.0
+deriving instance Prim a => Prim (Monoid.Sum a)
+-- | @since 0.6.5.0
+deriving instance Prim a => Prim (Monoid.Product a)
+-- | @since 0.6.5.0
+deriving instance Prim a => Prim (Semigroup.First a)
+-- | @since 0.6.5.0
+deriving instance Prim a => Prim (Semigroup.Last a)
+-- | @since 0.6.5.0
+deriving instance Prim a => Prim (Semigroup.Min a)
+-- | @since 0.6.5.0
+deriving instance Prim a => Prim (Semigroup.Max a)
diff --git a/Data/Primitive/UnliftedArray.hs b/Data/Primitive/UnliftedArray.hs
deleted file mode 100644
--- a/Data/Primitive/UnliftedArray.hs
+++ /dev/null
@@ -1,638 +0,0 @@
-{-# Language BangPatterns #-}
-{-# Language CPP #-}
-{-# Language DeriveDataTypeable #-}
-{-# Language MagicHash #-}
-{-# Language RankNTypes #-}
-{-# Language ScopedTypeVariables #-}
-{-# Language TypeFamilies #-}
-{-# Language UnboxedTuples #-}
-
--- |
--- Module      : Data.Primitive.UnliftedArray
--- Copyright   : (c) Dan Doel 2016
--- License     : BSD-style
---
--- Maintainer  : Libraries <libraries@haskell.org>
--- Portability : non-portable
---
--- GHC contains three general classes of value types:
---
---   1. Unboxed types: values are machine values made up of fixed numbers of bytes
---   2. Unlifted types: values are pointers, but strictly evaluated
---   3. Lifted types: values are pointers, lazily evaluated
---
--- The first category can be stored in a 'ByteArray', and this allows types in
--- category 3 that are simple wrappers around category 1 types to be stored
--- more efficiently using a 'ByteArray'. This module provides the same facility
--- for category 2 types.
---
--- GHC has two primitive types, 'ArrayArray#' and 'MutableArrayArray#'. These
--- are arrays of pointers, but of category 2 values, so they are known to not
--- be bottom. This allows types that are wrappers around such types to be stored
--- in an array without an extra level of indirection.
---
--- The way that the 'ArrayArray#' API works is that one can read and write
--- 'ArrayArray#' values to the positions. This works because all category 2
--- types share a uniform representation, unlike unboxed values which are
--- represented by varying (by type) numbers of bytes. However, using the
--- this makes the internal API very unsafe to use, as one has to coerce values
--- to and from 'ArrayArray#'.
---
--- The API presented by this module is more type safe. 'UnliftedArray' and
--- 'MutableUnliftedArray' are parameterized by the type of arrays they contain, and
--- the coercions necessary are abstracted into a class, 'PrimUnlifted', of things
--- that are eligible to be stored.
-
-module Data.Primitive.UnliftedArray
-  ( -- * Types
-    UnliftedArray(..)
-  , MutableUnliftedArray(..)
-  , PrimUnlifted(..)
-    -- * Operations
-  , unsafeNewUnliftedArray
-  , newUnliftedArray
-  , setUnliftedArray
-  , sizeofUnliftedArray
-  , sizeofMutableUnliftedArray
-  , readUnliftedArray
-  , writeUnliftedArray
-  , indexUnliftedArray
-  , indexUnliftedArrayM
-  , unsafeFreezeUnliftedArray
-  , freezeUnliftedArray
-  , thawUnliftedArray
-  , runUnliftedArray
-  , sameMutableUnliftedArray
-  , copyUnliftedArray
-  , copyMutableUnliftedArray
-  , cloneUnliftedArray
-  , cloneMutableUnliftedArray
-    -- * List Conversion
-  , unliftedArrayToList
-  , unliftedArrayFromList
-  , unliftedArrayFromListN
-    -- * Folding
-  , foldrUnliftedArray
-  , foldrUnliftedArray'
-  , foldlUnliftedArray
-  , foldlUnliftedArray'
-    -- * Mapping
-  , mapUnliftedArray
--- Missing operations:
---  , unsafeThawUnliftedArray
-  ) where
-
-import Data.Typeable
-import Control.Applicative
-
-import GHC.Prim
-import GHC.Base (Int(..),build)
-
-import Control.Monad.Primitive
-
-import Control.Monad.ST (runST,ST)
-
-import Data.Monoid (Monoid,mappend)
-import Data.Primitive.Internal.Compat ( isTrue# )
-
-import qualified Data.List as L
-import           Data.Primitive.Array (Array)
-import qualified Data.Primitive.Array as A
-import           Data.Primitive.ByteArray (ByteArray)
-import qualified Data.Primitive.ByteArray as BA
-import qualified Data.Primitive.PrimArray as PA
-import qualified Data.Primitive.SmallArray as SA
-import qualified Data.Primitive.MutVar as MV
-import qualified Data.Monoid
-import qualified GHC.MVar as GM (MVar(..))
-import qualified GHC.Conc as GC (TVar(..))
-import qualified GHC.Stable as GSP (StablePtr(..))
-import qualified GHC.Weak as GW (Weak(..))
-import qualified GHC.Conc.Sync as GCS (ThreadId(..))
-import qualified GHC.Exts as E
-import qualified GHC.ST as GHCST
-
-#if MIN_VERSION_base(4,9,0)
-import Data.Semigroup (Semigroup)
-import qualified Data.Semigroup
-#endif
-
-#if MIN_VERSION_base(4,10,0)
-import GHC.Exts (runRW#)
-#elif MIN_VERSION_base(4,9,0)
-import GHC.Base (runRW#)
-#endif
-
--- | Immutable arrays that efficiently store types that are simple wrappers
--- around unlifted primitive types. The values of the unlifted type are
--- stored directly, eliminating a layer of indirection.
-data UnliftedArray e = UnliftedArray ArrayArray#
-  deriving (Typeable)
-
--- | Mutable arrays that efficiently store types that are simple wrappers
--- around unlifted primitive types. The values of the unlifted type are
--- stored directly, eliminating a layer of indirection.
-data MutableUnliftedArray s e = MutableUnliftedArray (MutableArrayArray# s)
-  deriving (Typeable)
-
--- | Classifies the types that are able to be stored in 'UnliftedArray' and
--- 'MutableUnliftedArray'. These should be types that are just liftings of the
--- unlifted pointer types, so that their internal contents can be safely coerced
--- into an 'ArrayArray#'.
-class PrimUnlifted a where
-  toArrayArray# :: a -> ArrayArray#
-  fromArrayArray# :: ArrayArray# -> a
-
-instance PrimUnlifted (UnliftedArray e) where
-  toArrayArray# (UnliftedArray aa#) = aa#
-  fromArrayArray# aa# = UnliftedArray aa#
-
-instance PrimUnlifted (MutableUnliftedArray s e) where
-  toArrayArray# (MutableUnliftedArray maa#) = unsafeCoerce# maa#
-  fromArrayArray# aa# = MutableUnliftedArray (unsafeCoerce# aa#)
-
-instance PrimUnlifted (Array a) where
-  toArrayArray# (A.Array a#) = unsafeCoerce# a#
-  fromArrayArray# aa# = A.Array (unsafeCoerce# aa#)
-
-instance PrimUnlifted (A.MutableArray s a) where
-  toArrayArray# (A.MutableArray ma#) = unsafeCoerce# ma#
-  fromArrayArray# aa# = A.MutableArray (unsafeCoerce# aa#)
-
-instance PrimUnlifted ByteArray where
-  toArrayArray# (BA.ByteArray ba#) = unsafeCoerce# ba#
-  fromArrayArray# aa# = BA.ByteArray (unsafeCoerce# aa#)
-
-instance PrimUnlifted (BA.MutableByteArray s) where
-  toArrayArray# (BA.MutableByteArray mba#) = unsafeCoerce# mba#
-  fromArrayArray# aa# = BA.MutableByteArray (unsafeCoerce# aa#)
-
--- | @since 0.6.4.0
-instance PrimUnlifted (PA.PrimArray a) where
-  toArrayArray# (PA.PrimArray ba#) = unsafeCoerce# ba#
-  fromArrayArray# aa# = PA.PrimArray (unsafeCoerce# aa#)
-
--- | @since 0.6.4.0
-instance PrimUnlifted (PA.MutablePrimArray s a) where
-  toArrayArray# (PA.MutablePrimArray mba#) = unsafeCoerce# mba#
-  fromArrayArray# aa# = PA.MutablePrimArray (unsafeCoerce# aa#)
-
-instance PrimUnlifted (SA.SmallArray a) where
-  toArrayArray# (SA.SmallArray sa#) = unsafeCoerce# sa#
-  fromArrayArray# aa# = SA.SmallArray (unsafeCoerce# aa#)
-
-instance PrimUnlifted (SA.SmallMutableArray s a) where
-  toArrayArray# (SA.SmallMutableArray sma#) = unsafeCoerce# sma#
-  fromArrayArray# aa# = SA.SmallMutableArray (unsafeCoerce# aa#)
-
-instance PrimUnlifted (MV.MutVar s a) where
-  toArrayArray# (MV.MutVar mv#) = unsafeCoerce# mv#
-  fromArrayArray# aa# = MV.MutVar (unsafeCoerce# aa#)
-
--- | @since 0.6.4.0
-instance PrimUnlifted (GM.MVar a) where
-  toArrayArray# (GM.MVar mv#) = unsafeCoerce# mv#
-  fromArrayArray# mv# = GM.MVar (unsafeCoerce# mv#)
-
--- | @since 0.6.4.0
-instance PrimUnlifted (GC.TVar a) where
-  toArrayArray# (GC.TVar tv#) = unsafeCoerce# tv#
-  fromArrayArray# tv# = GC.TVar (unsafeCoerce# tv#)
-
--- | @since 0.6.4.0
-instance PrimUnlifted (GSP.StablePtr a) where
-  toArrayArray# (GSP.StablePtr tv#) = unsafeCoerce# tv#
-  fromArrayArray# tv# = GSP.StablePtr (unsafeCoerce# tv#)
-
--- | @since 0.6.4.0
-instance PrimUnlifted (GW.Weak a) where
-  toArrayArray# (GW.Weak tv#) = unsafeCoerce# tv#
-  fromArrayArray# tv# = GW.Weak (unsafeCoerce# tv#)
-
--- | @since 0.6.4.0
-instance PrimUnlifted GCS.ThreadId where
-  toArrayArray# (GCS.ThreadId tv#) = unsafeCoerce# tv#
-  fromArrayArray# tv# = GCS.ThreadId (unsafeCoerce# tv#)
-
-die :: String -> String -> a
-die fun problem = error $ "Data.Primitive.UnliftedArray." ++ fun ++ ": " ++ problem
-
--- | Creates a new 'MutableUnliftedArray'. This function is unsafe because it
--- initializes all elements of the array as pointers to the array itself. Attempting
--- to read one of these elements before writing to it is in effect an unsafe
--- coercion from the @MutableUnliftedArray s a@ to the element type.
-unsafeNewUnliftedArray
-  :: (PrimMonad m)
-  => Int -- ^ size
-  -> m (MutableUnliftedArray (PrimState m) a)
-unsafeNewUnliftedArray (I# i#) = primitive $ \s -> case newArrayArray# i# s of
-  (# s', maa# #) -> (# s', MutableUnliftedArray maa# #)
-{-# inline unsafeNewUnliftedArray #-}
-
--- | Sets all the positions in an unlifted array to the designated value.
-setUnliftedArray
-  :: (PrimMonad m, PrimUnlifted a)
-  => MutableUnliftedArray (PrimState m) a -- ^ destination
-  -> a -- ^ value to fill with
-  -> m ()
-setUnliftedArray mua v = loop $ sizeofMutableUnliftedArray mua - 1
- where
- loop i | i < 0     = return ()
-        | otherwise = writeUnliftedArray mua i v >> loop (i-1)
-{-# inline setUnliftedArray #-}
-
--- | Creates a new 'MutableUnliftedArray' with the specified value as initial
--- contents. This is slower than 'unsafeNewUnliftedArray', but safer.
-newUnliftedArray
-  :: (PrimMonad m, PrimUnlifted a)
-  => Int -- ^ size
-  -> a -- ^ initial value
-  -> m (MutableUnliftedArray (PrimState m) a)
-newUnliftedArray len v =
-  unsafeNewUnliftedArray len >>= \mua -> setUnliftedArray mua v >> return mua
-{-# inline newUnliftedArray #-}
-
--- | Yields the length of an 'UnliftedArray'.
-sizeofUnliftedArray :: UnliftedArray e -> Int
-sizeofUnliftedArray (UnliftedArray aa#) = I# (sizeofArrayArray# aa#)
-{-# inline sizeofUnliftedArray #-}
-
--- | Yields the length of a 'MutableUnliftedArray'.
-sizeofMutableUnliftedArray :: MutableUnliftedArray s e -> Int
-sizeofMutableUnliftedArray (MutableUnliftedArray maa#)
-  = I# (sizeofMutableArrayArray# maa#)
-{-# inline sizeofMutableUnliftedArray #-}
-
--- Internal indexing function.
---
--- Note: ArrayArray# is strictly evaluated, so this should have similar
--- consequences to indexArray#, where matching on the unboxed single causes the
--- array access to happen.
-indexUnliftedArrayU
-  :: PrimUnlifted a
-  => UnliftedArray a
-  -> Int
-  -> (# a #)
-indexUnliftedArrayU (UnliftedArray src#) (I# i#)
-  = case indexArrayArrayArray# src# i# of
-      aa# -> (# fromArrayArray# aa# #)
-{-# inline indexUnliftedArrayU #-}
-
--- | Gets the value at the specified position of an 'UnliftedArray'.
-indexUnliftedArray
-  :: PrimUnlifted a
-  => UnliftedArray a -- ^ source
-  -> Int -- ^ index
-  -> a
-indexUnliftedArray ua i
-  = case indexUnliftedArrayU ua i of (# v #) -> v
-{-# inline indexUnliftedArray #-}
-
--- | Gets the value at the specified position of an 'UnliftedArray'.
--- The purpose of the 'Monad' is to allow for being eager in the
--- 'UnliftedArray' value without having to introduce a data dependency
--- directly on the result value.
---
--- It should be noted that this is not as much of a problem as with a normal
--- 'Array', because elements of an 'UnliftedArray' are guaranteed to not
--- be exceptional. This function is provided in case it is more desirable
--- than being strict in the result value.
-indexUnliftedArrayM
-  :: (PrimUnlifted a, Monad m)
-  => UnliftedArray a -- ^ source
-  -> Int -- ^ index
-  -> m a
-indexUnliftedArrayM ua i
-  = case indexUnliftedArrayU ua i of
-      (# v #) -> return v
-{-# inline indexUnliftedArrayM #-}
-
--- | Gets the value at the specified position of a 'MutableUnliftedArray'.
-readUnliftedArray
-  :: (PrimMonad m, PrimUnlifted a)
-  => MutableUnliftedArray (PrimState m) a -- ^ source
-  -> Int -- ^ index
-  -> m a
-readUnliftedArray (MutableUnliftedArray maa#) (I# i#)
-  = primitive $ \s -> case readArrayArrayArray# maa# i# s of
-      (# s', aa# #) -> (# s',  fromArrayArray# aa# #)
-{-# inline readUnliftedArray #-}
-
--- | Sets the value at the specified position of a 'MutableUnliftedArray'.
-writeUnliftedArray
-  :: (PrimMonad m, PrimUnlifted a)
-  => MutableUnliftedArray (PrimState m) a -- ^ destination
-  -> Int -- ^ index
-  -> a -- ^ value
-  -> m ()
-writeUnliftedArray (MutableUnliftedArray maa#) (I# i#) a
-  = primitive_ (writeArrayArrayArray# maa# i# (toArrayArray# a))
-{-# inline writeUnliftedArray #-}
-
--- | Freezes a 'MutableUnliftedArray', yielding an 'UnliftedArray'. This simply
--- marks the array as frozen in place, so it should only be used when no further
--- modifications to the mutable array will be performed.
-unsafeFreezeUnliftedArray
-  :: (PrimMonad m)
-  => MutableUnliftedArray (PrimState m) a
-  -> m (UnliftedArray a)
-unsafeFreezeUnliftedArray (MutableUnliftedArray maa#)
-  = primitive $ \s -> case unsafeFreezeArrayArray# maa# s of
-      (# s', aa# #) -> (# s', UnliftedArray aa# #)
-{-# inline unsafeFreezeUnliftedArray #-}
-
--- | Determines whether two 'MutableUnliftedArray' values are the same. This is
--- object/pointer identity, not based on the contents.
-sameMutableUnliftedArray
-  :: MutableUnliftedArray s a
-  -> MutableUnliftedArray s a
-  -> Bool
-sameMutableUnliftedArray (MutableUnliftedArray maa1#) (MutableUnliftedArray maa2#)
-  = isTrue# (sameMutableArrayArray# maa1# maa2#)
-{-# inline sameMutableUnliftedArray #-}
-
--- | Copies the contents of an immutable array into a mutable array.
-copyUnliftedArray
-  :: (PrimMonad m)
-  => MutableUnliftedArray (PrimState m) a -- ^ destination
-  -> Int -- ^ offset into destination
-  -> UnliftedArray a -- ^ source
-  -> Int -- ^ offset into source
-  -> Int -- ^ number of elements to copy
-  -> m ()
-copyUnliftedArray
-  (MutableUnliftedArray dst) (I# doff)
-  (UnliftedArray src) (I# soff) (I# ln) =
-    primitive_ $ copyArrayArray# src soff dst doff ln
-{-# inline copyUnliftedArray #-}
-
--- | Copies the contents of one mutable array into another.
-copyMutableUnliftedArray
-  :: (PrimMonad m)
-  => MutableUnliftedArray (PrimState m) a -- ^ destination
-  -> Int -- ^ offset into destination
-  -> MutableUnliftedArray (PrimState m) a -- ^ source
-  -> Int -- ^ offset into source
-  -> Int -- ^ number of elements to copy
-  -> m ()
-copyMutableUnliftedArray
-  (MutableUnliftedArray dst) (I# doff)
-  (MutableUnliftedArray src) (I# soff) (I# ln) =
-    primitive_ $ copyMutableArrayArray# src soff dst doff ln
-{-# inline copyMutableUnliftedArray #-}
-
--- | Freezes a portion of a 'MutableUnliftedArray', yielding an 'UnliftedArray'.
--- This operation is safe, in that it copies the frozen portion, and the
--- existing mutable array may still be used afterward.
-freezeUnliftedArray
-  :: (PrimMonad m)
-  => MutableUnliftedArray (PrimState m) a -- ^ source
-  -> Int -- ^ offset
-  -> Int -- ^ length
-  -> m (UnliftedArray a)
-freezeUnliftedArray src off len = do
-  dst <- unsafeNewUnliftedArray len
-  copyMutableUnliftedArray dst 0 src off len
-  unsafeFreezeUnliftedArray dst
-{-# inline freezeUnliftedArray #-}
-
--- | Thaws a portion of an 'UnliftedArray', yielding a 'MutableUnliftedArray'.
--- This copies the thawed portion, so mutations will not affect the original
--- array.
-thawUnliftedArray
-  :: (PrimMonad m)
-  => UnliftedArray a -- ^ source
-  -> Int -- ^ offset
-  -> Int -- ^ length
-  -> m (MutableUnliftedArray (PrimState m) a)
-thawUnliftedArray src off len = do
-  dst <- unsafeNewUnliftedArray len
-  copyUnliftedArray dst 0 src off len
-  return dst
-{-# inline thawUnliftedArray #-}
-
-#if !MIN_VERSION_base(4,9,0)
-unsafeCreateUnliftedArray
-  :: Int
-  -> (forall s. MutableUnliftedArray s a -> ST s ())
-  -> UnliftedArray a
-unsafeCreateUnliftedArray 0 _ = emptyUnliftedArray
-unsafeCreateUnliftedArray n f = runUnliftedArray $ do
-  mary <- unsafeNewUnliftedArray n
-  f mary
-  pure mary
-
--- | Execute a stateful computation and freeze the resulting array.
-runUnliftedArray
-  :: (forall s. ST s (MutableUnliftedArray s a))
-  -> UnliftedArray a
-runUnliftedArray m = runST $ m >>= unsafeFreezeUnliftedArray
-
-#else /* Below, runRW# is available. */
-
--- This low-level business is designed to work with GHC's worker-wrapper
--- transformation. A lot of the time, we don't actually need an Array
--- constructor. By putting it on the outside, and being careful about
--- how we special-case the empty array, we can make GHC smarter about this.
--- The only downside is that separately created 0-length arrays won't share
--- their Array constructors, although they'll share their underlying
--- Array#s.
-unsafeCreateUnliftedArray
-  :: Int
-  -> (forall s. MutableUnliftedArray s a -> ST s ())
-  -> UnliftedArray a
-unsafeCreateUnliftedArray 0 _ = UnliftedArray (emptyArrayArray# (# #))
-unsafeCreateUnliftedArray n f = runUnliftedArray $ do
-  mary <- unsafeNewUnliftedArray n
-  f mary
-  pure mary
-
--- | Execute a stateful computation and freeze the resulting array.
-runUnliftedArray
-  :: (forall s. ST s (MutableUnliftedArray s a))
-  -> UnliftedArray a
-runUnliftedArray m = UnliftedArray (runUnliftedArray# m)
-
-runUnliftedArray#
-  :: (forall s. ST s (MutableUnliftedArray s a))
-  -> ArrayArray#
-runUnliftedArray# m = case runRW# $ \s ->
-  case unST m s of { (# s', MutableUnliftedArray mary# #) ->
-  unsafeFreezeArrayArray# mary# s'} of (# _, ary# #) -> ary#
-
-unST :: ST s a -> State# s -> (# State# s, a #)
-unST (GHCST.ST f) = f
-
-emptyArrayArray# :: (# #) -> ArrayArray#
-emptyArrayArray# _ = case emptyUnliftedArray of UnliftedArray ar -> ar
-{-# NOINLINE emptyArrayArray# #-}
-#endif
-
--- | Creates a copy of a portion of an 'UnliftedArray'
-cloneUnliftedArray
-  :: UnliftedArray a -- ^ source
-  -> Int -- ^ offset
-  -> Int -- ^ length
-  -> UnliftedArray a
-cloneUnliftedArray src off len =
-  runUnliftedArray (thawUnliftedArray src off len)
-{-# inline cloneUnliftedArray #-}
-
--- | Creates a new 'MutableUnliftedArray' containing a copy of a portion of
--- another mutable array.
-cloneMutableUnliftedArray
-  :: (PrimMonad m)
-  => MutableUnliftedArray (PrimState m) a -- ^ source
-  -> Int -- ^ offset
-  -> Int -- ^ length
-  -> m (MutableUnliftedArray (PrimState m) a)
-cloneMutableUnliftedArray src off len = do
-  dst <- unsafeNewUnliftedArray len
-  copyMutableUnliftedArray dst 0 src off len
-  return dst
-{-# inline cloneMutableUnliftedArray #-}
-
-instance Eq (MutableUnliftedArray s a) where
-  (==) = sameMutableUnliftedArray
-
-instance (Eq a, PrimUnlifted a) => Eq (UnliftedArray a) where
-  aa1 == aa2 = sizeofUnliftedArray aa1 == sizeofUnliftedArray aa2
-            && loop (sizeofUnliftedArray aa1 - 1)
-   where
-   loop i
-     | i < 0 = True
-     | otherwise = indexUnliftedArray aa1 i == indexUnliftedArray aa2 i && loop (i-1)
-
--- | Lexicographic ordering. Subject to change between major versions.
---
---   @since 0.6.4.0
-instance (Ord a, PrimUnlifted a) => Ord (UnliftedArray a) where
-  compare a1 a2 = loop 0
-    where
-    mn = sizeofUnliftedArray a1 `min` sizeofUnliftedArray a2
-    loop i
-      | i < mn
-      , x1 <- indexUnliftedArray a1 i
-      , x2 <- indexUnliftedArray a2 i
-      = compare x1 x2 `mappend` loop (i+1)
-      | otherwise = compare (sizeofUnliftedArray a1) (sizeofUnliftedArray a2)
-
--- | @since 0.6.4.0
-instance (Show a, PrimUnlifted a) => Show (UnliftedArray a) where
-  showsPrec p a = showParen (p > 10) $
-    showString "fromListN " . shows (sizeofUnliftedArray a) . showString " "
-      . shows (unliftedArrayToList a)
-
-#if MIN_VERSION_base(4,9,0)
--- | @since 0.6.4.0
-instance PrimUnlifted a => Semigroup (UnliftedArray a) where
-  (<>) = concatUnliftedArray
-#endif
-
--- | @since 0.6.4.0
-instance PrimUnlifted a => Monoid (UnliftedArray a) where
-  mempty = emptyUnliftedArray
-#if !(MIN_VERSION_base(4,11,0))
-  mappend = concatUnliftedArray
-#endif
-
-emptyUnliftedArray :: UnliftedArray a
-emptyUnliftedArray = runUnliftedArray (unsafeNewUnliftedArray 0)
-{-# NOINLINE emptyUnliftedArray #-}
-
-concatUnliftedArray :: UnliftedArray a -> UnliftedArray a -> UnliftedArray a
-concatUnliftedArray x y = unsafeCreateUnliftedArray (sizeofUnliftedArray x + sizeofUnliftedArray y) $ \m -> do
-  copyUnliftedArray m 0 x 0 (sizeofUnliftedArray x)
-  copyUnliftedArray m (sizeofUnliftedArray x) y 0 (sizeofUnliftedArray y)
-
--- | Lazy right-associated fold over the elements of an 'UnliftedArray'.
-{-# INLINE foldrUnliftedArray #-}
-foldrUnliftedArray :: forall a b. PrimUnlifted a => (a -> b -> b) -> b -> UnliftedArray a -> b
-foldrUnliftedArray f z arr = go 0
-  where
-    !sz = sizeofUnliftedArray arr
-    go !i
-      | sz > i = f (indexUnliftedArray arr i) (go (i+1))
-      | otherwise = z
-
--- | Strict right-associated fold over the elements of an 'UnliftedArray.
-{-# INLINE foldrUnliftedArray' #-}
-foldrUnliftedArray' :: forall a b. PrimUnlifted a => (a -> b -> b) -> b -> UnliftedArray a -> b
-foldrUnliftedArray' f z0 arr = go (sizeofUnliftedArray arr - 1) z0
-  where
-    go !i !acc
-      | i < 0 = acc
-      | otherwise = go (i - 1) (f (indexUnliftedArray arr i) acc)
-
--- | Lazy left-associated fold over the elements of an 'UnliftedArray'.
-{-# INLINE foldlUnliftedArray #-}
-foldlUnliftedArray :: forall a b. PrimUnlifted a => (b -> a -> b) -> b -> UnliftedArray a -> b
-foldlUnliftedArray f z arr = go (sizeofUnliftedArray arr - 1)
-  where
-    go !i
-      | i < 0 = z
-      | otherwise = f (go (i - 1)) (indexUnliftedArray arr i)
-
--- | Strict left-associated fold over the elements of an 'UnliftedArray'.
-{-# INLINE foldlUnliftedArray' #-}
-foldlUnliftedArray' :: forall a b. PrimUnlifted a => (b -> a -> b) -> b -> UnliftedArray a -> b
-foldlUnliftedArray' f z0 arr = go 0 z0
-  where
-    !sz = sizeofUnliftedArray arr
-    go !i !acc
-      | i < sz = go (i + 1) (f acc (indexUnliftedArray arr i))
-      | otherwise = acc
-
--- | Map over the elements of an 'UnliftedArray'.
-{-# INLINE mapUnliftedArray #-}
-mapUnliftedArray :: (PrimUnlifted a, PrimUnlifted b)
-  => (a -> b)
-  -> UnliftedArray a
-  -> UnliftedArray b
-mapUnliftedArray f arr = unsafeCreateUnliftedArray sz $ \marr -> do
-  let go !ix = if ix < sz
-        then do
-          let b = f (indexUnliftedArray arr ix)
-          writeUnliftedArray marr ix b
-          go (ix + 1)
-        else return ()
-  go 0
-  where
-  !sz = sizeofUnliftedArray arr
-
--- | Convert the unlifted array to a list.
-{-# INLINE unliftedArrayToList #-}
-unliftedArrayToList :: PrimUnlifted a => UnliftedArray a -> [a]
-unliftedArrayToList xs = build (\c n -> foldrUnliftedArray c n xs)
-
-unliftedArrayFromList :: PrimUnlifted a => [a] -> UnliftedArray a
-unliftedArrayFromList xs = unliftedArrayFromListN (L.length xs) xs
-
-unliftedArrayFromListN :: forall a. PrimUnlifted a => Int -> [a] -> UnliftedArray a
-unliftedArrayFromListN len vs = unsafeCreateUnliftedArray len run where
-  run :: forall s. MutableUnliftedArray s a -> ST s ()
-  run arr = do
-    let go :: [a] -> Int -> ST s ()
-        go [] !ix = if ix == len
-          -- The size check is mandatory since failure to initialize all elements
-          -- introduces the possibility of a segfault happening when someone attempts
-          -- to read the unitialized element. See the docs for unsafeNewUnliftedArray.
-          then return ()
-          else die "unliftedArrayFromListN" "list length less than specified size"
-        go (a : as) !ix = if ix < len
-          then do
-            writeUnliftedArray arr ix a
-            go as (ix + 1)
-          else die "unliftedArrayFromListN" "list length greater than specified size"
-    go vs 0
-
-
-#if MIN_VERSION_base(4,7,0)
--- | @since 0.6.4.0
-instance PrimUnlifted a => E.IsList (UnliftedArray a) where
-  type Item (UnliftedArray a) = a
-  fromList = unliftedArrayFromList
-  fromListN = unliftedArrayFromListN
-  toList = unliftedArrayToList
-#endif
-
diff --git a/Setup.hs b/Setup.hs
deleted file mode 100644
--- a/Setup.hs
+++ /dev/null
@@ -1,3 +0,0 @@
-import Distribution.Simple
-main = defaultMain
-
diff --git a/bench/Array/Traverse/Closure.hs b/bench/Array/Traverse/Closure.hs
new file mode 100644
--- /dev/null
+++ b/bench/Array/Traverse/Closure.hs
@@ -0,0 +1,49 @@
+{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE UnboxedTuples #-}
+{-# LANGUAGE RankNTypes #-}
+{-# LANGUAGE MagicHash #-}
+
+module Array.Traverse.Closure
+  ( traversePoly
+  ) where
+
+import Control.Applicative
+import Control.Monad.ST
+import Data.Primitive.Array
+import GHC.Exts (Int(..),MutableArray#)
+
+{-# INLINE traversePoly #-}
+traversePoly
+  :: Applicative f
+  => (a -> f b)
+  -> Array a
+  -> f (Array b)
+traversePoly f = \ !ary ->
+  let
+    !len = sizeofArray ary
+    go !i
+      | i == len = pure $ STA $ \mary -> unsafeFreezeArray (MutableArray mary)
+      | (# x #) <- indexArray## ary i
+      = liftA2 (\b (STA m) -> STA $ \mary ->
+                  writeArray (MutableArray mary) i b >> m mary)
+               (f x) (go (i + 1))
+  in if len == 0
+     then pure mempty
+     else runSTA len <$> go 0
+
+badTraverseValue :: a
+badTraverseValue = die "traversePoly" "bad indexing"
+{-# NOINLINE badTraverseValue #-}
+
+die :: String -> String -> a
+die fun problem = error $ "Array.Traverse.Closure" ++ fun ++ ": " ++ problem
+
+newtype STA a = STA {_runSTA :: forall s. MutableArray# s a -> ST s (Array a)}
+
+runSTA :: Int -> STA a -> Array a
+runSTA !sz = \ (STA m) -> runST $ newArray_ sz >>= \ ar -> m (marray# ar)
+{-# INLINE runSTA #-}
+
+newArray_ :: Int -> ST s (MutableArray s a)
+newArray_ !n = newArray n badTraverseValue
+
diff --git a/bench/Array/Traverse/Unsafe.hs b/bench/Array/Traverse/Unsafe.hs
new file mode 100644
--- /dev/null
+++ b/bench/Array/Traverse/Unsafe.hs
@@ -0,0 +1,48 @@
+{-# LANGUAGE BangPatterns #-}
+
+module Array.Traverse.Unsafe
+  ( traversePoly
+  , traverseMono
+  ) where
+
+import Control.Monad.ST
+import Control.Monad.Trans.State.Strict
+import Control.Monad.Primitive
+import Data.Primitive.Array
+
+{-# INLINE traversePoly #-}
+traversePoly
+  :: PrimMonad m
+  => (a -> m b)
+  -> Array a
+  -> m (Array b)
+traversePoly f = \ !ary ->
+  let
+    !sz = sizeofArray ary
+    go !i !mary
+      | i == sz
+      = unsafeFreezeArray mary
+      | otherwise
+      = do
+          a <- indexArrayM ary i
+          b <- f a
+          writeArray mary i b
+          go (i + 1) mary
+  in do
+    mary <- newArray sz badTraverseValue
+    go 0 mary
+
+badTraverseValue :: a
+badTraverseValue = die "traversePoly" "bad indexing"
+{-# NOINLINE badTraverseValue #-}
+
+die :: String -> String -> a
+die fun problem = error $ "Array.Traverse.Unsafe" ++ fun ++ ": " ++ problem
+
+-- Included to make it easy to inspect GHC Core that results
+-- from inlining traversePoly.
+traverseMono :: 
+     (Int -> StateT Word (ST s) Int)
+  -> Array Int
+  -> StateT Word (ST s) (Array Int)
+traverseMono f x = traversePoly f x
diff --git a/bench/ByteArray/Compare.hs b/bench/ByteArray/Compare.hs
new file mode 100644
--- /dev/null
+++ b/bench/ByteArray/Compare.hs
@@ -0,0 +1,96 @@
+{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+
+module ByteArray.Compare
+  ( benchmark
+  , argumentSmall
+  , argumentMedium
+  , argumentLarge
+  ) where
+
+import Data.Primitive
+import Data.Word
+import Control.Monad
+import Control.Monad.ST (runST)
+import GHC.Exts (fromList)
+
+-- This takes the cross product of the argument with itself
+-- and compares each pair of combined ByteArrays. In other words,
+-- it compare every ByteArray to every other ByteArray (including
+-- itself). This is does efficiently and should not allocate
+-- any memory.
+benchmark :: Array ByteArray -> Int
+benchmark !uarr = outer 0
+  where
+  sz = sizeofArray uarr
+  outer :: Int -> Int
+  outer !v0 =
+    let go !v !ix = if ix < sz
+          then go (inner v (indexArray uarr ix)) (ix + 1)
+          else v
+     in go v0 0
+  inner :: Int -> ByteArray -> Int
+  inner !v0 !barr =
+    let go !v !ix = if ix < sz
+          then
+            let !y = case compare barr (indexArray uarr ix) of
+                  LT -> (-1)
+                  EQ -> 0
+                  GT -> 1
+             in go (v + y) (ix + 1)
+          else v
+     in go v0 0
+
+-- This is an array of all byte arrays consistent of the bytes 0 and 1
+-- bewteen length 0 and 7 inclusive:
+--
+-- []
+-- [0]
+-- [1]
+-- [0,0]
+-- [0,1]
+-- ...
+-- [1,1,1,1,1,1,0]
+-- [1,1,1,1,1,1,1]
+--
+-- These are very small byte arrays. All of them are smaller than a
+-- cache line. A comparison function that uses the FFI may perform
+-- worse on such inputs than one that does not.
+argumentSmall :: Array ByteArray
+argumentSmall = runST $ do
+  let (ys :: [[Word8]]) = foldMap (\n -> replicateM n [0,1]) (enumFromTo 0 7)
+  marr <- newArray (length ys) undefined
+  let go !_ [] = return ()
+      go !ix (x : xs) = do
+        writeArray marr ix (fromList x)
+        go (ix + 1) xs
+  go 0 ys
+  unsafeFreezeArray marr
+
+
+-- This is an array of all byte arrays consistent of the bytes 0 and 1
+-- bewteen length 0 and 7 inclusive. However, they are all padded on the
+-- left by the same 256 bytes. Comparing any two of them will require
+-- walking and comparing the first 256 bytes.
+argumentMedium :: Array ByteArray
+argumentMedium  = runST $ do
+  let (ys :: [[Word8]]) = foldMap (\n -> map (enumFromTo 0 255 ++) (replicateM n [0,1])) (enumFromTo 0 7)
+  marr <- newArray (length ys) undefined
+  let go !_ [] = return ()
+      go !ix (x : xs) = do
+        writeArray marr ix (fromList x)
+        go (ix + 1) xs
+  go 0 ys
+  unsafeFreezeArray marr
+
+-- Same thing but with left padding of 1024 bytes.
+argumentLarge :: Array ByteArray
+argumentLarge  = runST $ do
+  let (ys :: [[Word8]]) = foldMap (\n -> map (concat (replicate 4 (enumFromTo 0 255)) ++) (replicateM n [0,1])) (enumFromTo 0 7)
+  marr <- newArray (length ys) undefined
+  let go !_ [] = return ()
+      go !ix (x : xs) = do
+        writeArray marr ix (fromList x)
+        go (ix + 1) xs
+  go 0 ys
+  unsafeFreezeArray marr
diff --git a/bench/PrimArray/Compare.hs b/bench/PrimArray/Compare.hs
new file mode 100644
--- /dev/null
+++ b/bench/PrimArray/Compare.hs
@@ -0,0 +1,58 @@
+{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+
+module PrimArray.Compare
+  ( benchmarkLt
+  , benchmarkLtDef
+  , benchmarkLte
+  , benchmarkLteDef
+  , argumentA
+  , argumentB
+  ) where
+
+import Data.Primitive
+import Data.Word
+import Control.Monad
+import Control.Monad.ST (runST)
+import GHC.Exts (fromList)
+
+benchmarkLtDef :: PrimArray Int -> PrimArray Int -> Bool
+benchmarkLtDef a b = case compare a b of
+  LT -> True
+  _ -> False
+
+benchmarkLteDef :: PrimArray Int -> PrimArray Int -> Bool
+benchmarkLteDef a b = case compare a b of
+  GT -> False
+  _ -> True
+
+benchmarkLt :: PrimArray Int -> PrimArray Int -> Bool
+benchmarkLt a b =
+  let !sz1 = sizeofPrimArray a
+      !sz2 = sizeofPrimArray b
+      !sz = min sz1 sz2
+      loop !i
+        | i < sz = if indexPrimArray a i < indexPrimArray b i
+            then True
+            else loop (i + 1)
+        | otherwise = sz1 < sz2
+   in loop 0
+
+benchmarkLte :: PrimArray Int -> PrimArray Int -> Bool
+benchmarkLte a b =
+  let !sz1 = sizeofPrimArray a
+      !sz2 = sizeofPrimArray b
+      !sz = min sz1 sz2
+      loop !i
+        | i < sz = if indexPrimArray a i <= indexPrimArray b i
+            then loop (i + 1)
+            else False
+        | otherwise = sz1 < sz2
+   in loop 0
+
+argumentA :: PrimArray Int
+argumentA = fromList (enumFromTo 0 8000 ++ [55])
+
+argumentB :: PrimArray Int
+argumentB = fromList (enumFromTo 0 8000 ++ [56])
+
diff --git a/bench/PrimArray/Traverse.hs b/bench/PrimArray/Traverse.hs
new file mode 100644
--- /dev/null
+++ b/bench/PrimArray/Traverse.hs
@@ -0,0 +1,23 @@
+module PrimArray.Traverse
+  ( benchmarkApplicative
+  , benchmarkPrimMonad
+  , argument
+  ) where
+
+import Control.Monad.ST (runST)
+import Control.Monad.Trans.Maybe (MaybeT(..))
+import Data.Bool (bool)
+import Data.Primitive.PrimArray
+import GHC.Exts (fromList)
+
+benchmarkApplicative :: PrimArray Int -> Maybe (PrimArray Int)
+benchmarkApplicative xs = traversePrimArray (\x -> bool Nothing (Just (x + 1)) (x > 0)) xs
+
+benchmarkPrimMonad :: PrimArray Int -> Maybe (PrimArray Int)
+benchmarkPrimMonad xs = runST $ runMaybeT $ traversePrimArrayP
+  (\x -> bool (MaybeT (return Nothing)) (MaybeT (return (Just (x + 1)))) (x > 0))
+  xs
+
+argument :: PrimArray Int
+argument = fromList (enumFromTo 1 10000)
+
diff --git a/bench/main.hs b/bench/main.hs
new file mode 100644
--- /dev/null
+++ b/bench/main.hs
@@ -0,0 +1,85 @@
+{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE KindSignatures #-}
+{-# LANGUAGE MagicHash #-}
+{-# LANGUAGE UnboxedTuples #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+
+import Test.Tasty.Bench
+import Control.Monad.ST
+import Data.Primitive
+import Control.Monad.Trans.State.Strict
+import Data.Set (Set)
+
+-- These are fixed implementations of certain operations. In the event
+-- that primitive changes its implementation of a function, these
+-- implementations stay the same. They are helpful for ensuring that
+-- something that is a performance win in one version of GHC doesn't
+-- become a regression later. They are also helpful for evaluating
+-- how well different implementation hold up in different scenarios.
+import qualified Array.Traverse.Unsafe
+import qualified Array.Traverse.Closure
+
+-- These are particular scenarios that are tested against the
+-- implementations actually used by primitive.
+import qualified ByteArray.Compare
+import qualified PrimArray.Compare
+import qualified PrimArray.Traverse
+
+import qualified Data.Set as Set
+
+main :: IO ()
+main = defaultMain
+  [ bgroup "Array"
+    [ bgroup "implementations"
+      [ bgroup "traverse"
+        [ bench "closure" (nf (\x -> runST (runStateT (Array.Traverse.Closure.traversePoly cheap x) 0)) numbers)
+        , bench "unsafe" (nf (\x -> runST (runStateT (Array.Traverse.Unsafe.traversePoly cheap x) 0)) numbers)
+        ]
+      ]
+    , bgroup "arrayFromListN"
+      [ bench "set-to-list-to-array" (whnf arrayFromSet setOfIntegers1024)
+      ]
+    ]
+  , bgroup "ByteArray"
+    [ bgroup "compare"
+      [ bench "small" (whnf ByteArray.Compare.benchmark ByteArray.Compare.argumentSmall)
+      , bench "medium" (whnf ByteArray.Compare.benchmark ByteArray.Compare.argumentMedium)
+      , bench "large" (whnf ByteArray.Compare.benchmark ByteArray.Compare.argumentLarge)
+      ]
+    ]
+  , bgroup "PrimArray"
+    [ bgroup "traverse"
+      [ bgroup "Maybe"
+        [ bench "Applicative" (whnf PrimArray.Traverse.benchmarkApplicative PrimArray.Traverse.argument)
+        , bench "PrimMonad" (whnf PrimArray.Traverse.benchmarkPrimMonad PrimArray.Traverse.argument)
+        ]
+      ]
+    , bgroup "implementations"
+      [ bgroup "less-than"
+        [ bench "default" (whnf (PrimArray.Compare.benchmarkLtDef PrimArray.Compare.argumentA) PrimArray.Compare.argumentB)
+        , bench "override" (whnf (PrimArray.Compare.benchmarkLt PrimArray.Compare.argumentA) PrimArray.Compare.argumentB)
+        ]
+      , bgroup "less-than-equal"
+        [ bench "default" (whnf (PrimArray.Compare.benchmarkLteDef PrimArray.Compare.argumentA) PrimArray.Compare.argumentB)
+        , bench "override" (whnf (PrimArray.Compare.benchmarkLte PrimArray.Compare.argumentA) PrimArray.Compare.argumentB)
+        ]
+      ]
+    ]
+  ]
+
+setOfIntegers1024 :: Set Integer
+{-# noinline setOfIntegers1024 #-}
+setOfIntegers1024 = Set.fromList [1..1024]
+
+-- The performance of this is used to confirm whether or not arrayFromListN is
+-- actining as a good consumer for list fusion.
+arrayFromSet :: Set Integer -> Array Integer
+{-# noinline arrayFromSet #-}
+arrayFromSet s = arrayFromListN (Set.size s) (Set.toList s)
+
+cheap :: Int -> StateT Int (ST s) Int
+cheap i = modify (\x -> x + i) >> return (i * i)
+
+numbers :: Array Int
+numbers = arrayFromList (enumFromTo 0 10000)
diff --git a/cbits/primitive-memops.c b/cbits/primitive-memops.c
--- a/cbits/primitive-memops.c
+++ b/cbits/primitive-memops.c
@@ -40,14 +40,19 @@
   return memcmp( s1, s2, n );
 }
 
-void hsprimitive_memset_Word8 (HsWord8 *p, ptrdiff_t off, size_t n, HsWord x)
+int hsprimitive_memcmp_offset( HsWord8 *s1, HsInt off1, HsWord8 *s2, HsInt off2, size_t n )
 {
+  return memcmp( s1 + off1, s2 + off2, n );
+}
+
+void hsprimitive_memset_Word8 (HsWord8 *p, ptrdiff_t off, size_t n, HsWord8 x)
+{
   memset( (char *)(p+off), x, n );
 }
 
 /* MEMSET(HsWord8, HsWord) */
-MEMSET(Word16, HsWord)
-MEMSET(Word32, HsWord)
+MEMSET(Word16, HsWord16)
+MEMSET(Word32, HsWord32)
 MEMSET(Word64, HsWord64)
 MEMSET(Word, HsWord)
 MEMSET(Ptr, HsPtr)
diff --git a/cbits/primitive-memops.h b/cbits/primitive-memops.h
--- a/cbits/primitive-memops.h
+++ b/cbits/primitive-memops.h
@@ -1,23 +1,25 @@
 #ifndef haskell_primitive_memops_h
 #define haskell_primitive_memops_h
 
+// N.B. GHC RTS headers want to come first, lest things break on Windows.
+#include <HsFFI.h>
+
 #include <stdlib.h>
 #include <stddef.h>
-#include <HsFFI.h>
 
-void hsprimitive_memcpy( void *dst, ptrdiff_t doff, void *src, ptrdiff_t soff, size_t len );
-void hsprimitive_memmove( void *dst, ptrdiff_t doff, void *src, ptrdiff_t soff, size_t len );
-int  hsprimitive_memcmp( HsWord8 *s1, HsWord8 *s2, size_t n );
+void hsprimitive_memcpy(void *dst, ptrdiff_t doff, void *src, ptrdiff_t soff, size_t len);
+void hsprimitive_memmove(void *dst, ptrdiff_t doff, void *src, ptrdiff_t soff, size_t len);
+int  hsprimitive_memcmp(HsWord8 *s1, HsWord8 *s2, size_t n);
+int  hsprimitive_memcmp_offset(HsWord8 *s1, HsInt off1, HsWord8 *s2, HsInt off2, size_t n);
 
-void hsprimitive_memset_Word8 (HsWord8 *, ptrdiff_t, size_t, HsWord);
-void hsprimitive_memset_Word16 (HsWord16 *, ptrdiff_t, size_t, HsWord);
-void hsprimitive_memset_Word32 (HsWord32 *, ptrdiff_t, size_t, HsWord);
-void hsprimitive_memset_Word64 (HsWord64 *, ptrdiff_t, size_t, HsWord64);
-void hsprimitive_memset_Word (HsWord *, ptrdiff_t, size_t, HsWord);
-void hsprimitive_memset_Ptr (HsPtr *, ptrdiff_t, size_t, HsPtr);
-void hsprimitive_memset_Float (HsFloat *, ptrdiff_t, size_t, HsFloat);
-void hsprimitive_memset_Double (HsDouble *, ptrdiff_t, size_t, HsDouble);
-void hsprimitive_memset_Char (HsChar *, ptrdiff_t, size_t, HsChar);
+void hsprimitive_memset_Word8(HsWord8 *, ptrdiff_t, size_t, HsWord8);
+void hsprimitive_memset_Word16(HsWord16 *, ptrdiff_t, size_t, HsWord16);
+void hsprimitive_memset_Word32(HsWord32 *, ptrdiff_t, size_t, HsWord32);
+void hsprimitive_memset_Word64(HsWord64 *, ptrdiff_t, size_t, HsWord64);
+void hsprimitive_memset_Word(HsWord *, ptrdiff_t, size_t, HsWord);
+void hsprimitive_memset_Ptr(HsPtr *, ptrdiff_t, size_t, HsPtr);
+void hsprimitive_memset_Float(HsFloat *, ptrdiff_t, size_t, HsFloat);
+void hsprimitive_memset_Double(HsDouble *, ptrdiff_t, size_t, HsDouble);
+void hsprimitive_memset_Char(HsChar *, ptrdiff_t, size_t, HsChar);
 
 #endif
-
diff --git a/changelog.md b/changelog.md
--- a/changelog.md
+++ b/changelog.md
@@ -1,3 +1,186 @@
+## Changes in version 0.9.1.0
+
+  * Make fromListN functions good consumers for list fusion.
+
+  * Add functions to improve `MutVar`'s interoperability with `IORef` and `STRef`.
+
+  * Add `createPrimArray` and `createByteArray`.
+
+  * Add `byteArrayAsForeignPtr` and `mutableByteArrayAsForeignPtr`.
+
+  * Use `copyMutableByteArrayNonOverlapping#` in the implementation of `copyMutableByteArray`
+    on sufficiently new GHCs. This does not change the contract for `copyMutableByteArray`.
+    This function has always been documented as having undefined behavior when the slices
+    overlap. However, overlaps previously were handled gracefully (with the semantics
+    of C's `memmove`). Going forward, users who do not uphold `copyMutableByteArray`'s
+    precondition will be met with unpredictable results.
+
+  * Drop support for GHC 8.0.
+
+## Changes in version 0.9.0.0
+
+  * Add `withByteArrayContents`, `withMutableByteArrayContents`,
+    `withPrimArrayContents`, `withMutablePrimArrayContents`.
+
+  * Fix signature of `keepAlive`.
+
+  * Remove re-export of `fromList` and `fromListN` from `Data.Primitive.Array`.
+
+  * Use `mutableByteArrayContents#` in GHC 9.2+
+
+  * Add `Prim` instance for `Complex`.
+
+  * Add `getSizeofSmallMutableArray` that wraps `getSizeofSmallMutableArray#`
+    from `GHC.Exts`.
+
+  * Add default definitions for the `setByteArray#` and `setOffAddr#` methods,
+    so they don't need to be defined explicitly anymore.
+
+  * Add standalone `sizeOfType`/`alignmentOfType` (recommended over `sizeOf`/`alignment`)
+    and `Prim` class methods `sizeOfType#`/`alignmentOfType#` (recommended over `sizeOf#`/`alignment#`)
+
+  * Change `Show` instances of `PrimArray`, `Array`, and `SmallArray`. These
+    previously used the `fromListN n [...]` form, but they now used the more
+    terse `[...]` form.
+
+  * Correct the `Read` instances of `Array` and `SmallArray`. These instances
+    are supposed to be able to handle all three of these forms: `fromList [...]`,
+    `fromListN n [...]`, and `[...]`. They had been rejected the last form, but
+    this mistake was discovered by the test suite when the Show instances were
+    changed.
+
+## Changes in version 0.8.0.0
+
+  * Add `resizeSmallMutableArray` that wraps `resizeSmallMutableArray#` from
+    `GHC.Exts`.
+
+  * New module `Data.Primitive.PrimVar`. This is essentially `PrimArray` with
+    element length 1. For types with `Prim` instances, this is a drop-in
+    replacement for `MutVar` with fewer indirections.
+
+  * `PrimArray`'s type argument has been given a nominal role instead of a phantom role.
+    This is a breaking change.
+
+  * Add `readCharArray`, `writeCharArray`, `indexCharArray` for operating on
+    8-bit characters in a byte array.
+
+  * When building with `base-4.17` and newer, re-export the `ByteArray` and
+    `MutableByteArray` types from `base` instead of defining them in this
+    library. This does not change the user-facing interface of
+    `Data.Primitive.ByteArray`.
+
+  * Add `keepAlive` that wraps `keepAlive#` for GHC 9.2 and newer. It
+    falls back to using `touch` for older GHCs.
+
+## Changes in version 0.7.4.0
+
+  * Add Lift instances (#332)
+
+  * Expose `copyPtrToMutablePrimArray`
+
+  * Improve definitions for stimes (#326)
+
+  * Support GHC 9.4. Note: GHC 9.4 is not released at the time of
+    primitive-0.7.4.0's release, so this support might be reverted by
+    a hackage metadata revision if things change.
+
+  * Drop support for GHC 7.10
+
+## Changes in version 0.7.3.0
+
+  * Correct implementations of `*>` for `Array` and `SmallArray`.
+
+  * Drop support for GHC < 7.10
+
+  * Export `runByteArray` and `runPrimArray`.
+
+  * Export `createArray` and `createSmallArray`.
+
+  * Export `emptyByteArray`, `emptyPrimArray`, `emptyArray` and `emptySmallArray`.
+
+## Changes in version 0.7.2.0
+
+  * Add `thawByteArray` and `thawPrimArray`.
+
+  * Changed the `Show` instance of `ByteArray`, so that all 8-bit words
+    are rendered as two digits. For example, display `0x0D` instead of `0xD`.
+
+## Changes in version 0.7.1.0
+
+  * Introduce convenience class `MonadPrim` and `MonadPrimBase`.
+
+  * Add `PrimMonad` and `PrimBase` instances for `Lazy.ST` (GHC >= 8.2).
+    thanks to Avi Dessauer (@Avi-D-coder) for this first contribution
+
+  * Add `freezeByteArray` and `freezePrimArray`.
+
+  * Add `compareByteArrays`.
+
+  * Add `shrinkMutableByteArray`.
+
+  * Add `Eq` instances for `MutableByteArray` and `MutablePrimArray`.
+    by Andrew Martin
+
+  * Add functions for manipulating pinned Prim Arrays
+    by Andrew Martin
+
+  * Add `copyPtrToMutableByteArray`.
+
+  * Add `NFData` instances for `ByteArray`, `MutableByteArray`,
+    `PrimArray` and `MutablePrimArray`.
+    by Callan McGill
+
+  * Add `shrinkSmallMutableArray`.
+
+  * Add `clonePrimArray` and `cloneMutablePrimArray`.
+
+  * Add `cloneMutableByteArray` and `cloneByteArray`.
+
+  * Add `Prim` instances for `WordPtr` and `IntPtr`.
+
+  * Add `NFData` instances for `Array` and `SmallArray`.
+    by Callan McGill
+
+  * Add `copyByteArrayToPtr` and `copyMutableByteArrayToPtr`.
+
+  * Export `arrayFromList` and `arrayFromListN`.
+
+## Changes in version 0.7.0.1
+
+  * Allow building with GHC 8.12.
+    Thanks Ryan GL Scott for this and every compat patch over time.
+
+## Changes in version 0.7.0.0
+
+  * Remove `Addr` data type, lifted code should use `Ptr a` now
+
+  * Define `MonadFail` instances for `Array` and `SmallArray`.
+
+  * Define `unsafeInterleave`.
+
+  * Add a `Prim` instance for `StablePtr`
+
+  * Remove `UnliftedArray` and related type classes
+
+  * Add a lot more tests for `PrimArray`.
+
+  * Added PrimMonad instance for CPS Writer and RWS monads from Transformers
+
+  * Remove useless accidental laziness in `atomicModifyMutVar`, making it match
+    `atomicModifyIORef`. The semantics should be the same.
+
+  * lots of little documentation twiddles.
+
+## Changes in version 0.6.4.1
+
+ * Add instances for the following newtypes from `base`:
+   `Const`, `Identity`, `Down`, `Dual`, `Sum`, `Product`,
+   `First`, `Last`, `Min`, `Max`
+
+ * Add `base-orphans` dependency to test suite to accomodate
+   older versions of GHC not having instances of `Show` and `Eq`
+   for some of the above newtypes.
+
 ## Changes in version 0.6.4.0
 
  * Introduce `Data.Primitive.PrimArray`, which offers types and function
@@ -37,7 +220,7 @@
 
  * Fix the implementation of `mconcat` in the `Monoid` instance for
    `SmallArray`.
- 
+
  * Implement `Data.Primitive.Ptr`, implementations of `Ptr` functions
    that require a `Prim` constraint instead of a `Storable` constraint.
 
@@ -58,6 +241,11 @@
 
  * Add `defaultSetByteArray#` and `defaultSetOffAddr#` to
    `Data.Primitive.Types`.
+
+ * Add `Data.Primitive.MVar`, a replacement for `Control.Concurrent.MVar`
+   that can run in any `PrimMonad` instead of just `IO`. It is not a full
+   replacement. Notably, it's missing masking functions and support for
+   adding finalizers.
 
 ## Changes in version 0.6.3.0
 
diff --git a/primitive.cabal b/primitive.cabal
--- a/primitive.cabal
+++ b/primitive.cabal
@@ -1,5 +1,6 @@
+Cabal-Version:  2.0
 Name:           primitive
-Version:        0.6.4.0
+Version:        0.9.1.0
 License:        BSD3
 License-File:   LICENSE
 
@@ -10,26 +11,30 @@
 Bug-Reports:    https://github.com/haskell/primitive/issues
 Category:       Data
 Synopsis:       Primitive memory-related operations
-Cabal-Version:  >= 1.10
 Build-Type:     Simple
 Description:    This package provides various primitive memory-related operations.
 
 Extra-Source-Files: changelog.md
                     test/*.hs
                     test/LICENSE
-                    test/primitive-tests.cabal
 
 Tested-With:
-  GHC == 7.4.2,
-  GHC == 7.6.3,
-  GHC == 7.8.4,
-  GHC == 7.10.3,
-  GHC == 8.0.2,
-  GHC == 8.2.2,
-  GHC == 8.4.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.2
+  GHC == 9.10.1
 
 Library
   Default-Language: Haskell2010
+  Default-Extensions:
+        TypeOperators
   Other-Extensions:
         BangPatterns, CPP, DeriveDataTypeable,
         MagicHash, TypeFamilies, UnboxedTuples, UnliftedFFITypes
@@ -43,30 +48,76 @@
         Data.Primitive.ByteArray
         Data.Primitive.PrimArray
         Data.Primitive.SmallArray
-        Data.Primitive.UnliftedArray
-        Data.Primitive.Addr
         Data.Primitive.Ptr
         Data.Primitive.MutVar
         Data.Primitive.MVar
+        Data.Primitive.PrimVar
 
   Other-Modules:
-        Data.Primitive.Internal.Compat
         Data.Primitive.Internal.Operations
+        Data.Primitive.Internal.Read
 
-  Build-Depends: base >= 4.5 && < 4.12
-               , ghc-prim >= 0.2 && < 0.6
-               , transformers >= 0.2 && < 0.6
+  Build-Depends: base >= 4.10 && < 4.22
+               , deepseq >= 1.1 && < 1.6
+               , transformers >= 0.5 && < 0.7
+               , template-haskell >= 2.11
 
+  if impl(ghc >= 9.2)
+    cpp-options: -DHAVE_KEEPALIVE
+
+  if impl(ghc < 9.4)
+    build-depends: data-array-byte >= 0.1 && < 0.1.1
+
   Ghc-Options: -O2
 
   Include-Dirs: cbits
   Install-Includes: primitive-memops.h
-  includes: primitive-memops.h
   c-sources: cbits/primitive-memops.c
   if !os(solaris)
       cc-options: -ftree-vectorize
   if arch(i386) || arch(x86_64)
       cc-options: -msse2
+
+test-suite test-qc
+  Default-Language: Haskell2010
+  hs-source-dirs: test
+                  test/src
+  main-is: Main.hs
+  Other-Modules: PrimLaws
+  type: exitcode-stdio-1.0
+  build-depends: base
+               , base-orphans
+               , primitive
+               , quickcheck-classes-base >= 0.6 && <0.7
+               , QuickCheck >= 2.13 && < 2.16
+               , tasty >= 1.2 && < 1.6
+               , tasty-quickcheck
+               , tagged
+               , transformers >= 0.5
+               , transformers-compat
+
+  cpp-options: -DHAVE_UNARY_LAWS
+  ghc-options: -O2
+
+benchmark bench
+  Default-Language: Haskell2010
+  hs-source-dirs: bench
+  main-is: main.hs
+  type: exitcode-stdio-1.0
+  ghc-options: -O2
+  other-modules:
+    Array.Traverse.Closure
+    Array.Traverse.Unsafe
+    ByteArray.Compare
+    PrimArray.Compare
+    PrimArray.Traverse
+  build-depends:
+      base
+    , containers
+    , primitive
+    , deepseq
+    , tasty-bench
+    , transformers >= 0.5
 
 source-repository head
   type:     git
diff --git a/test/Main.hs b/test/Main.hs
new file mode 100644
--- /dev/null
+++ b/test/Main.hs
@@ -0,0 +1,439 @@
+{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE KindSignatures #-}
+{-# LANGUAGE MagicHash #-}
+{-# LANGUAGE UnboxedTuples #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE StandaloneDeriving #-}
+{-# LANGUAGE TypeApplications #-}
+
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+
+#if __GLASGOW_HASKELL__ >= 805
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE DerivingStrategies #-}
+{-# LANGUAGE DerivingVia #-}
+{-# LANGUAGE TypeInType #-}
+#endif
+
+import Control.Monad
+import Control.Monad.ST
+import Data.Complex
+import Data.Primitive
+import Data.Word
+import Data.Proxy (Proxy(..))
+import GHC.Int
+import GHC.IO
+import GHC.Exts
+import Data.Function (on)
+import Control.Applicative (Const(..))
+import PrimLaws (primLaws)
+
+import Data.Functor.Identity (Identity(..))
+import qualified Data.Monoid as Monoid
+import Data.Semigroup (stimes, stimesMonoid)
+import qualified Data.Semigroup as Semigroup
+#if !(MIN_VERSION_base(4,11,0))
+import Data.Monoid ((<>))
+#endif
+#if __GLASGOW_HASKELL__ >= 805
+import Foreign.Storable (Storable)
+#endif
+import Data.Orphans ()
+
+import Test.Tasty (defaultMain,testGroup,TestTree)
+import Test.QuickCheck (Arbitrary,Arbitrary1,Gen,CoArbitrary,Function,(===),(==>))
+import qualified Test.Tasty.QuickCheck as TQC
+import qualified Test.QuickCheck as QC
+import qualified Test.QuickCheck.Classes.Base as QCC
+import qualified Test.QuickCheck.Classes.Base.IsList as QCCL
+import qualified Data.List as L
+
+main :: IO ()
+main = do
+  testArray
+  testByteArray
+  defaultMain $ testGroup "properties"
+    [ testGroup "Array"
+      [ lawsToTest (QCC.eqLaws (Proxy :: Proxy (Array Int)))
+      , lawsToTest (QCC.ordLaws (Proxy :: Proxy (Array Int)))
+      , lawsToTest (QCC.monoidLaws (Proxy :: Proxy (Array Int)))
+      , lawsToTest (QCC.showReadLaws (Proxy :: Proxy (Array Int)))
+      , lawsToTest (QCC.functorLaws (Proxy :: Proxy Array))
+      , lawsToTest (QCC.applicativeLaws (Proxy :: Proxy Array))
+      , lawsToTest (QCC.alternativeLaws (Proxy :: Proxy Array))
+      , lawsToTest (QCC.monadLaws (Proxy :: Proxy Array))
+      , lawsToTest (QCC.monadZipLaws (Proxy :: Proxy Array))
+      , lawsToTest (QCC.monadPlusLaws (Proxy :: Proxy Array))
+      , lawsToTest (QCC.foldableLaws (Proxy :: Proxy Array))
+      , lawsToTest (QCC.traversableLaws (Proxy :: Proxy Array))
+      , lawsToTest (QCC.isListLaws (Proxy :: Proxy (Array Int)))
+      , TQC.testProperty "mapArray'" (QCCL.mapProp int16 int32 mapArray')
+      , TQC.testProperty "*>" $ \(xs :: Array Int) (ys :: Array Int) -> toList (xs *> ys) === (toList xs *> toList ys)
+      , TQC.testProperty "<*" $ \(xs :: Array Int) (ys :: Array Int) -> toList (xs <* ys) === (toList xs <* toList ys)
+      , lawsToTest (QCC.semigroupLaws (Proxy :: Proxy (Array Int)))
+      , TQC.testProperty "stimes" $ \(QC.NonNegative (n :: Int)) (xs :: Array Int) -> stimes n xs == stimesMonoid n xs
+      ]
+    , testGroup "SmallArray"
+      [ lawsToTest (QCC.eqLaws (Proxy :: Proxy (SmallArray Int)))
+      , lawsToTest (QCC.ordLaws (Proxy :: Proxy (SmallArray Int)))
+      , lawsToTest (QCC.monoidLaws (Proxy :: Proxy (SmallArray Int)))
+      , lawsToTest (QCC.showReadLaws (Proxy :: Proxy (Array Int)))
+      , lawsToTest (QCC.functorLaws (Proxy :: Proxy SmallArray))
+      , lawsToTest (QCC.applicativeLaws (Proxy :: Proxy SmallArray))
+      , lawsToTest (QCC.alternativeLaws (Proxy :: Proxy SmallArray))
+      , lawsToTest (QCC.monadLaws (Proxy :: Proxy SmallArray))
+      , lawsToTest (QCC.monadZipLaws (Proxy :: Proxy SmallArray))
+      , lawsToTest (QCC.monadPlusLaws (Proxy :: Proxy SmallArray))
+      , lawsToTest (QCC.foldableLaws (Proxy :: Proxy SmallArray))
+      , lawsToTest (QCC.traversableLaws (Proxy :: Proxy SmallArray))
+      , lawsToTest (QCC.isListLaws (Proxy :: Proxy (SmallArray Int)))
+      , TQC.testProperty "mapSmallArray'" (QCCL.mapProp int16 int32 mapSmallArray')
+      , TQC.testProperty "*>" $ \(xs :: SmallArray Int) (ys :: SmallArray Int) -> toList (xs *> ys) === (toList xs *> toList ys)
+      , TQC.testProperty "<*" $ \(xs :: SmallArray Int) (ys :: SmallArray Int) -> toList (xs <* ys) === (toList xs <* toList ys)
+      , lawsToTest (QCC.semigroupLaws (Proxy :: Proxy (SmallArray Int)))
+      , TQC.testProperty "stimes" $ \(QC.NonNegative (n :: Int)) (xs :: SmallArray Int) -> stimes n xs == stimesMonoid n xs
+      ]
+    , testGroup "ByteArray"
+      [ testGroup "Ordering"
+        [ TQC.testProperty "equality" byteArrayEqProp
+        , TQC.testProperty "compare" byteArrayCompareProp
+      , testGroup "Filling"
+        [ TQC.testProperty "Int8" (setByteArrayProp (Proxy :: Proxy Int8))
+        , TQC.testProperty "Int16" (setByteArrayProp (Proxy :: Proxy Int16))
+        , TQC.testProperty "Int32" (setByteArrayProp (Proxy :: Proxy Int32))
+        , TQC.testProperty "Int64" (setByteArrayProp (Proxy :: Proxy Int64))
+        , TQC.testProperty "Int" (setByteArrayProp (Proxy :: Proxy Int))
+        , TQC.testProperty "Word8" (setByteArrayProp (Proxy :: Proxy Word8))
+        , TQC.testProperty "Word16" (setByteArrayProp (Proxy :: Proxy Word16))
+        , TQC.testProperty "Word32" (setByteArrayProp (Proxy :: Proxy Word32))
+        , TQC.testProperty "Word64" (setByteArrayProp (Proxy :: Proxy Word64))
+        , TQC.testProperty "Word" (setByteArrayProp (Proxy :: Proxy Word))
+        ]
+      ]
+      , testGroup "Resize"
+        [ TQC.testProperty "shrink" byteArrayShrinkProp
+        , TQC.testProperty "grow" byteArrayGrowProp
+        ]
+      , lawsToTest (QCC.eqLaws (Proxy :: Proxy ByteArray))
+      , lawsToTest (QCC.ordLaws (Proxy :: Proxy ByteArray))
+      , lawsToTest (QCC.monoidLaws (Proxy :: Proxy ByteArray))
+      , lawsToTest (QCC.showReadLaws (Proxy :: Proxy (Array Int)))
+      , lawsToTest (QCC.isListLaws (Proxy :: Proxy ByteArray))
+      , TQC.testProperty "foldrByteArray" (QCCL.foldrProp word8 foldrByteArray)
+      , lawsToTest (QCC.semigroupLaws (Proxy :: Proxy ByteArray))
+      , TQC.testProperty "stimes" $ \(QC.NonNegative (n :: Int)) (xs :: ByteArray) -> stimes n xs == stimesMonoid n xs
+      ]
+    , testGroup "PrimArray"
+      [ lawsToTest (QCC.eqLaws (Proxy :: Proxy (PrimArray Word16)))
+      , lawsToTest (QCC.ordLaws (Proxy :: Proxy (PrimArray Word16)))
+      , lawsToTest (QCC.monoidLaws (Proxy :: Proxy (PrimArray Word16)))
+      , lawsToTest (QCC.isListLaws (Proxy :: Proxy (PrimArray Word16)))
+      , TQC.testProperty "foldrPrimArray" (QCCL.foldrProp int16 foldrPrimArray)
+      , TQC.testProperty "foldrPrimArray'" (QCCL.foldrProp int16 foldrPrimArray')
+      , TQC.testProperty "foldlPrimArray" (QCCL.foldlProp int16 foldlPrimArray)
+      , TQC.testProperty "foldlPrimArray'" (QCCL.foldlProp int16 foldlPrimArray')
+      , TQC.testProperty "foldlPrimArrayM'" (QCCL.foldlMProp int16 foldlPrimArrayM')
+      , TQC.testProperty "mapPrimArray" (QCCL.mapProp int16 int32 mapPrimArray)
+      , TQC.testProperty "traversePrimArray" (QCCL.traverseProp int16 int32 traversePrimArray)
+      , TQC.testProperty "traversePrimArrayP" (QCCL.traverseProp int16 int32 traversePrimArrayP)
+      , TQC.testProperty "imapPrimArray" (QCCL.imapProp int16 int32 imapPrimArray)
+      , TQC.testProperty "itraversePrimArray" (QCCL.imapMProp int16 int32 itraversePrimArray)
+      , TQC.testProperty "itraversePrimArrayP" (QCCL.imapMProp int16 int32 itraversePrimArrayP)
+      , TQC.testProperty "generatePrimArray" (QCCL.generateProp int16 generatePrimArray)
+      , TQC.testProperty "generatePrimArrayA" (QCCL.generateMProp int16 generatePrimArrayA)
+      , TQC.testProperty "generatePrimArrayP" (QCCL.generateMProp int16 generatePrimArrayP)
+      , TQC.testProperty "replicatePrimArray" (QCCL.replicateProp int16 replicatePrimArray)
+      , TQC.testProperty "replicatePrimArrayA" (QCCL.replicateMProp int16 replicatePrimArrayA)
+      , TQC.testProperty "replicatePrimArrayP" (QCCL.replicateMProp int16 replicatePrimArrayP)
+      , TQC.testProperty "filterPrimArray" (QCCL.filterProp int16 filterPrimArray)
+      , TQC.testProperty "filterPrimArrayA" (QCCL.filterMProp int16 filterPrimArrayA)
+      , TQC.testProperty "filterPrimArrayP" (QCCL.filterMProp int16 filterPrimArrayP)
+      , TQC.testProperty "mapMaybePrimArray" (QCCL.mapMaybeProp int16 int32 mapMaybePrimArray)
+      , TQC.testProperty "mapMaybePrimArrayA" (QCCL.mapMaybeMProp int16 int32 mapMaybePrimArrayA)
+      , TQC.testProperty "mapMaybePrimArrayP" (QCCL.mapMaybeMProp int16 int32 mapMaybePrimArrayP)
+      , lawsToTest (QCC.semigroupLaws (Proxy :: Proxy (PrimArray Word16)))
+      , TQC.testProperty "stimes" $ \(QC.NonNegative (n :: Int)) (xs :: PrimArray Word16) -> stimes n xs == stimesMonoid n xs
+      ]
+    , testGroup "DefaultSetMethod"
+      [ lawsToTest (primLaws (Proxy :: Proxy DefaultSetMethod))
+      ]
+#if __GLASGOW_HASKELL__ >= 805
+    , testGroup "PrimStorable"
+      [ lawsToTest (QCC.storableLaws (Proxy :: Proxy Derived))
+      ]
+#endif
+    , testGroup "Prim"
+      [ renameLawsToTest "Word" (primLaws (Proxy :: Proxy Word))
+      , renameLawsToTest "Word8" (primLaws (Proxy :: Proxy Word8))
+      , renameLawsToTest "Word16" (primLaws (Proxy :: Proxy Word16))
+      , renameLawsToTest "Word32" (primLaws (Proxy :: Proxy Word32))
+      , renameLawsToTest "Word64" (primLaws (Proxy :: Proxy Word64))
+      , renameLawsToTest "Int" (primLaws (Proxy :: Proxy Int))
+      , renameLawsToTest "Int8" (primLaws (Proxy :: Proxy Int8))
+      , renameLawsToTest "Int16" (primLaws (Proxy :: Proxy Int16))
+      , renameLawsToTest "Int32" (primLaws (Proxy :: Proxy Int32))
+      , renameLawsToTest "Int64" (primLaws (Proxy :: Proxy Int64))
+      , renameLawsToTest "Const" (primLaws (Proxy :: Proxy (Const Int16 Int16)))
+      , renameLawsToTest "Down" (primLaws (Proxy :: Proxy (Down Int16)))
+      , renameLawsToTest "Identity" (primLaws (Proxy :: Proxy (Identity Int16)))
+      , renameLawsToTest "Dual" (primLaws (Proxy :: Proxy (Monoid.Dual Int16)))
+      , renameLawsToTest "Sum" (primLaws (Proxy :: Proxy (Monoid.Sum Int16)))
+      , renameLawsToTest "Product" (primLaws (Proxy :: Proxy (Monoid.Product Int16)))
+      , renameLawsToTest "First" (primLaws (Proxy :: Proxy (Semigroup.First Int16)))
+      , renameLawsToTest "Last" (primLaws (Proxy :: Proxy (Semigroup.Last Int16)))
+      , renameLawsToTest "Min" (primLaws (Proxy :: Proxy (Semigroup.Min Int16)))
+      , renameLawsToTest "Max" (primLaws (Proxy :: Proxy (Semigroup.Max Int16)))
+      , renameLawsToTest "Complex" (primLaws (Proxy :: Proxy (Complex Double)))
+      ]
+    ]
+
+deriving instance Arbitrary a => Arbitrary (Down a)
+-- Const, Dual, Sum, Product: all have Arbitrary instances defined
+-- in QuickCheck itself
+deriving instance Arbitrary a => Arbitrary (Semigroup.First a)
+deriving instance Arbitrary a => Arbitrary (Semigroup.Last a)
+deriving instance Arbitrary a => Arbitrary (Semigroup.Min a)
+deriving instance Arbitrary a => Arbitrary (Semigroup.Max a)
+
+word8 :: Proxy Word8
+word8 = Proxy
+
+int16 :: Proxy Int16
+int16 = Proxy
+
+int32 :: Proxy Int32
+int32 = Proxy
+
+
+setByteArrayProp :: forall a. (Prim a, Eq a, Arbitrary a, Show a) => Proxy a -> QC.Property
+setByteArrayProp _ = QC.property $ \(QC.NonNegative (n :: Int)) (QC.NonNegative (off :: Int)) (QC.NonNegative (len :: Int)) (x :: a) (y :: a) ->
+  (off < n && off + len <= n) ==>
+  -- We use PrimArray in this test because it makes it easier to
+  -- get the element-vs-byte distinction right.
+  let actual = runST $ do
+        m <- newPrimArray n
+        forM_ (enumFromTo 0 (n - 1)) $ \ix -> writePrimArray m ix x
+        setPrimArray m off len y
+        unsafeFreezePrimArray m
+      expected = runST $ do
+        m <- newPrimArray n
+        forM_ (enumFromTo 0 (n - 1)) $ \ix -> writePrimArray m ix x
+        forM_ (enumFromTo off (off + len - 1)) $ \ix -> writePrimArray m ix y
+        unsafeFreezePrimArray m
+   in expected === actual
+
+
+-- Tests that using resizeByteArray to shrink a byte array produces
+-- the same results as calling Data.List.take on the list that the
+-- byte array corresponds to.
+byteArrayShrinkProp :: QC.Property
+byteArrayShrinkProp = QC.property $ \(QC.NonNegative (n :: Int)) (QC.NonNegative (m :: Int)) ->
+  let large = max n m
+      small = min n m
+      xs = intsLessThan large
+      ys = byteArrayFromList xs
+      largeBytes = large * sizeOfType @Int
+      smallBytes = small * sizeOfType @Int
+      expected = byteArrayFromList (L.take small xs)
+      actual = runST $ do
+        mzs0 <- newByteArray largeBytes
+        copyByteArray mzs0 0 ys 0 largeBytes
+        mzs1 <- resizeMutableByteArray mzs0 smallBytes
+        unsafeFreezeByteArray mzs1
+   in expected === actual
+
+-- Tests that using resizeByteArray with copyByteArray (to fill in the
+-- new empty space) to grow a byte array produces the same results as
+-- calling Data.List.++ on the lists corresponding to the original
+-- byte array and the appended byte array.
+byteArrayGrowProp :: QC.Property
+byteArrayGrowProp = QC.property $ \(QC.NonNegative (n :: Int)) (QC.NonNegative (m :: Int)) ->
+  let large = max n m
+      small = min n m
+      xs1 = intsLessThan small
+      xs2 = intsLessThan (large - small)
+      ys1 = byteArrayFromList xs1
+      ys2 = byteArrayFromList xs2
+      largeBytes = large * sizeOfType @Int
+      smallBytes = small * sizeOfType @Int
+      expected = byteArrayFromList (xs1 ++ xs2)
+      actual = runST $ do
+        mzs0 <- newByteArray smallBytes
+        copyByteArray mzs0 0 ys1 0 smallBytes
+        mzs1 <- resizeMutableByteArray mzs0 largeBytes
+        copyByteArray mzs1 smallBytes ys2 0 ((large - small) * sizeOfType @Int)
+        unsafeFreezeByteArray mzs1
+   in expected === actual
+
+-- Tests that writing stable ptrs to a PrimArray, reading them back
+-- out, and then dereferencing them gives correct results.
+--stablePtrPrimProp :: QC.Property
+--stablePtrPrimProp = QC.property $ \(xs :: [Integer]) -> unsafePerformIO $ do
+--  ptrs <- mapM newStablePtr xs
+--  let ptrs' = primArrayToList (primArrayFromList ptrs)
+--  ys <- mapM deRefStablePtr ptrs'
+--  mapM_ freeStablePtr ptrs'
+--  return (xs === ys)
+
+--stablePtrPrimBlockProp :: QC.Property
+--stablePtrPrimBlockProp = QC.property $ \(x :: Word) (QC.NonNegative (len :: Int)) -> unsafePerformIO $ do
+--  ptr <- newStablePtr x
+--  let ptrs' = replicatePrimArray len ptr
+--  let go ix = if ix < len
+--        then do
+--          n <- deRefStablePtr (indexPrimArray ptrs' ix)
+--          ns <- go (ix + 1)
+--          return (n : ns)
+--        else return []
+--  ys <- go 0
+--  freeStablePtr ptr
+--  return (L.replicate len x === ys)
+
+
+
+-- Provide the non-negative integers up to the bound. For example:
+--
+-- >>> intsLessThan 5
+-- [0,1,2,3,4]
+intsLessThan :: Int -> [Int]
+intsLessThan i = if i < 1
+  then []
+  else (i - 1) : intsLessThan (i - 1)
+
+byteArrayCompareProp :: QC.Property
+byteArrayCompareProp = QC.property $ \(xs :: [Word8]) (ys :: [Word8]) ->
+  compareLengthFirst xs ys === compare (byteArrayFromList xs) (byteArrayFromList ys)
+
+byteArrayEqProp :: QC.Property
+byteArrayEqProp = QC.property $ \(xs :: [Word8]) (ys :: [Word8]) ->
+  (compareLengthFirst xs ys == EQ) === (byteArrayFromList xs == byteArrayFromList ys)
+
+compareLengthFirst :: [Word8] -> [Word8] -> Ordering
+compareLengthFirst xs ys = (compare `on` length) xs ys <> compare xs ys
+
+lawsToTest :: QCC.Laws -> TestTree
+lawsToTest (QCC.Laws name pairs) = testGroup name (map (uncurry TQC.testProperty) pairs)
+
+renameLawsToTest :: String -> QCC.Laws -> TestTree
+renameLawsToTest name (QCC.Laws _ pairs) = testGroup name (map (uncurry TQC.testProperty) pairs)
+
+testArray :: IO ()
+testArray = do
+    arr <- newArray 1 'A'
+    let unit =
+            case writeArray arr 0 'B' of
+                IO f ->
+                    case f realWorld# of
+                        (# _, _ #) -> ()
+    c1 <- readArray arr 0
+    return $! unit
+    c2 <- readArray arr 0
+    if c1 == 'A' && c2 == 'B'
+        then return ()
+        else error $ "Expected AB, got: " ++ show (c1, c2)
+
+testByteArray :: IO ()
+testByteArray = do
+    let arr1 = mkByteArray ([0xde, 0xad, 0xbe, 0xef] :: [Word8])
+        arr2 = mkByteArray ([0xde, 0xad, 0xbe, 0xef] :: [Word8])
+        arr3 = mkByteArray ([0xde, 0xad, 0xbe, 0xee] :: [Word8])
+        arr4 = mkByteArray ([0xde, 0xad, 0xbe, 0xdd] :: [Word8])
+        arr5 = mkByteArray ([0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xdd] :: [Word8])
+        arr6 = mkByteArray ([0xde, 0xad, 0x00, 0x01, 0xb0] :: [Word8])
+    when (show arr1 /= "[0xde, 0xad, 0xbe, 0xef]") $
+        fail $ "ByteArray Show incorrect: "++show arr1
+    when (show arr6 /= "[0xde, 0xad, 0x00, 0x01, 0xb0]") $
+        fail $ "ByteArray Show incorrect: "++ show arr6
+    when (compareByteArrays arr3 1 arr4 1 3 /= GT) $
+        fail $ "arr3[1,3] should be greater than arr4[1,3]"
+    when (compareByteArrays arr3 0 arr4 1 3 /= GT) $
+        fail $ "arr3[0,3] should be greater than arr4[1,3]"
+    when (compareByteArrays arr5 1 arr2 1 3 /= EQ) $
+        fail $ "arr3[1,3] should be equal to than arr4[1,3]"
+    unless (arr1 > arr3) $
+        fail $ "ByteArray Ord incorrect"
+    unless (arr1 == arr2) $
+        fail $ "ByteArray Eq incorrect"
+    unless (mappend arr1 arr4 == arr5) $
+        fail $ "ByteArray Monoid mappend incorrect"
+    unless (mappend arr1 (mappend arr3 arr4) == mappend (mappend arr1 arr3) arr4) $
+        fail $ "ByteArray Monoid mappend not associative"
+    unless (mconcat [arr1,arr2,arr3,arr4,arr5] == (arr1 <> arr2 <> arr3 <> arr4 <> arr5)) $
+        fail $ "ByteArray Monoid mconcat incorrect"
+    unless (stimes (3 :: Int) arr4 == (arr4 <> arr4 <> arr4)) $
+        fail $ "ByteArray Semigroup stimes incorrect"
+
+mkByteArray :: forall a. Prim a => [a] -> ByteArray
+mkByteArray xs = runST $ do
+    marr <- newByteArray (length xs * sizeOfType @a)
+    sequence_ $ zipWith (writeByteArray marr) [0..] xs
+    unsafeFreezeByteArray marr
+
+instance Arbitrary1 Array where
+  liftArbitrary elemGen = fmap fromList (QC.liftArbitrary elemGen)
+
+instance Arbitrary a => Arbitrary (Array a) where
+  arbitrary = fmap fromList QC.arbitrary
+
+instance Arbitrary1 SmallArray where
+  liftArbitrary elemGen = fmap smallArrayFromList (QC.liftArbitrary elemGen)
+
+instance Arbitrary a => Arbitrary (SmallArray a) where
+  arbitrary = fmap smallArrayFromList QC.arbitrary
+
+instance Arbitrary ByteArray where
+  arbitrary = do
+    xs <- QC.arbitrary :: Gen [Word8]
+    return $ runST $ do
+      a <- newByteArray (L.length xs)
+      iforM_ xs $ \ix x -> do
+        writeByteArray a ix x
+      unsafeFreezeByteArray a
+
+instance (Arbitrary a, Prim a) => Arbitrary (PrimArray a) where
+  arbitrary = do
+    xs <- QC.arbitrary :: Gen [a]
+    return $ runST $ do
+      a <- newPrimArray (L.length xs)
+      iforM_ xs $ \ix x -> do
+        writePrimArray a ix x
+      unsafeFreezePrimArray a
+
+
+
+instance (Prim a, CoArbitrary a) => CoArbitrary (PrimArray a) where
+  coarbitrary x = QC.coarbitrary (primArrayToList x)
+
+instance (Prim a, Function a) => Function (PrimArray a) where
+  function = QC.functionMap primArrayToList primArrayFromList
+
+iforM_ :: Monad m => [a] -> (Int -> a -> m b) -> m ()
+iforM_ xs0 f = go 0 xs0 where
+  go !_ [] = return ()
+  go !ix (x : xs) = f ix x >> go (ix + 1) xs
+
+newtype DefaultSetMethod = DefaultSetMethod Int16
+  deriving (Eq,Show,Arbitrary)
+
+instance Prim DefaultSetMethod where
+  sizeOfType# _ = sizeOfType# (Proxy :: Proxy Int16)
+  alignmentOfType# _ = alignmentOfType# (Proxy :: Proxy Int16)
+  indexByteArray# arr ix = DefaultSetMethod (indexByteArray# arr ix)
+  readByteArray# arr ix s0 = case readByteArray# arr ix s0 of
+    (# s1, n #) -> (# s1, DefaultSetMethod n #)
+  writeByteArray# arr ix (DefaultSetMethod n) s0 = writeByteArray# arr ix n s0
+  setByteArray# = defaultSetByteArray#
+  indexOffAddr# addr off = DefaultSetMethod (indexOffAddr# addr off)
+  readOffAddr# addr off s0 = case readOffAddr# addr off s0 of
+    (# s1, n #) -> (# s1, DefaultSetMethod n #)
+  writeOffAddr# addr off (DefaultSetMethod n) s0 = writeOffAddr# addr off n s0
+  setOffAddr# = defaultSetOffAddr#
+
+#if __GLASGOW_HASKELL__ >= 805
+newtype Derived = Derived Int16
+  deriving stock (Eq, Show)
+  deriving newtype (Arbitrary, Prim)
+  deriving Storable via (PrimStorable Derived)
+#endif
diff --git a/test/main.hs b/test/main.hs
deleted file mode 100644
--- a/test/main.hs
+++ /dev/null
@@ -1,342 +0,0 @@
-{-# LANGUAGE BangPatterns #-}
-{-# LANGUAGE CPP #-}
-{-# LANGUAGE GeneralizedNewtypeDeriving #-}
-{-# LANGUAGE KindSignatures #-}
-{-# LANGUAGE MagicHash #-}
-{-# LANGUAGE UnboxedTuples #-}
-{-# LANGUAGE ScopedTypeVariables #-}
-
-import Control.Applicative
-import Control.Monad
-import Control.Monad.Fix (fix)
-import Control.Monad.Primitive
-import Control.Monad.ST
-import Data.Monoid
-import Data.Primitive
-import Data.Primitive.Array
-import Data.Primitive.ByteArray
-import Data.Primitive.Types
-import Data.Primitive.SmallArray
-import Data.Primitive.PrimArray
-import Data.Word
-import Data.Proxy (Proxy(..))
-import GHC.Int
-import GHC.IO
-import GHC.Prim
-import Data.Function (on)
-#if MIN_VERSION_base(4,9,0)
-import Data.Semigroup (stimes)
-#endif
-
-import Test.Tasty (defaultMain,testGroup,TestTree)
-import Test.QuickCheck (Arbitrary,Arbitrary1,Gen,(===),CoArbitrary,Function)
-import qualified Test.Tasty.QuickCheck as TQC
-import qualified Test.QuickCheck as QC
-import qualified Test.QuickCheck.Classes as QCC
-import qualified Test.QuickCheck.Classes.IsList as QCCL
-import qualified Data.List as L
-
-main :: IO ()
-main = do
-  testArray
-  testByteArray
-  defaultMain $ testGroup "properties"
-    [ testGroup "Array"
-      [ lawsToTest (QCC.eqLaws (Proxy :: Proxy (Array Int)))
-      , lawsToTest (QCC.ordLaws (Proxy :: Proxy (Array Int)))
-      , lawsToTest (QCC.monoidLaws (Proxy :: Proxy (Array Int)))
-      , lawsToTest (QCC.showReadLaws (Proxy :: Proxy (Array Int)))
-#if MIN_VERSION_base(4,9,0) || MIN_VERSION_transformers(0,4,0)
-      , lawsToTest (QCC.functorLaws (Proxy1 :: Proxy1 Array))
-      , lawsToTest (QCC.applicativeLaws (Proxy1 :: Proxy1 Array))
-      , lawsToTest (QCC.monadLaws (Proxy1 :: Proxy1 Array))
-      , lawsToTest (QCC.foldableLaws (Proxy1 :: Proxy1 Array))
-      , lawsToTest (QCC.traversableLaws (Proxy1 :: Proxy1 Array))
-#endif
-#if MIN_VERSION_base(4,7,0)
-      , lawsToTest (QCC.isListLaws (Proxy :: Proxy (Array Int)))
-      , TQC.testProperty "mapArray'" (QCCL.mapProp int16 int32 mapArray')
-#endif
-      ]
-    , testGroup "SmallArray"
-      [ lawsToTest (QCC.eqLaws (Proxy :: Proxy (SmallArray Int)))
-      , lawsToTest (QCC.ordLaws (Proxy :: Proxy (SmallArray Int)))
-      , lawsToTest (QCC.monoidLaws (Proxy :: Proxy (SmallArray Int)))
-      , lawsToTest (QCC.showReadLaws (Proxy :: Proxy (Array Int)))
-#if MIN_VERSION_base(4,9,0) || MIN_VERSION_transformers(0,4,0)
-      , lawsToTest (QCC.functorLaws (Proxy1 :: Proxy1 SmallArray))
-      , lawsToTest (QCC.applicativeLaws (Proxy1 :: Proxy1 SmallArray))
-      , lawsToTest (QCC.monadLaws (Proxy1 :: Proxy1 SmallArray))
-      , lawsToTest (QCC.foldableLaws (Proxy1 :: Proxy1 SmallArray))
-      , lawsToTest (QCC.traversableLaws (Proxy1 :: Proxy1 SmallArray))
-#endif
-#if MIN_VERSION_base(4,7,0)
-      , lawsToTest (QCC.isListLaws (Proxy :: Proxy (SmallArray Int)))
-      , TQC.testProperty "mapSmallArray'" (QCCL.mapProp int16 int32 mapSmallArray')
-#endif
-      ]
-    , testGroup "ByteArray"
-      [ testGroup "Ordering"
-        [ TQC.testProperty "equality" byteArrayEqProp
-        , TQC.testProperty "compare" byteArrayCompareProp
-        ]
-      , testGroup "Resize"
-        [ TQC.testProperty "shrink" byteArrayShrinkProp
-        , TQC.testProperty "grow" byteArrayGrowProp
-        ]
-      , lawsToTest (QCC.eqLaws (Proxy :: Proxy ByteArray))
-      , lawsToTest (QCC.ordLaws (Proxy :: Proxy ByteArray))
-      , lawsToTest (QCC.showReadLaws (Proxy :: Proxy (Array Int)))
-#if MIN_VERSION_base(4,7,0)
-      , lawsToTest (QCC.isListLaws (Proxy :: Proxy ByteArray))
-#endif
-      ]
-    , testGroup "PrimArray"
-      [ lawsToTest (QCC.eqLaws (Proxy :: Proxy (PrimArray Word16)))
-      , lawsToTest (QCC.ordLaws (Proxy :: Proxy (PrimArray Word16)))
-      , lawsToTest (QCC.monoidLaws (Proxy :: Proxy (PrimArray Word16)))
-#if MIN_VERSION_base(4,7,0)
-      , lawsToTest (QCC.isListLaws (Proxy :: Proxy (PrimArray Word16)))
-      , TQC.testProperty "foldrPrimArray" (QCCL.foldrProp int16 foldrPrimArray)
-      , TQC.testProperty "foldrPrimArray'" (QCCL.foldrProp int16 foldrPrimArray')
-      , TQC.testProperty "foldlPrimArray" (QCCL.foldlProp int16 foldlPrimArray)
-      , TQC.testProperty "foldlPrimArray'" (QCCL.foldlProp int16 foldlPrimArray')
-      , TQC.testProperty "foldlPrimArrayM'" (QCCL.foldlMProp int16 foldlPrimArrayM')
-      , TQC.testProperty "mapPrimArray" (QCCL.mapProp int16 int32 mapPrimArray)
-      , TQC.testProperty "traversePrimArray" (QCCL.traverseProp int16 int32 traversePrimArray)
-      , TQC.testProperty "traversePrimArrayP" (QCCL.traverseProp int16 int32 traversePrimArrayP)
-      , TQC.testProperty "imapPrimArray" (QCCL.imapProp int16 int32 imapPrimArray)
-      , TQC.testProperty "itraversePrimArray" (QCCL.imapMProp int16 int32 itraversePrimArray)
-      , TQC.testProperty "itraversePrimArrayP" (QCCL.imapMProp int16 int32 itraversePrimArrayP)
-      , TQC.testProperty "generatePrimArray" (QCCL.generateProp int16 generatePrimArray)
-      , TQC.testProperty "generatePrimArrayA" (QCCL.generateMProp int16 generatePrimArrayA)
-      , TQC.testProperty "generatePrimArrayP" (QCCL.generateMProp int16 generatePrimArrayP)
-      , TQC.testProperty "replicatePrimArray" (QCCL.replicateProp int16 replicatePrimArray)
-      , TQC.testProperty "replicatePrimArrayA" (QCCL.replicateMProp int16 replicatePrimArrayA)
-      , TQC.testProperty "replicatePrimArrayP" (QCCL.replicateMProp int16 replicatePrimArrayP)
-      , TQC.testProperty "filterPrimArray" (QCCL.filterProp int16 filterPrimArray)
-      , TQC.testProperty "filterPrimArrayA" (QCCL.filterMProp int16 filterPrimArrayA)
-      , TQC.testProperty "filterPrimArrayP" (QCCL.filterMProp int16 filterPrimArrayP)
-      , TQC.testProperty "mapMaybePrimArray" (QCCL.mapMaybeProp int16 int32 mapMaybePrimArray)
-      , TQC.testProperty "mapMaybePrimArrayA" (QCCL.mapMaybeMProp int16 int32 mapMaybePrimArrayA)
-      , TQC.testProperty "mapMaybePrimArrayP" (QCCL.mapMaybeMProp int16 int32 mapMaybePrimArrayP)
-#endif
-      ]
-    , testGroup "UnliftedArray"
-      [ lawsToTest (QCC.eqLaws (Proxy :: Proxy (UnliftedArray (PrimArray Int16))))
-      , lawsToTest (QCC.ordLaws (Proxy :: Proxy (UnliftedArray (PrimArray Int16))))
-      , lawsToTest (QCC.monoidLaws (Proxy :: Proxy (UnliftedArray (PrimArray Int16))))
-#if MIN_VERSION_base(4,7,0)
-      , lawsToTest (QCC.isListLaws (Proxy :: Proxy (UnliftedArray (PrimArray Int16))))
-      , TQC.testProperty "mapUnliftedArray" (QCCL.mapProp arrInt16 arrInt32 mapUnliftedArray)
-      , TQC.testProperty "foldrUnliftedArray" (QCCL.foldrProp arrInt16 foldrUnliftedArray)
-      , TQC.testProperty "foldrUnliftedArray'" (QCCL.foldrProp arrInt16 foldrUnliftedArray')
-      , TQC.testProperty "foldlUnliftedArray" (QCCL.foldlProp arrInt16 foldlUnliftedArray)
-      , TQC.testProperty "foldlUnliftedArray'" (QCCL.foldlProp arrInt16 foldlUnliftedArray')
-#endif
-      ]
-    , testGroup "DefaultSetMethod"
-      [ lawsToTest (QCC.primLaws (Proxy :: Proxy DefaultSetMethod))
-      ]
-    -- , testGroup "PrimStorable"
-    --   [ lawsToTest (QCC.storableLaws (Proxy :: Proxy Derived))
-    --   ]
-    ]
-
-int16 :: Proxy Int16
-int16 = Proxy
-
-int32 :: Proxy Int32
-int32 = Proxy
-
-arrInt16 :: Proxy (PrimArray Int16)
-arrInt16 = Proxy
-
-arrInt32 :: Proxy (PrimArray Int16)
-arrInt32 = Proxy
-
--- Tests that using resizeByteArray to shrink a byte array produces
--- the same results as calling Data.List.take on the list that the
--- byte array corresponds to.
-byteArrayShrinkProp :: QC.Property
-byteArrayShrinkProp = QC.property $ \(QC.NonNegative (n :: Int)) (QC.NonNegative (m :: Int)) ->
-  let large = max n m
-      small = min n m
-      xs = intsLessThan large
-      ys = byteArrayFromList xs
-      largeBytes = large * sizeOf (undefined :: Int)
-      smallBytes = small * sizeOf (undefined :: Int)
-      expected = byteArrayFromList (L.take small xs)
-      actual = runST $ do
-        mzs0 <- newByteArray largeBytes
-        copyByteArray mzs0 0 ys 0 largeBytes
-        mzs1 <- resizeMutableByteArray mzs0 smallBytes
-        unsafeFreezeByteArray mzs1
-   in expected === actual
-
--- Tests that using resizeByteArray with copyByteArray (to fill in the
--- new empty space) to grow a byte array produces the same results as
--- calling Data.List.++ on the lists corresponding to the original
--- byte array and the appended byte array.
-byteArrayGrowProp :: QC.Property
-byteArrayGrowProp = QC.property $ \(QC.NonNegative (n :: Int)) (QC.NonNegative (m :: Int)) ->
-  let large = max n m
-      small = min n m
-      xs1 = intsLessThan small
-      xs2 = intsLessThan (large - small)
-      ys1 = byteArrayFromList xs1
-      ys2 = byteArrayFromList xs2
-      largeBytes = large * sizeOf (undefined :: Int)
-      smallBytes = small * sizeOf (undefined :: Int)
-      expected = byteArrayFromList (xs1 ++ xs2)
-      actual = runST $ do
-        mzs0 <- newByteArray smallBytes
-        copyByteArray mzs0 0 ys1 0 smallBytes
-        mzs1 <- resizeMutableByteArray mzs0 largeBytes
-        copyByteArray mzs1 smallBytes ys2 0 ((large - small) * sizeOf (undefined :: Int))
-        unsafeFreezeByteArray mzs1
-   in expected === actual
-
--- Provide the non-negative integers up to the bound. For example:
---
--- >>> intsLessThan 5
--- [0,1,2,3,4]
-intsLessThan :: Int -> [Int]
-intsLessThan i = if i < 1
-  then []
-  else (i - 1) : intsLessThan (i - 1)
-  
-byteArrayCompareProp :: QC.Property
-byteArrayCompareProp = QC.property $ \(xs :: [Word8]) (ys :: [Word8]) ->
-  compareLengthFirst xs ys === compare (byteArrayFromList xs) (byteArrayFromList ys)
-
-byteArrayEqProp :: QC.Property
-byteArrayEqProp = QC.property $ \(xs :: [Word8]) (ys :: [Word8]) ->
-  (compareLengthFirst xs ys == EQ) === (byteArrayFromList xs == byteArrayFromList ys)
-
-compareLengthFirst :: [Word8] -> [Word8] -> Ordering
-compareLengthFirst xs ys = (compare `on` length) xs ys <> compare xs ys
-
--- on GHC 7.4, Proxy is not polykinded, so we need this instead.
-data Proxy1 (f :: * -> *) = Proxy1
-
-lawsToTest :: QCC.Laws -> TestTree
-lawsToTest (QCC.Laws name pairs) = testGroup name (map (uncurry TQC.testProperty) pairs)
-
-testArray :: IO ()
-testArray = do
-    arr <- newArray 1 'A'
-    let unit =
-            case writeArray arr 0 'B' of
-                IO f ->
-                    case f realWorld# of
-                        (# _, _ #) -> ()
-    c1 <- readArray arr 0
-    return $! unit
-    c2 <- readArray arr 0
-    if c1 == 'A' && c2 == 'B'
-        then return ()
-        else error $ "Expected AB, got: " ++ show (c1, c2)
-
-testByteArray :: IO ()
-testByteArray = do
-    let arr1 = mkByteArray ([0xde, 0xad, 0xbe, 0xef] :: [Word8])
-        arr2 = mkByteArray ([0xde, 0xad, 0xbe, 0xef] :: [Word8])
-        arr3 = mkByteArray ([0xde, 0xad, 0xbe, 0xee] :: [Word8])
-        arr4 = mkByteArray ([0xde, 0xad, 0xbe, 0xdd] :: [Word8])
-        arr5 = mkByteArray ([0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xdd] :: [Word8])
-    when (show arr1 /= "[0xde, 0xad, 0xbe, 0xef]") $
-        fail $ "ByteArray Show incorrect: "++show arr1
-    unless (arr1 > arr3) $
-        fail $ "ByteArray Ord incorrect"
-    unless (arr1 == arr2) $
-        fail $ "ByteArray Eq incorrect"
-    unless (mappend arr1 arr4 == arr5) $
-        fail $ "ByteArray Monoid mappend incorrect"
-    unless (mappend arr1 (mappend arr3 arr4) == mappend (mappend arr1 arr3) arr4) $
-        fail $ "ByteArray Monoid mappend not associative"
-    unless (mconcat [arr1,arr2,arr3,arr4,arr5] == (arr1 <> arr2 <> arr3 <> arr4 <> arr5)) $
-        fail $ "ByteArray Monoid mconcat incorrect"
-#if MIN_VERSION_base(4,9,0)
-    unless (stimes (3 :: Int) arr4 == (arr4 <> arr4 <> arr4)) $
-        fail $ "ByteArray Semigroup stimes incorrect"
-#endif
-
-mkByteArray :: Prim a => [a] -> ByteArray
-mkByteArray xs = runST $ do
-    marr <- newByteArray (length xs * sizeOf (head xs))
-    sequence $ zipWith (writeByteArray marr) [0..] xs
-    unsafeFreezeByteArray marr
-
-instance Arbitrary1 Array where
-  liftArbitrary elemGen = fmap fromList (QC.liftArbitrary elemGen)
-
-instance Arbitrary a => Arbitrary (Array a) where
-  arbitrary = fmap fromList QC.arbitrary
-
-instance Arbitrary1 SmallArray where
-  liftArbitrary elemGen = fmap smallArrayFromList (QC.liftArbitrary elemGen)
-
-instance Arbitrary a => Arbitrary (SmallArray a) where
-  arbitrary = fmap smallArrayFromList QC.arbitrary
-
-instance Arbitrary ByteArray where
-  arbitrary = do
-    xs <- QC.arbitrary :: Gen [Word8]
-    return $ runST $ do
-      a <- newByteArray (L.length xs)
-      iforM_ xs $ \ix x -> do
-        writeByteArray a ix x
-      unsafeFreezeByteArray a
-
-instance (Arbitrary a, Prim a) => Arbitrary (PrimArray a) where
-  arbitrary = do
-    xs <- QC.arbitrary :: Gen [a]
-    return $ runST $ do
-      a <- newPrimArray (L.length xs)
-      iforM_ xs $ \ix x -> do
-        writePrimArray a ix x
-      unsafeFreezePrimArray a
-
-instance (Arbitrary a, PrimUnlifted a) => Arbitrary (UnliftedArray a) where
-  arbitrary = do
-    xs <- QC.vector =<< QC.choose (0,3)
-    return (unliftedArrayFromList xs)
-
-instance (Prim a, CoArbitrary a) => CoArbitrary (PrimArray a) where
-  coarbitrary x = QC.coarbitrary (primArrayToList x)
-
-instance (Prim a, Function a) => Function (PrimArray a) where
-  function = QC.functionMap primArrayToList primArrayFromList
-
-iforM_ :: Monad m => [a] -> (Int -> a -> m b) -> m ()
-iforM_ xs0 f = go 0 xs0 where
-  go !_ [] = return ()
-  go !ix (x : xs) = f ix x >> go (ix + 1) xs
-
-newtype DefaultSetMethod = DefaultSetMethod Int16
-  deriving (Eq,Show,Arbitrary)
-
-instance Prim DefaultSetMethod where
-  sizeOf# _ = sizeOf# (undefined :: Int16)
-  alignment# _ = alignment# (undefined :: Int16)
-  indexByteArray# arr ix = DefaultSetMethod (indexByteArray# arr ix)
-  readByteArray# arr ix s0 = case readByteArray# arr ix s0 of
-    (# s1, n #) -> (# s1, DefaultSetMethod n #)
-  writeByteArray# arr ix (DefaultSetMethod n) s0 = writeByteArray# arr ix n s0
-  setByteArray# = defaultSetByteArray#
-  indexOffAddr# addr off = DefaultSetMethod (indexOffAddr# addr off)
-  readOffAddr# addr off s0 = case readOffAddr# addr off s0 of
-    (# s1, n #) -> (# s1, DefaultSetMethod n #)
-  writeOffAddr# addr off (DefaultSetMethod n) s0 = writeOffAddr# addr off n s0
-  setOffAddr# = defaultSetOffAddr#
-
--- TODO: Uncomment this out when GHC 8.6 is release. Also, uncomment
--- the corresponding PrimStorable test group above.
---
--- newtype Derived = Derived Int16
---   deriving newtype (Prim)
---   deriving Storable via (PrimStorable Derived)
-
-
-
diff --git a/test/primitive-tests.cabal b/test/primitive-tests.cabal
deleted file mode 100644
--- a/test/primitive-tests.cabal
+++ /dev/null
@@ -1,45 +0,0 @@
-Name:           primitive-tests
-Version:        0.1
-License:        BSD3
-License-File:   LICENSE
-
-Author:         Roman Leshchinskiy <rl@cse.unsw.edu.au>
-Maintainer:     libraries@haskell.org
-Copyright:      (c) Roman Leshchinskiy 2009-2012
-Homepage:       https://github.com/haskell/primitive
-Bug-Reports:    https://github.com/haskell/primitive/issues
-Category:       Data
-Synopsis:       primitive tests
-Cabal-Version:  >= 1.10
-Build-Type:     Simple
-Description:    @primitive@ tests
-
-Tested-With:
-  GHC == 7.4.2,
-  GHC == 7.6.3,
-  GHC == 7.8.4,
-  GHC == 7.10.3,
-  GHC == 8.0.2,
-  GHC == 8.2.2,
-  GHC == 8.4.2
-
-test-suite test
-  Default-Language: Haskell2010
-  hs-source-dirs: .
-  main-is: main.hs
-  type: exitcode-stdio-1.0
-  build-depends: base >= 4.5 && < 4.12
-               , ghc-prim
-               , primitive
-               , QuickCheck
-               , tasty
-               , tasty-quickcheck
-               , tagged
-               , transformers >= 0.3
-               , quickcheck-classes >= 0.4.11.1
-  ghc-options: -O2
-
-source-repository head
-  type:     git
-  location: https://github.com/haskell/primitive
-  subdir:   test
diff --git a/test/src/PrimLaws.hs b/test/src/PrimLaws.hs
new file mode 100644
--- /dev/null
+++ b/test/src/PrimLaws.hs
@@ -0,0 +1,154 @@
+{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE MagicHash #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE TypeApplications #-}
+
+{-# OPTIONS_GHC -Wall #-}
+
+-- This module is almost an exact copy of the unexported module
+-- Test.QuickCheck.Classes.Prim from quickcheck-classes. We cannot depend
+-- on quickcheck-classes in the test suite since that would imply a circular
+-- dependency between primitive and quickcheck-classes. Instead, we copy
+-- this one module and then depend on quickcheck-classes-base to get
+-- everything else we need.
+module PrimLaws
+  ( primLaws
+  ) where
+
+import Control.Monad.Primitive (primitive_)
+import Control.Monad.ST
+import Data.Proxy (Proxy)
+import Data.Primitive.PrimArray
+import Data.Primitive.ByteArray
+import Data.Primitive.Types
+import Data.Primitive.Ptr
+import Foreign.Marshal.Alloc
+import GHC.Exts (State#, Int#, Int(I#), (+#), (<#), IsList(fromList,toList))
+
+import System.IO.Unsafe
+import Test.QuickCheck hiding ((.&.))
+
+import qualified Data.List as L
+import qualified Data.Primitive as P
+
+import Test.QuickCheck.Classes.Base (Laws(..))
+import Test.QuickCheck.Classes.Internal (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)
+  , ("ByteArray List Conversion Roundtrips", primListByteArray p)
+  , ("Ptr Put-Get (you get back what you put in)", primPutGetAddr p)
+  , ("Ptr 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 a <- mallocBytes (len * P.sizeOfType @a)
+  let go :: Int -> [a] -> IO ()
+      go !ix xs = case xs of
+        [] -> return ()
+        (x : xsNext) -> do
+          writeOffPtr ptr ix x
+          go (ix + 1) xsNext
+  go 0 as
+  let rebuild :: Int -> IO [a]
+      rebuild !ix = if ix < len
+        then (:) <$> readOffPtr ptr 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 a <- mallocBytes (len * P.sizeOfType @a)
+    writeOffPtr ptr ix a
+    a' <- readOffPtr ptr ix
+    free ptr
+    return (a == a')
+
+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)
+
+primListByteArray :: forall a. (Prim a, Eq a, Arbitrary a, Show a) => Proxy a -> Property
+primListByteArray _ = property $ \(as :: [a]) ->
+  as == toList (fromList as :: PrimArray 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
