diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,27 @@
+Copyright 2019 rightfold
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+   this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/compact-mutable-vector.cabal b/compact-mutable-vector.cabal
new file mode 100644
--- /dev/null
+++ b/compact-mutable-vector.cabal
@@ -0,0 +1,66 @@
+build-type: Simple
+cabal-version: >= 1.8
+
+name: compact-mutable-vector
+version: 0.0.0.1
+
+category: Data
+license: BSD3
+license-file: LICENSE
+author: rightfold
+maintainer: rightfold <rightfold@gmail.com>
+bug-reports: https://github.com/rightfold/compact-mutable-vector/issues
+
+synopsis: Mutable vector with different GC characteristics
+description:
+  Library for avoiding excessive mutable array traversals by the garbage
+  collector when you use compact regions for your elements.
+  .
+  See Haddock for more information.
+
+source-repository head
+  type: git
+  location: https://github.com/rightfold/compact-mutable-vector.git
+
+library
+  build-depends:
+    base          >= 4.12 && < 5,
+    compact,
+    ghc-prim,
+    primitive     >= 0.5.0.1 && < 0.8,
+    vector        >= 0.12 && < 0.13
+
+  exposed-modules:
+    Data.Vector.Mutable.Compact
+
+  ghc-options:
+    -Wall
+    -Wincomplete-record-updates
+    -Wincomplete-uni-patterns
+
+  hs-source-dirs:
+    src
+
+test-suite test
+  build-depends:
+    base,
+    compact,
+    compact-mutable-vector,
+    hspec
+
+  ghc-options:
+    -Wall
+    -Wincomplete-record-updates
+    -Wincomplete-uni-patterns
+
+  hs-source-dirs:
+    test
+
+  main-is:
+    Main.hs
+
+  other-modules:
+    Data.Vector.Mutable.CompactSpec
+
+  type:
+    exitcode-stdio-1.0
diff --git a/src/Data/Vector/Mutable/Compact.hs b/src/Data/Vector/Mutable/Compact.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Vector/Mutable/Compact.hs
@@ -0,0 +1,108 @@
+{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE MagicHash #-}
+{-# LANGUAGE UnboxedTuples #-}
+
+-- |
+-- When you use normal mutable vectors, the garbage collector will traverse
+-- them on every garbage collection, because they may point to data that is
+-- newer than them. If you want to store your elements on a compact region
+-- anyway, but also need to point to them from a mutable vector, you can
+-- exploit the stability of the elements' addresses after adding them to the
+-- compact region, to avoid these expensive traversals when collecting garbage.
+--
+-- This library offers a data type for mutable vectors where all elements are
+-- stored in a compact region, using an unspeakable pointer casting hack.
+module Data.Vector.Mutable.Compact
+  ( -- * Basic operations
+    CVector
+  , replicate
+  , read
+  , write
+
+    -- * Shape querying
+  , length
+  , null
+  ) where
+
+import Prelude hiding (length, null, read, replicate)
+
+import Data.Primitive.Addr (Addr (..))
+import Data.Compact (Compact)
+import Data.Vector.Primitive.Mutable (MVector)
+import GHC.IO (IO (..))
+import GHC.Prim (RealWorld)
+
+import qualified Data.Compact as Compact
+import qualified Data.Vector.Primitive.Mutable as MVector
+import qualified GHC.Prim as Prim
+
+--------------------------------------------------------------------------------
+-- Basic operations
+
+-- |
+-- Store the elements of the vector in a compact region, and use an unboxed
+-- vector of pointers to the elements. Values in a compact region will not be
+-- moved by the garbage collector, so their addresses are stable, and can hence
+-- be stored in a mutable byte array. This avoids garbage collection overhead
+-- if you store your elements in a compact region anyway, because the garbage
+-- collector will not traverse byte arrays.
+--
+-- The elements must be able to be stored in a compact region. See
+-- "Data.Compact" for a list of restrictions. The vector itself cannot itself
+-- be stored in a compact region, because it is mutable.
+--
+-- Elements can never be bottom, because they are added to a compact region.
+data CVector a =
+  CVector
+    !(Compact ())
+    {-# UNPACK #-} !(MVector RealWorld Addr)
+
+-- |
+-- Create a new mutable vector of the given length, with each element set to
+-- the given value. The given compact region will be used for all future
+-- elements inserted into this vector.
+replicate :: Compact () -> Int -> b -> IO (CVector b)
+replicate c n x = do
+  -- TODO: Should we add the element with sharing?
+  xC <- Compact.getCompact <$> Compact.compactAdd c x
+  xA <- IO $ \s -> let !(# s', xA #) = Prim.anyToAddr# xC s in
+                   (# s', Addr xA #)
+  v  <- MVector.replicate n xA
+  pure $ CVector c v
+
+-- |
+-- Read the element at the given zero-based position. If the position is out of
+-- bounds, crash.
+read :: CVector a -> Int -> IO a
+read (CVector _ v) i = do
+  Addr xA <- MVector.read v i
+  let (# xC #) = Prim.addrToAny# xA
+  pure xC
+
+-- |
+-- Write an element at the given zero-based position. If the position is out of
+-- bounds, crash.
+--
+-- The element will be added to the compact region.
+write :: CVector a -> Int -> a -> IO ()
+write (CVector c v) i x = do
+  -- TODO: Should we add the element with sharing?
+  xC <- Compact.getCompact <$> Compact.compactAdd c x
+  xA <- IO $ \s -> let !(# s', xA #) = Prim.anyToAddr# xC s in
+                   (# s', Addr xA #)
+  MVector.write v i xA
+
+--------------------------------------------------------------------------------
+-- Shape querying
+
+-- |
+-- How many elements are in the vector?
+{-# INLINE length #-}
+length :: CVector a -> Int
+length (CVector _ v) = MVector.length v
+
+-- |
+-- Is the vector empty?
+{-# INLINE null #-}
+null :: CVector a -> Bool
+null (CVector _ v) = MVector.null v
diff --git a/test/Data/Vector/Mutable/CompactSpec.hs b/test/Data/Vector/Mutable/CompactSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Data/Vector/Mutable/CompactSpec.hs
@@ -0,0 +1,29 @@
+module Data.Vector.Mutable.CompactSpec
+  ( spec
+  ) where
+
+import Data.Foldable (for_)
+import Test.Hspec (Spec, it, shouldBe)
+import System.Mem (performGC)
+
+import qualified Data.Compact as Compact
+import qualified Data.Vector.Mutable.Compact as CVector
+
+spec :: Spec
+spec =
+  for_ [0 .. 63] $ \n ->
+    it (show n) $ do
+      c <- Compact.compact ()
+      v <- CVector.replicate c n "ABC"
+      for_ [0 .. n - 1] $ \i -> do
+
+        performGC
+
+        abc <- CVector.read v i
+        abc `shouldBe` "ABC"
+
+        performGC
+
+        CVector.write v i (show i)
+        def <- CVector.read v i
+        def `shouldBe` (show i)
diff --git a/test/Main.hs b/test/Main.hs
new file mode 100644
--- /dev/null
+++ b/test/Main.hs
@@ -0,0 +1,1 @@
+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
