diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,22 @@
+Copyright (c) 2022 Nikita Volkov
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
diff --git a/isomorphism-class.cabal b/isomorphism-class.cabal
new file mode 100644
--- /dev/null
+++ b/isomorphism-class.cabal
@@ -0,0 +1,65 @@
+cabal-version: 3.0
+
+name: isomorphism-class
+version: 0.1
+synopsis: Isomorphism typeclass solving the conversion problem
+homepage: https://github.com/nikita-volkov/isomorphism-class
+bug-reports: https://github.com/nikita-volkov/isomorphism-class/issues
+author: Nikita Volkov <nikita.y.volkov@mail.ru>
+maintainer: Nikita Volkov <nikita.y.volkov@mail.ru>
+copyright: (c) 2022 Nikita Volkov
+license: MIT
+license-file: LICENSE
+
+source-repository head
+  type: git
+  location: git://github.com/nikita-volkov/isomorphism-class.git
+
+common language-settings
+  default-language: Haskell2010
+  default-extensions:
+    FlexibleContexts
+    FlexibleInstances
+    MagicHash
+    MultiParamTypeClasses
+    NoImplicitPrelude
+    ScopedTypeVariables
+    TypeApplications
+    UndecidableSuperClasses
+
+common library-dependencies
+  build-depends:
+    base >=4.12 && <5,
+    bytestring >=0.10 && <0.12,
+    containers >=0.6 && <0.7,
+    hashable >=1 && <2,
+    primitive >=0.7.3 && <0.8,
+    text >=1.2 && <3,
+    unordered-containers >=0.2 && <0.3,
+    vector >=0.12 && <0.14,
+
+library
+  import: language-settings, library-dependencies
+  hs-source-dirs: library
+  exposed-modules:
+    IsomorphismClass
+  other-modules:
+    IsomorphismClass.Prelude
+    IsomorphismClass.TextCompat.Array
+
+test-suite test
+  import: language-settings, library-dependencies
+  type: exitcode-stdio-1.0
+  hs-source-dirs: test
+  main-is: Main.hs
+  other-modules:
+    Test.ExtraInstances
+  build-depends:
+    bytestring,
+    isomorphism-class,
+    QuickCheck >=2.13 && <3,
+    quickcheck-instances ==0.3.27,
+    rebase >=1.15 && <1.16,
+    tasty >=1.2.3 && <2,
+    tasty-hunit >=0.10.0.2 && <0.11,
+    tasty-quickcheck >=0.10.1 && <0.11,
diff --git a/library/IsomorphismClass.hs b/library/IsomorphismClass.hs
new file mode 100644
--- /dev/null
+++ b/library/IsomorphismClass.hs
@@ -0,0 +1,552 @@
+-- |
+-- Isomorphism law as a lawful solution to the conversion problem.
+--
+-- = Conversion problem
+--
+-- Have you ever looked for a @toString@ function? How often do you
+-- import @Data.Text.Lazy@ only to call its 'Data.Text.Lazy.fromStrict'? How
+-- about importing @Data.Text@ only to to call its 'Data.Text.unpack'? How
+-- about going thru the always fun sequence of
+-- importing @Data.ByteString.Builder@ only to to call its
+-- 'Data.ByteString.Builder.toLazyByteString' and then importing
+-- @Data.ByteString.Lazy@ only to call its 'Data.ByteString.Lazy.toStrict'?
+--
+-- Those all are instances of one pattern. They are conversions between
+-- representations of the same information. Codebases that don't attempt to
+-- abstract over this pattern tend to be sprawling with this type of
+-- boilerplate. It's noise to the codereader, it's a burden to the
+-- implementor and the maintainer.
+--
+-- = Why another conversion library?
+--
+-- Many libraries exist that approach the conversion problem. However all of
+-- them provide lawless typeclasses leaving it up to the author of the
+-- instance to define what makes a proper conversion. This results in
+-- inconsistencies across instances and their behaviour being not evident to
+-- the user.
+--
+-- This library tackles this problem with a lawful typeclass, making it
+-- evident what any of its instances do.
+--
+-- = The law
+--
+-- The key insight of this library is that if you add a requirement for the
+-- conversion to be lossless and to have a mirror conversion in the opposite
+-- direction, there usually appears to be only one way of defining it. That
+-- makes it very clear what the conversion does to the user and how to define
+-- it to the author of the conversion.
+--
+-- That insight itself stems from an observation that almost all of the
+-- practical conversions in Haskell share a property: you can restore the
+-- original data from its converted form. E.g., you can get a bytestring from
+-- a builder and you can create a builder from a bytestring, you can convert
+-- a text into a list of chars and vice-versa, bytestring to\/from bytearray,
+-- strict bytestring to\/from lazy, list to\/from sequence, sequence to/from
+-- vector, set of ints to\/from int-set. In other words, it's always a two-way
+-- street with them and there's a lot of instances of this pattern.
+--
+-- = UX
+--
+-- A few other accidental findings like encoding this property with recursive
+-- typeclass constraints and fine-tuning for the use of
+-- the @TypeApplications@ extension resulted in a very terse yet clear API.
+--
+-- Essentially the whole API is just two functions: 'to' and 'from'. Both
+-- perform a conversion between two types. The only difference between them
+-- is in what the first type application parameter specifies. E.g.:
+--
+-- > fromString = from @String
+--
+-- > toText = to @Text
+--
+-- In other words 'to' and 'from' let you explicitly specify either the source
+-- or the target type of a conversion when you need to help the type
+-- inferencer.
+--
+-- Here are more practical examples:
+--
+-- @
+-- renderNameAndHeight :: 'Text' -> 'Int' -> 'Text'
+-- renderNameAndHeight name height =
+--   'from' @'TextLazyBuilder.Builder' $
+--     "Height of " <> 'to' name <> " is " <> 'showAs' height
+-- @
+--
+-- @
+-- combineEncodings :: 'ByteStringShort.ShortByteString' -> 'PrimitiveByteArray.ByteArray' -> 'ByteString' -> [Word8]
+-- combineEncodings a b c =
+--   'from' @'ByteStringBuilder.Builder' $
+--     'to' a <> 'to' b <> 'to' c
+-- @
+module IsomorphismClass
+  ( -- * Typeclass
+    IsomorphicTo (..),
+    from,
+
+    -- * Common Utilities
+    showAs,
+  )
+where
+
+import qualified Data.ByteString as ByteString
+import qualified Data.ByteString.Builder as ByteStringBuilder
+import qualified Data.ByteString.Lazy as ByteStringLazy
+import qualified Data.ByteString.Short as ByteStringShort
+import qualified Data.ByteString.Short.Internal as ByteStringShortInternal
+import qualified Data.Primitive.ByteArray as PrimitiveByteArray
+import qualified Data.Sequence as Seq
+import qualified Data.Set as Set
+import qualified Data.Text as Text
+import qualified Data.Text.Array as TextArray
+import qualified Data.Text.Lazy as TextLazy
+import qualified Data.Text.Lazy.Builder as TextLazyBuilder
+import qualified Data.Vector as Vector
+import qualified Data.Vector.Generic as VectorGeneric
+import IsomorphismClass.Prelude
+import qualified IsomorphismClass.TextCompat.Array as TextCompatArray
+
+-- | Bidirectional conversion between two types with no loss of information.
+-- The bidirectionality is encoded via a recursive dependency with arguments
+-- flipped.
+--
+-- You can read the signature @IsomorphicTo a b@ as \"/B/ is isomorphic to /A/\".
+--
+-- __Laws__
+--
+-- /A/ is isomorphic to /B/ if and only if there exists a conversion from /A/
+-- to /B/ ('to') and a conversion from /B/ to /A/ ('from') such that:
+--
+-- - @'from' . 'to' = 'id'@ - For all values of /A/ converting from /A/ to /B/
+--     and then converting from /B/ to /A/ produces a value that is identical
+--     to the original.
+--
+-- - @'to' . 'from' = 'id'@ - For all values of /B/ converting from /B/ to /A/
+--     and then converting from /A/ to /B/ produces a value that is identical
+--     to the original.
+--
+-- __Usage__
+--
+-- This class is particularly easy to use in combination with
+-- the @TypeApplications@ extension making it clear to the reader what sort
+-- of conversion he sees. E.g.,
+--
+-- > fromString = from @String
+--
+-- > toText = to @Text
+--
+-- The types are also self-evident:
+--
+-- > > :t from @String
+-- > from @String :: IsomorphicTo b String => String -> b
+--
+-- > > :t to @Text
+-- > to @Text :: IsomorphicTo Text b => b -> Text
+--
+-- __Instance Definition__
+--
+-- For each pair of isomorphic types (/A/ and /B/) the compiler will require
+-- you to define two instances, namely: @IsomorphicTo A B@ and @IsomorphicTo
+-- B A@.
+class IsomorphicTo b a => IsomorphicTo a b where
+  to :: b -> a
+
+--
+
+instance IsomorphicTo String Text where
+  to = Text.unpack
+
+instance IsomorphicTo String TextLazy.Text where
+  to = TextLazy.unpack
+
+instance IsomorphicTo String TextLazyBuilder.Builder where
+  to = TextLazy.unpack . TextLazyBuilder.toLazyText
+
+--
+
+instance IsomorphicTo [Word8] ByteString where
+  to = ByteString.unpack
+
+instance IsomorphicTo [Word8] ByteStringLazy.ByteString where
+  to = ByteStringLazy.unpack
+
+instance IsomorphicTo [Word8] ByteStringShort.ShortByteString where
+  to = ByteStringShort.unpack
+
+instance IsomorphicTo [Word8] ByteStringBuilder.Builder where
+  to = ByteStringLazy.unpack . ByteStringBuilder.toLazyByteString
+
+instance IsomorphicTo [Word8] PrimitiveByteArray.ByteArray where
+  to = toList
+
+instance IsomorphicTo [Word8] TextArray.Array where
+  to = to . to @ByteStringShort.ShortByteString
+
+--
+
+instance IsomorphicTo [a] [a] where
+  to = id
+
+instance IsomorphicTo [a] (Vector a) where
+  to = toList
+
+instance IsomorphicTo [a] (Seq a) where
+  to = toList
+
+--
+
+instance IsomorphicTo Text Text where
+  to = id
+
+instance IsomorphicTo Text String where
+  to = Text.pack
+
+instance IsomorphicTo Text TextLazy.Text where
+  to = TextLazy.toStrict
+
+instance IsomorphicTo Text TextLazyBuilder.Builder where
+  to = TextLazy.toStrict . TextLazyBuilder.toLazyText
+
+--
+
+instance IsomorphicTo TextLazy.Text TextLazy.Text where
+  to = id
+
+instance IsomorphicTo TextLazy.Text String where
+  to = TextLazy.pack
+
+instance IsomorphicTo TextLazy.Text Text where
+  to = TextLazy.fromStrict
+
+instance IsomorphicTo TextLazy.Text TextLazyBuilder.Builder where
+  to = TextLazyBuilder.toLazyText
+
+--
+
+instance IsomorphicTo TextLazyBuilder.Builder TextLazyBuilder.Builder where
+  to = id
+
+instance IsomorphicTo TextLazyBuilder.Builder String where
+  to = TextLazyBuilder.fromString
+
+instance IsomorphicTo TextLazyBuilder.Builder Text where
+  to = TextLazyBuilder.fromText
+
+instance IsomorphicTo TextLazyBuilder.Builder TextLazy.Text where
+  to = TextLazyBuilder.fromLazyText
+
+--
+
+instance IsomorphicTo ByteString ByteString where
+  to = id
+
+instance IsomorphicTo ByteString [Word8] where
+  to = ByteString.pack
+
+instance IsomorphicTo ByteString ByteStringLazy.ByteString where
+  to = ByteStringLazy.toStrict
+
+instance IsomorphicTo ByteString ByteStringShort.ShortByteString where
+  to = ByteStringShort.fromShort
+
+instance IsomorphicTo ByteString ByteStringBuilder.Builder where
+  to = ByteStringLazy.toStrict . ByteStringBuilder.toLazyByteString
+
+instance IsomorphicTo ByteString PrimitiveByteArray.ByteArray where
+  to = to . to @ByteStringShort.ShortByteString
+
+instance IsomorphicTo ByteString TextArray.Array where
+  to = to . to @ByteStringShort.ShortByteString
+
+--
+
+instance IsomorphicTo ByteStringLazy.ByteString ByteStringLazy.ByteString where
+  to = id
+
+instance IsomorphicTo ByteStringLazy.ByteString [Word8] where
+  to = ByteStringLazy.pack
+
+instance IsomorphicTo ByteStringLazy.ByteString ByteString where
+  to = ByteStringLazy.fromStrict
+
+instance IsomorphicTo ByteStringLazy.ByteString ByteStringShort.ShortByteString where
+  to = from @ByteString . to
+
+instance IsomorphicTo ByteStringLazy.ByteString ByteStringBuilder.Builder where
+  to = ByteStringBuilder.toLazyByteString
+
+instance IsomorphicTo ByteStringLazy.ByteString PrimitiveByteArray.ByteArray where
+  to = to . to @ByteStringShort.ShortByteString
+
+instance IsomorphicTo ByteStringLazy.ByteString TextArray.Array where
+  to = to . to @ByteStringShort.ShortByteString
+
+--
+
+instance IsomorphicTo ByteStringShort.ShortByteString ByteStringShort.ShortByteString where
+  to = id
+
+instance IsomorphicTo ByteStringShort.ShortByteString [Word8] where
+  to = ByteStringShort.pack
+
+instance IsomorphicTo ByteStringShort.ShortByteString ByteString where
+  to = ByteStringShort.toShort
+
+instance IsomorphicTo ByteStringShort.ShortByteString ByteStringLazy.ByteString where
+  to = to . to @ByteString
+
+instance IsomorphicTo ByteStringShort.ShortByteString ByteStringBuilder.Builder where
+  to = to . to @ByteStringLazy.ByteString
+
+instance IsomorphicTo ByteStringShort.ShortByteString PrimitiveByteArray.ByteArray where
+  to (PrimitiveByteArray.ByteArray array) = ByteStringShortInternal.SBS array
+
+instance IsomorphicTo ByteStringShort.ShortByteString TextArray.Array where
+  to a = ByteStringShortInternal.SBS (TextCompatArray.toUnliftedByteArray a)
+
+--
+
+instance IsomorphicTo ByteStringBuilder.Builder ByteStringBuilder.Builder where
+  to = id
+
+instance IsomorphicTo ByteStringBuilder.Builder [Word8] where
+  to = to . to @ByteString
+
+instance IsomorphicTo ByteStringBuilder.Builder ByteString where
+  to = ByteStringBuilder.byteString
+
+instance IsomorphicTo ByteStringBuilder.Builder ByteStringLazy.ByteString where
+  to = ByteStringBuilder.lazyByteString
+
+instance IsomorphicTo ByteStringBuilder.Builder ByteStringShort.ShortByteString where
+  to = ByteStringBuilder.shortByteString
+
+instance IsomorphicTo ByteStringBuilder.Builder PrimitiveByteArray.ByteArray where
+  to = to . to @ByteStringShort.ShortByteString
+
+instance IsomorphicTo ByteStringBuilder.Builder TextArray.Array where
+  to = to . to @ByteStringShort.ShortByteString
+
+--
+
+instance IsomorphicTo PrimitiveByteArray.ByteArray PrimitiveByteArray.ByteArray where
+  to = id
+
+instance IsomorphicTo PrimitiveByteArray.ByteArray [Word8] where
+  to = fromList
+
+instance IsomorphicTo PrimitiveByteArray.ByteArray ByteStringShort.ShortByteString where
+  to (ByteStringShortInternal.SBS array) = PrimitiveByteArray.ByteArray array
+
+instance IsomorphicTo PrimitiveByteArray.ByteArray ByteString where
+  to = to . to @ByteStringShort.ShortByteString
+
+instance IsomorphicTo PrimitiveByteArray.ByteArray ByteStringLazy.ByteString where
+  to = to . to @ByteStringShort.ShortByteString
+
+instance IsomorphicTo PrimitiveByteArray.ByteArray ByteStringBuilder.Builder where
+  to = to . to @ByteStringShort.ShortByteString
+
+instance IsomorphicTo PrimitiveByteArray.ByteArray TextArray.Array where
+  to a = PrimitiveByteArray.ByteArray (TextCompatArray.toUnliftedByteArray a)
+
+--
+
+instance IsomorphicTo TextArray.Array [Word8] where
+  to = to . to @ByteStringShort.ShortByteString
+
+instance IsomorphicTo TextArray.Array PrimitiveByteArray.ByteArray where
+  to (PrimitiveByteArray.ByteArray arr) = TextCompatArray.fromUnliftedByteArray arr
+
+instance IsomorphicTo TextArray.Array ByteStringShort.ShortByteString where
+  to (ByteStringShortInternal.SBS arr) = TextCompatArray.fromUnliftedByteArray arr
+
+instance IsomorphicTo TextArray.Array ByteString where
+  to = to . to @ByteStringShort.ShortByteString
+
+instance IsomorphicTo TextArray.Array ByteStringLazy.ByteString where
+  to = to . to @ByteStringShort.ShortByteString
+
+instance IsomorphicTo TextArray.Array ByteStringBuilder.Builder where
+  to = to . to @ByteStringShort.ShortByteString
+
+--
+
+instance IsomorphicTo (Vector a) (Vector a) where
+  to = id
+
+instance IsomorphicTo (Vector a) [a] where
+  to = Vector.fromList
+
+instance IsomorphicTo (Vector a) (Seq a) where
+  to = from @[a] . to
+
+--
+
+instance IsomorphicTo (Seq a) (Seq a) where
+  to = id
+
+instance IsomorphicTo (Seq a) [a] where
+  to = Seq.fromList
+
+instance IsomorphicTo (Seq a) (Vector a) where
+  to = from @[a] . to
+
+--
+
+instance IsomorphicTo (Set a) (Set a) where
+  to = id
+
+instance IsomorphicTo (Set Int) IntSet where
+  to = fromList . toList
+
+--
+
+instance IsomorphicTo IntSet IntSet where
+  to = id
+
+instance IsomorphicTo IntSet (Set Int) where
+  to = fromList . toList
+
+--
+
+instance IsomorphicTo (Map k v) (Map k v) where
+  to = id
+
+instance IsomorphicTo (Map Int v) (IntMap v) where
+  to = fromList . toList
+
+--
+
+instance IsomorphicTo (IntMap a) (IntMap a) where
+  to = id
+
+instance IsomorphicTo (IntMap v) (Map Int v) where
+  to = fromList . toList
+
+--
+
+instance IsomorphicTo (Maybe a) (Maybe a) where to = id
+
+instance IsomorphicTo (Either a b) (Either a b) where to = id
+
+instance IsomorphicTo (First a) (First a) where to = id
+
+instance IsomorphicTo (Last a) (Last a) where to = id
+
+instance IsomorphicTo (Product a) (Product a) where to = id
+
+instance IsomorphicTo (Sum a) (Sum a) where to = id
+
+--
+
+instance IsomorphicTo Bool Bool where to = id
+
+instance IsomorphicTo Char Char where to = id
+
+instance IsomorphicTo Double Double where to = id
+
+instance IsomorphicTo Float Float where to = id
+
+instance IsomorphicTo Int Int where to = id
+
+instance IsomorphicTo Int Word where to = fromIntegral
+
+instance IsomorphicTo Int16 Int16 where to = id
+
+instance IsomorphicTo Int16 Word16 where to = fromIntegral
+
+instance IsomorphicTo Int32 Int32 where to = id
+
+instance IsomorphicTo Int32 Word32 where to = fromIntegral
+
+instance IsomorphicTo Int64 Int64 where to = id
+
+instance IsomorphicTo Int64 Word64 where to = fromIntegral
+
+instance IsomorphicTo Int8 Int8 where to = id
+
+instance IsomorphicTo Int8 Word8 where to = fromIntegral
+
+instance IsomorphicTo Integer Integer where to = id
+
+instance IsomorphicTo Rational Rational where to = id
+
+instance IsomorphicTo Word Int where to = fromIntegral
+
+instance IsomorphicTo Word Word where to = id
+
+instance IsomorphicTo Word16 Int16 where to = fromIntegral
+
+instance IsomorphicTo Word16 Word16 where to = id
+
+instance IsomorphicTo Word32 Int32 where to = fromIntegral
+
+instance IsomorphicTo Word32 Word32 where to = id
+
+instance IsomorphicTo Word64 Int64 where to = fromIntegral
+
+instance IsomorphicTo Word64 Word64 where to = id
+
+instance IsomorphicTo Word8 Int8 where to = fromIntegral
+
+instance IsomorphicTo Word8 Word8 where to = id
+
+--
+
+-- |
+-- 'to' in reverse direction.
+--
+-- Particularly useful in combination with the @TypeApplications@ extension,
+-- where it allows to specify the input type, e.g.:
+--
+-- > fromString :: IsomorphicTo a String => String -> a
+-- > fromString = from @String
+--
+-- The first type application of the 'to' function on the other hand specifies
+-- the output data type.
+from :: forall a b. IsomorphicTo b a => a -> b
+from = to
+
+-- |
+-- Ideally there should be a direct instance and this function
+-- should merely serve as a helper for defining instances
+-- by merely composing from other instances.
+--
+-- E.g.,
+--
+-- > thru @String Proxy
+--
+-- captures the following pattern:
+--
+-- > from @String . to
+--
+-- However it is advised to use the conversion functions directly,
+-- since it makes the intent clearer and is actually shorter.
+{-# INLINE thru #-}
+thru :: (IsomorphicTo a b, IsomorphicTo a c) => Proxy a -> b -> c
+thru proxy = from . flip asProxyTypeOf proxy . to
+
+{-# INLINE thruString #-}
+thruString :: (IsomorphicTo String a, IsomorphicTo String b) => a -> b
+thruString = from @String . to
+
+{-# INLINE thruText #-}
+thruText :: (IsomorphicTo Text a, IsomorphicTo Text b) => a -> b
+thruText = from @Text . to
+
+{-# INLINE thruList #-}
+thruList :: forall a f g. (IsomorphicTo [a] (f a), IsomorphicTo [a] (g a)) => f a -> g a
+thruList = from @[a] . to
+
+-- | A utility, which uses the 'Show' instance to produce a value
+-- that 'String' is isomorphic to.
+--
+-- It lets you generalize over the functions like the following:
+--
+-- > showAsText :: Show a => a -> Text
+-- > showAsText = showAs @Text
+--
+-- > showAsBuilder :: Show a => a -> Builder
+-- > showAsBuilder = showAs @Builder
+showAs :: forall b a. (IsomorphicTo String b, Show a) => a -> b
+showAs = from . show
diff --git a/library/IsomorphismClass/Prelude.hs b/library/IsomorphismClass/Prelude.hs
new file mode 100644
--- /dev/null
+++ b/library/IsomorphismClass/Prelude.hs
@@ -0,0 +1,89 @@
+module IsomorphismClass.Prelude
+  ( module Exports,
+  )
+where
+
+import Control.Applicative as Exports
+import Control.Arrow as Exports hiding (first, second)
+import Control.Category as Exports
+import Control.Concurrent as Exports
+import Control.Exception as Exports
+import Control.Monad as Exports hiding (fail, forM, forM_, mapM, mapM_, msum, sequence, sequence_)
+import Control.Monad.Fail as Exports
+import Control.Monad.Fix as Exports hiding (fix)
+import Control.Monad.IO.Class as Exports
+import Control.Monad.ST as Exports
+import Data.Bifunctor as Exports
+import Data.Bits as Exports
+import Data.Bool as Exports
+import Data.ByteString as Exports (ByteString)
+import Data.Char as Exports
+import Data.Coerce as Exports
+import Data.Complex as Exports
+import Data.Data as Exports
+import Data.Dynamic as Exports
+import Data.Either as Exports
+import Data.Fixed as Exports
+import Data.Foldable as Exports hiding (toList)
+import Data.Function as Exports hiding (id, (.))
+import Data.Functor as Exports
+import Data.Functor.Classes as Exports
+import Data.Functor.Compose as Exports
+import Data.Functor.Contravariant as Exports
+import Data.Functor.Identity as Exports
+import Data.HashMap.Strict as Exports (HashMap)
+import Data.HashSet as Exports (HashSet)
+import Data.Hashable as Exports
+import Data.IORef as Exports
+import Data.Int as Exports
+import Data.IntMap.Strict as Exports (IntMap)
+import Data.IntSet as Exports (IntSet)
+import Data.Ix as Exports
+import Data.List as Exports hiding (all, and, any, concat, concatMap, elem, find, foldl, foldl', foldl1, foldr, foldr1, mapAccumL, mapAccumR, maximum, maximumBy, minimum, minimumBy, notElem, or, product, sum)
+import Data.List.NonEmpty as Exports (NonEmpty (..))
+import Data.Map.Strict as Exports (Map)
+import Data.Maybe as Exports
+import Data.Monoid as Exports hiding (First (..), Last (..), (<>))
+import Data.Ord as Exports
+import Data.Proxy as Exports
+import Data.Ratio as Exports
+import Data.STRef as Exports
+import Data.Semigroup as Exports hiding (Option)
+import Data.Sequence as Exports (Seq)
+import Data.Set as Exports (Set)
+import Data.String as Exports
+import Data.Text as Exports (Text)
+import Data.Traversable as Exports
+import Data.Tuple as Exports
+import Data.Unique as Exports
+import Data.Vector as Exports (Vector)
+import Data.Version as Exports
+import Data.Void as Exports
+import Data.Word as Exports
+import Debug.Trace as Exports
+import Foreign.ForeignPtr as Exports
+import Foreign.Ptr as Exports
+import Foreign.StablePtr as Exports
+import Foreign.Storable as Exports
+import GHC.Conc as Exports hiding (orElse, threadWaitRead, threadWaitReadSTM, threadWaitWrite, threadWaitWriteSTM, withMVar)
+import GHC.Exts as Exports (IsList (..), groupWith, inline, lazy, sortWith)
+import GHC.Generics as Exports (Generic, Generic1)
+import GHC.IO.Exception as Exports
+import GHC.OverloadedLabels as Exports
+import GHC.Records as Exports
+import Numeric as Exports
+import Numeric.Natural as Exports
+import System.Environment as Exports
+import System.Exit as Exports
+import System.IO as Exports
+import System.IO.Error as Exports
+import System.IO.Unsafe as Exports
+import System.Mem as Exports
+import System.Mem.StableName as Exports
+import System.Timeout as Exports
+import Text.ParserCombinators.ReadP as Exports (ReadP, ReadS, readP_to_S, readS_to_P)
+import Text.ParserCombinators.ReadPrec as Exports (ReadPrec, readP_to_Prec, readPrec_to_P, readPrec_to_S, readS_to_Prec)
+import Text.Printf as Exports (hPrintf, printf)
+import Text.Read as Exports (Read (..), readEither, readMaybe)
+import Unsafe.Coerce as Exports
+import Prelude as Exports hiding (all, and, any, concat, concatMap, elem, fail, foldl, foldl1, foldr, foldr1, id, mapM, mapM_, maximum, minimum, notElem, or, product, sequence, sequence_, sum, (.))
diff --git a/library/IsomorphismClass/TextCompat/Array.hs b/library/IsomorphismClass/TextCompat/Array.hs
new file mode 100644
--- /dev/null
+++ b/library/IsomorphismClass/TextCompat/Array.hs
@@ -0,0 +1,23 @@
+{-# LANGUAGE CPP #-}
+
+module IsomorphismClass.TextCompat.Array where
+
+import Data.Text.Array
+import GHC.Exts (ByteArray#)
+import IsomorphismClass.Prelude
+
+{-# INLINE toUnliftedByteArray #-}
+toUnliftedByteArray :: Array -> ByteArray#
+#if MIN_VERSION_text(2,0,0)
+toUnliftedByteArray (ByteArray a) = a
+#else
+toUnliftedByteArray (Array a) = a
+#endif
+
+{-# INLINE fromUnliftedByteArray #-}
+fromUnliftedByteArray :: ByteArray# -> Array
+#if MIN_VERSION_text(2,0,0)
+fromUnliftedByteArray = ByteArray
+#else
+fromUnliftedByteArray = Array
+#endif
diff --git a/test/Main.hs b/test/Main.hs
new file mode 100644
--- /dev/null
+++ b/test/Main.hs
@@ -0,0 +1,132 @@
+module Main where
+
+import qualified Data.ByteString as ByteString
+import qualified Data.ByteString.Builder as ByteStringBuilder
+import qualified Data.ByteString.Lazy as ByteStringLazy
+import qualified Data.ByteString.Short as ByteStringShort
+import qualified Data.Primitive.ByteArray as PrimitiveByteArray
+import qualified Data.Sequence as Seq
+import qualified Data.Set as Set
+import qualified Data.Text as Text
+import qualified Data.Text.Lazy as TextLazy
+import qualified Data.Text.Lazy.Builder as TextLazyBuilder
+import qualified Data.Vector as Vector
+import qualified Data.Vector.Generic as VectorGeneric
+import IsomorphismClass
+import Rebase.Prelude
+import Test.ExtraInstances
+import Test.Tasty
+import Test.Tasty.HUnit
+import Test.Tasty.QuickCheck hiding ((.&.))
+
+main = defaultMain allTests
+
+allTests =
+  testGroup "All" $
+    [ testPair @String @Text Proxy Proxy,
+      testPair @String @TextLazy.Text Proxy Proxy,
+      testPair @String @TextLazyBuilder.Builder Proxy Proxy,
+      testPair @[Word8] @ByteString Proxy Proxy,
+      testPair @[Word8] @ByteStringLazy.ByteString Proxy Proxy,
+      testPair @[Word8] @ByteStringShort.ShortByteString Proxy Proxy,
+      testPair @[Word8] @ByteStringBuilder.Builder Proxy Proxy,
+      testPair @[Word8] @PrimitiveByteArray.ByteArray Proxy Proxy,
+      testPair @[Word8] @[Word8] Proxy Proxy,
+      testPair @[Word8] @(Vector Word8) Proxy Proxy,
+      testPair @[Word8] @(Seq Word8) Proxy Proxy,
+      testPair @Text @Text Proxy Proxy,
+      testPair @Text @String Proxy Proxy,
+      testPair @Text @TextLazy.Text Proxy Proxy,
+      testPair @Text @TextLazyBuilder.Builder Proxy Proxy,
+      testPair @TextLazy.Text @TextLazy.Text Proxy Proxy,
+      testPair @TextLazy.Text @String Proxy Proxy,
+      testPair @TextLazy.Text @Text Proxy Proxy,
+      testPair @TextLazy.Text @TextLazyBuilder.Builder Proxy Proxy,
+      testPair @TextLazyBuilder.Builder @TextLazyBuilder.Builder Proxy Proxy,
+      testPair @TextLazyBuilder.Builder @String Proxy Proxy,
+      testPair @TextLazyBuilder.Builder @Text Proxy Proxy,
+      testPair @TextLazyBuilder.Builder @TextLazy.Text Proxy Proxy,
+      testPair @ByteString @ByteString Proxy Proxy,
+      testPair @ByteString @[Word8] Proxy Proxy,
+      testPair @ByteString @ByteStringLazy.ByteString Proxy Proxy,
+      testPair @ByteString @ByteStringShort.ShortByteString Proxy Proxy,
+      testPair @ByteString @ByteStringBuilder.Builder Proxy Proxy,
+      testPair @ByteString @PrimitiveByteArray.ByteArray Proxy Proxy,
+      testPair @ByteStringLazy.ByteString @ByteStringLazy.ByteString Proxy Proxy,
+      testPair @ByteStringLazy.ByteString @[Word8] Proxy Proxy,
+      testPair @ByteStringLazy.ByteString @ByteString Proxy Proxy,
+      testPair @ByteStringLazy.ByteString @ByteStringShort.ShortByteString Proxy Proxy,
+      testPair @ByteStringLazy.ByteString @ByteStringBuilder.Builder Proxy Proxy,
+      testPair @ByteStringLazy.ByteString @PrimitiveByteArray.ByteArray Proxy Proxy,
+      testPair @ByteStringShort.ShortByteString @ByteStringShort.ShortByteString Proxy Proxy,
+      testPair @ByteStringShort.ShortByteString @[Word8] Proxy Proxy,
+      testPair @ByteStringShort.ShortByteString @ByteString Proxy Proxy,
+      testPair @ByteStringShort.ShortByteString @ByteStringLazy.ByteString Proxy Proxy,
+      testPair @ByteStringShort.ShortByteString @ByteStringBuilder.Builder Proxy Proxy,
+      testPair @ByteStringShort.ShortByteString @PrimitiveByteArray.ByteArray Proxy Proxy,
+      testPair @ByteStringBuilder.Builder @ByteStringBuilder.Builder Proxy Proxy,
+      testPair @ByteStringBuilder.Builder @[Word8] Proxy Proxy,
+      testPair @ByteStringBuilder.Builder @ByteString Proxy Proxy,
+      testPair @ByteStringBuilder.Builder @ByteStringLazy.ByteString Proxy Proxy,
+      testPair @ByteStringBuilder.Builder @ByteStringShort.ShortByteString Proxy Proxy,
+      testPair @ByteStringBuilder.Builder @PrimitiveByteArray.ByteArray Proxy Proxy,
+      testPair @PrimitiveByteArray.ByteArray @PrimitiveByteArray.ByteArray Proxy Proxy,
+      testPair @PrimitiveByteArray.ByteArray @[Word8] Proxy Proxy,
+      testPair @PrimitiveByteArray.ByteArray @ByteStringShort.ShortByteString Proxy Proxy,
+      testPair @PrimitiveByteArray.ByteArray @ByteString Proxy Proxy,
+      testPair @PrimitiveByteArray.ByteArray @ByteStringLazy.ByteString Proxy Proxy,
+      testPair @PrimitiveByteArray.ByteArray @ByteStringBuilder.Builder Proxy Proxy,
+      testPair @(Vector Word8) @(Vector Word8) Proxy Proxy,
+      testPair @(Vector Word8) @[Word8] Proxy Proxy,
+      testPair @(Vector Word8) @(Seq Word8) Proxy Proxy,
+      testPair @(Seq Word8) @(Seq Word8) Proxy Proxy,
+      testPair @(Seq Word8) @[Word8] Proxy Proxy,
+      testPair @(Seq Word8) @(Vector Word8) Proxy Proxy,
+      testPair @(Set Word8) @(Set Word8) Proxy Proxy,
+      testPair @(Set Int) @IntSet Proxy Proxy,
+      testPair @IntSet @IntSet Proxy Proxy,
+      testPair @IntSet @(Set Int) Proxy Proxy,
+      testPair @(Map Word8 Word8) @(Map Word8 Word8) Proxy Proxy,
+      testPair @(Map Int Word8) @(IntMap Word8) Proxy Proxy,
+      testPair @(IntMap Word8) @(IntMap Word8) Proxy Proxy,
+      testPair @(IntMap Word8) @(Map Int Word8) Proxy Proxy,
+      testPair @(Maybe Word8) @(Maybe Word8) Proxy Proxy,
+      testPair @(Either Word8 Word8) @(Either Word8 Word8) Proxy Proxy,
+      testPair @(First Word8) @(First Word8) Proxy Proxy,
+      testPair @(Last Word8) @(Last Word8) Proxy Proxy,
+      testPair @(Product Word8) @(Product Word8) Proxy Proxy,
+      testPair @(Sum Word8) @(Sum Word8) Proxy Proxy,
+      testPair @Bool @Bool Proxy Proxy,
+      testPair @Char @Char Proxy Proxy,
+      testPair @Double @Double Proxy Proxy,
+      testPair @Float @Float Proxy Proxy,
+      testPair @Int @Int Proxy Proxy,
+      testPair @Int @Word Proxy Proxy,
+      testPair @Int16 @Int16 Proxy Proxy,
+      testPair @Int16 @Word16 Proxy Proxy,
+      testPair @Int32 @Int32 Proxy Proxy,
+      testPair @Int32 @Word32 Proxy Proxy,
+      testPair @Int64 @Int64 Proxy Proxy,
+      testPair @Int64 @Word64 Proxy Proxy,
+      testPair @Int8 @Int8 Proxy Proxy,
+      testPair @Int8 @Word8 Proxy Proxy,
+      testPair @Integer @Integer Proxy Proxy,
+      testPair @Rational @Rational Proxy Proxy,
+      testPair @Word @Int Proxy Proxy,
+      testPair @Word @Word Proxy Proxy,
+      testPair @Word16 @Int16 Proxy Proxy,
+      testPair @Word16 @Word16 Proxy Proxy,
+      testPair @Word32 @Int32 Proxy Proxy,
+      testPair @Word32 @Word32 Proxy Proxy,
+      testPair @Word64 @Int64 Proxy Proxy,
+      testPair @Word64 @Word64 Proxy Proxy,
+      testPair @Word8 @Int8 Proxy Proxy,
+      testPair @Word8 @Word8 Proxy Proxy
+    ]
+
+testPair :: forall a b. (IsomorphicTo a b, Eq a, Arbitrary a, Show a, Typeable a, Typeable b) => Proxy a -> Proxy b -> TestTree
+testPair _ _ =
+  testProperty name $ \a ->
+    a === from @b (from @a a)
+  where
+    name = show (typeOf (undefined :: a)) <> "/" <> show (typeOf (undefined :: b))
diff --git a/test/Test/ExtraInstances.hs b/test/Test/ExtraInstances.hs
new file mode 100644
--- /dev/null
+++ b/test/Test/ExtraInstances.hs
@@ -0,0 +1,46 @@
+module Test.ExtraInstances () where
+
+import qualified Data.ByteString as ByteString
+import qualified Data.ByteString.Builder as ByteStringBuilder
+import qualified Data.ByteString.Lazy as ByteStringLazy
+import qualified Data.ByteString.Short as ByteStringShort
+import qualified Data.HashSet as HashSet
+import qualified Data.Primitive.ByteArray as PrimitiveByteArray
+import qualified Data.Sequence as Seq
+import qualified Data.Set as Set
+import qualified Data.Text as Text
+import qualified Data.Text.Lazy as TextLazy
+import qualified Data.Text.Lazy.Builder as TextLazyBuilder
+import qualified Data.Vector as Vector
+import qualified Data.Vector.Generic as VectorGeneric
+import qualified Data.Vector.Primitive as VectorPrimitive
+import qualified Data.Vector.Storable as VectorStorable
+import qualified Data.Vector.Unboxed as VectorUnboxed
+import Rebase.Prelude
+import Test.QuickCheck
+import Test.QuickCheck.Instances
+
+instance (Arbitrary a, VectorPrimitive.Prim a, Show a) => Arbitrary (VectorPrimitive.Vector a) where
+  arbitrary = arbitraryVector
+  shrink = shrinkVector
+
+instance Arbitrary PrimitiveByteArray.ByteArray where
+  arbitrary = fromList <$> arbitrary
+  shrink = fmap fromList . shrink . toList
+
+instance Arbitrary TextLazyBuilder.Builder where
+  arbitrary = TextLazyBuilder.fromText <$> arbitrary
+  shrink = shrinkMap TextLazyBuilder.fromLazyText TextLazyBuilder.toLazyText
+
+instance Eq ByteStringBuilder.Builder where
+  (==) = on (==) ByteStringBuilder.toLazyByteString
+
+instance Arbitrary ByteStringBuilder.Builder where
+  arbitrary = ByteStringBuilder.byteString <$> arbitrary
+  shrink = shrinkMap ByteStringBuilder.lazyByteString ByteStringBuilder.toLazyByteString
+
+arbitraryVector :: (VectorGeneric.Vector v a, Arbitrary a, Show a) => Gen (v a)
+arbitraryVector = VectorGeneric.fromList <$> arbitrary
+
+shrinkVector :: (VectorGeneric.Vector v a, Arbitrary a) => v a -> [v a]
+shrinkVector = fmap VectorGeneric.fromList . shrink . VectorGeneric.toList
