diff --git a/ChangeLog.md b/ChangeLog.md
new file mode 100644
--- /dev/null
+++ b/ChangeLog.md
@@ -0,0 +1,5 @@
+# Revision history for control-iso
+
+## 0.1.0.0  -- YYYY-mm-dd
+
+* First version. Released on an unsuspecting world.
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2018, Isaac Shapira
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of Isaac Shapira nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/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/control-iso.cabal b/control-iso.cabal
new file mode 100644
--- /dev/null
+++ b/control-iso.cabal
@@ -0,0 +1,34 @@
+-- Initial control-iso.cabal generated by cabal init.  For further
+-- documentation, see http://haskell.org/cabal/users-guide/
+
+name:                control-iso
+version:             0.1.0.0
+synopsis: A typeclass for type isomorphisms
+description: This is a type class for type isomorphisms, it provides string conversions as well as for newtypes and common types
+license:             BSD3
+license-file:        LICENSE
+author:              Isaac Shapira
+maintainer:          fresheyeball@gmail.com
+-- copyright:
+category:            Control
+build-type:          Simple
+extra-source-files:  ChangeLog.md
+cabal-version:       >=1.10
+
+source-repository head
+  type: git
+  location: https://gitlab.com/fresheyeball/control-iso.git
+
+library
+  exposed-modules:    Control.Isomorphic
+  -- other-modules:
+  -- other-extensions:
+  ghc-options: -Wall
+  build-depends:       base                >= 4.9 && <4.10.2
+                     , bytestring          >= 0.10.8 && < 0.11
+                     , newtype-generics    >= 0.5.1 && < 0.6
+                     , profunctors         >= 5.2.1 && < 5.3
+                     , text                >= 1.2.2 && < 1.3
+
+  hs-source-dirs:      src
+  default-language:    Haskell2010
diff --git a/src/Control/Isomorphic.hs b/src/Control/Isomorphic.hs
new file mode 100644
--- /dev/null
+++ b/src/Control/Isomorphic.hs
@@ -0,0 +1,311 @@
+{-# LANGUAGE AllowAmbiguousTypes     #-}
+{-# LANGUAGE DeriveGeneric           #-}
+{-# LANGUAGE FlexibleContexts        #-}
+{-# LANGUAGE FlexibleInstances       #-}
+{-# LANGUAGE FunctionalDependencies  #-}
+{-# LANGUAGE LambdaCase              #-}
+{-# LANGUAGE MultiParamTypeClasses   #-}
+{-# LANGUAGE ScopedTypeVariables     #-}
+{-# LANGUAGE TypeApplications        #-}
+{-# LANGUAGE TypeFamilies            #-}
+{-# LANGUAGE TypeFamilyDependencies  #-}
+{-# LANGUAGE UndecidableSuperClasses #-}
+
+module Control.Isomorphic where
+
+import           Control.Applicative
+import           Control.Arrow
+import qualified Control.Monad.ST.Lazy      as SL
+import qualified Control.Monad.ST.Strict    as SS
+import qualified Control.Newtype            as N
+import qualified Data.ByteString            as BS
+import qualified Data.ByteString.Char8      as BSC
+import qualified Data.ByteString.Lazy       as BL
+import qualified Data.ByteString.Lazy.Char8 as BLC
+import           Data.Fixed
+import           Data.Functor.Compose
+import           Data.Functor.Identity
+import qualified Data.List.NonEmpty         as NE
+import           Data.Monoid
+import           Data.Profunctor            (Profunctor (..))
+import qualified Data.Text                  as TS
+import qualified Data.Text.Encoding         as TSE
+import qualified Data.Text.Lazy             as TL
+import qualified Data.Text.Lazy.Encoding    as TLE
+import           GHC.Generics               (C1, D1, Generic, K1 (..), M1 (..),
+                                             S1)
+
+
+class Isomorphic b a => Isomorphic a b where
+  {-|
+   This type class provides a single method `to'`, which is a __bijective__ function from `a` to `b`.
+   We leverage `UndecidableSuperClasses` to demand that every instance of `Isomorphic` have an inverse.
+   The sister instance (which may be the same instance) must have the follwing property when used with
+   the `to'`
+
+   @
+     to' . to' = id
+   @
+
+   Note, this class documents type isomorphisms specifically, where two types are isomorphic to one another.
+   It does not document endoisomorphisms.
+  -}
+  to' :: a -> b
+
+instance Isomorphic (D1 d (C1 c (S1 s (K1 i a))) a) a where
+  to' (M1 (M1 (M1 (K1 x)))) = x
+instance Isomorphic a (D1 d (C1 c (S1 s (K1 i a))) a) where
+  to' x = (M1 (M1 (M1 (K1 x))))
+
+-- | Composition of two bijections
+via :: forall b a c. (Isomorphic b c, Isomorphic a b) => a -> c
+via = from @b . to
+{-# INLINE via #-}
+
+{-| This is the perfer version of the `to'` function.
+    The order of arguments in @forall@ has been set up so that the codomain is first.
+    This is important to work with `TypeApplications`.
+
+    @
+      to @Text "hi there"
+    @
+
+    For call sites where we need to annotate.
+-}
+to :: forall b a. Isomorphic a b => a -> b
+to = to'
+{-# INLINE to #-}
+
+{-| This is the same as `to` but has the @forall@ arguments with @a@ first.
+-}
+from :: forall a b. Isomorphic a b => a -> b
+from = to'
+{-# INLINE from #-}
+
+instance {-# OVERLAPPABLE #-} Isomorphic a a where to' = id
+                                                   {-# INLINE to' #-}
+
+instance Isomorphic (a,b) (b,a) where to' (a,b) = (b,a)
+                                      {-# INLINABLE to' #-}
+
+
+instance Isomorphic (a,(b,c)) ((a,b),c) where to' (a,(b,c)) = ((a,b),c)
+                                              {-# INLINABLE to' #-}
+instance Isomorphic ((a,b),c) (a,(b,c)) where to' ((a,b),c) = (a,(b,c))
+                                              {-# INLINABLE to' #-}
+
+instance Isomorphic (a,b,c) (a,(b,c))   where to' (a,b,c) = (a,(b,c))
+                                              {-# INLINABLE to' #-}
+instance Isomorphic (a,(b,c)) (a,b,c)   where to' (a,(b,c)) = (a,b,c)
+                                              {-# INLINABLE to' #-}
+
+instance Isomorphic (a,b,c) ((a,b),c)   where to' (a,b,c) = ((a,b),c)
+                                              {-# INLINABLE to' #-}
+instance Isomorphic ((a,b),c) (a,b,c)   where to' ((a,b),c) = (a,b,c)
+                                              {-# INLINABLE to' #-}
+
+-- String -> BS BL TS TL
+
+instance Isomorphic BS.ByteString String where
+  to' = BSC.unpack
+  {-# INLINE to' #-}
+instance Isomorphic String BS.ByteString where
+  to' = BSC.pack
+  {-# INLINE to' #-}
+
+instance Isomorphic BL.ByteString String where
+  to' = BLC.unpack
+  {-# INLINE to' #-}
+instance Isomorphic String BL.ByteString where
+  to' = BLC.pack
+  {-# INLINE to' #-}
+
+instance Isomorphic TS.Text String where
+  to' = TS.unpack
+  {-# INLINE to' #-}
+instance Isomorphic String TS.Text where
+  to' = TS.pack
+  {-# INLINE to' #-}
+
+instance Isomorphic TL.Text String where
+  to' = TL.unpack
+  {-# INLINE to' #-}
+instance Isomorphic String TL.Text where
+  to' = TL.pack
+  {-# INLINE to' #-}
+
+-- TS -> BS BL TL
+
+instance Isomorphic TS.Text BS.ByteString where
+  to' = TSE.encodeUtf8
+  {-# INLINE to' #-}
+instance Isomorphic BS.ByteString TS.Text where
+  to' = TSE.decodeUtf8
+  {-# INLINE to' #-}
+
+instance Isomorphic TS.Text BL.ByteString where
+  to' = BL.fromStrict . to
+  {-# INLINE to' #-}
+instance Isomorphic BL.ByteString TS.Text where
+  to' = to' . BL.toStrict
+  {-# INLINE to' #-}
+
+instance Isomorphic TS.Text TL.Text where
+  to' = TL.fromStrict
+  {-# INLINE to' #-}
+instance Isomorphic TL.Text TS.Text where
+  to' = TL.toStrict
+  {-# INLINE to' #-}
+
+-- TL -> BS BL
+
+
+instance Isomorphic TL.Text BS.ByteString where
+  to' = BL.toStrict . to
+  {-# INLINE to' #-}
+instance Isomorphic BS.ByteString TL.Text where
+  to' = to' . BL.fromStrict
+  {-# INLINE to' #-}
+
+instance Isomorphic TL.Text BL.ByteString where
+  to' = TLE.encodeUtf8
+  {-# INLINE to' #-}
+instance Isomorphic BL.ByteString TL.Text where
+  to' = TLE.decodeUtf8
+  {-# INLINE to' #-}
+
+-- BS -> BL
+
+instance Isomorphic BS.ByteString BL.ByteString where
+  to' = BL.fromStrict
+  {-# INLINE to' #-}
+instance Isomorphic BL.ByteString BS.ByteString where
+  to' = BL.toStrict
+  {-# INLINE to' #-}
+
+instance Isomorphic (Maybe a) (Either () a) where
+  to' = \case Just a -> Right a; _ -> Left ()
+instance Isomorphic (Either () a) (Maybe a) where
+  to' = \case Right a -> Just a; _ -> Nothing
+
+instance Isomorphic (Maybe a) (Either a ()) where
+  to' = \case Just a -> Left a; _ -> Right ()
+instance Isomorphic (Either a ()) (Maybe a) where
+  to' = \case Left a -> Just a; _ -> Nothing
+
+instance Isomorphic (a -> b -> c) ((a,b) -> c) where to' = uncurry
+                                                     {-# INLINE to' #-}
+instance Isomorphic ((a,b) -> c) (a -> b -> c) where to' = curry
+                                                     {-# INLINE to' #-}
+
+instance Isomorphic (a -> b -> c) (b -> a -> c) where to' = flip
+                                                      {-# INLINE to' #-}
+
+instance Isomorphic (Either a b) (Either b a) where
+  to' = \case Right x -> Left x; Left x -> Right x
+
+instance Isomorphic (NE.NonEmpty a) (a, [a]) where to' (x NE.:| xs) = (x,xs)
+instance Isomorphic (a, [a]) (NE.NonEmpty a) where to' (x,xs) = x NE.:| xs
+
+-- | A wrapper for @Either () ()@ that decides the isomorphism to `Bool` is `Right` biased
+newtype IsRight = IsRight { unIsRight :: Either () () } deriving (Show, Generic)
+-- | A wrapper for @Either () ()@ that decides the isomorphism to `Bool` is `Left` biased
+newtype IsLeft  = IsLeft { unIsLeft :: Either () () } deriving (Show, Generic)
+instance Isomorphic IsRight (Either () ()) where to' = unIsRight
+instance Isomorphic (Either () ()) IsRight where to' = IsRight
+instance Isomorphic IsLeft (Either () ()) where to' = unIsLeft
+instance Isomorphic (Either () ()) IsLeft where to' = IsLeft
+instance Isomorphic Bool IsRight where
+   to' True = IsRight (Right ())
+   to' _    = IsRight (Left ())
+instance Isomorphic IsRight Bool where
+   to' (IsRight (Right ())) = True
+   to' _                    = False
+instance Isomorphic Bool IsLeft where
+   to' True = IsLeft (Left ())
+   to' _    = IsLeft (Right ())
+instance Isomorphic IsLeft Bool where
+   to' (IsLeft (Left ())) = True
+   to' _                  = False
+
+instance Enum a => Isomorphic a Int where to' = fromEnum
+                                          {-# INLINE to' #-}
+instance Enum a => Isomorphic Int a where to' = toEnum
+                                          {-# INLINE to' #-}
+
+instance Isomorphic (SL.ST s a) (SS.ST s a) where to' = SL.lazyToStrictST
+                                                  {-# INLINE to' #-}
+instance Isomorphic (SS.ST s a)  (SL.ST s a) where to' = SL.strictToLazyST
+                                                   {-# INLINE to' #-}
+
+-- | A wrapper for @Maybe ()@ that decides the isomorphism to `Bool` is `Just` biased
+newtype IsJust = IsJust { unIsJust :: Maybe () } deriving Show
+-- | A wrapper for @Maybe ()@ that decides the isomorphism to `Bool` is `Nothing` biased
+newtype IsNothing = IsNothing { unIsNothing :: Maybe () } deriving Show
+instance Isomorphic IsJust (Maybe ()) where to' = unIsJust
+instance Isomorphic (Maybe ()) IsJust where to' = IsJust
+instance Isomorphic IsNothing (Maybe ()) where to' = unIsNothing
+instance Isomorphic (Maybe ()) IsNothing  where to' = IsNothing
+instance Isomorphic Bool IsJust where
+   to' True = IsJust (Just ())
+   to' _    = IsJust Nothing
+instance Isomorphic IsJust Bool where
+   to' (IsJust (Just ())) = True
+   to' _                  = False
+instance Isomorphic Bool IsNothing where
+   to' True = IsNothing Nothing
+   to' _    = IsNothing (Just ())
+instance Isomorphic IsNothing Bool where
+   to' (IsNothing Nothing) = True
+   to' _                   = False
+
+instance Isomorphic (WrappedMonad m a) (m a) where to' = N.unpack
+instance Isomorphic (m a) (WrappedMonad m a) where to' = N.pack
+instance Isomorphic (WrappedArrow a b c) (a b c) where to' = N.unpack
+instance Isomorphic (a b c) (WrappedArrow a b c) where to' = N.pack
+instance Isomorphic (ZipList a) [a] where to' = N.unpack
+instance Isomorphic [a] (ZipList a) where to' = N.pack
+instance Isomorphic (Kleisli m a b) (a -> m b) where to' = N.unpack
+instance Isomorphic (a -> m b) (Kleisli m a b) where to' = N.pack
+instance Isomorphic (ArrowMonad a b) (a () b) where to' = N.unpack
+instance Isomorphic (a () b) (ArrowMonad a b) where to' = N.pack
+instance Isomorphic (Fixed a) Integer where to' = N.unpack
+instance Isomorphic Integer (Fixed a) where to' = N.pack
+instance Isomorphic (Compose f g a) (f (g a)) where to' = N.unpack
+instance Isomorphic (f (g a)) (Compose f g a) where to' = N.pack
+instance Isomorphic (Const a x) a where to' = N.unpack
+instance Isomorphic a (Const a x) where to' = N.pack
+instance Isomorphic (Identity a) a where to' = N.unpack
+instance Isomorphic a (Identity a) where to' = N.pack
+instance Isomorphic (Dual a) a where to' = N.unpack
+instance Isomorphic a (Dual a) where to' = N.pack
+instance Isomorphic (Endo a) (a -> a) where to' = N.unpack
+instance Isomorphic (a -> a) (Endo a) where to' = N.pack
+instance Isomorphic All Bool where to' = N.unpack
+instance Isomorphic Bool All where to' = N.pack
+instance Isomorphic Any Bool where to' = N.unpack
+instance Isomorphic Bool Any where to' = N.pack
+instance Isomorphic (Sum a) a where to' = N.unpack
+instance Isomorphic a (Sum a) where to' = N.pack
+instance Isomorphic (Product a) a where to' = N.unpack
+instance Isomorphic a (Product a) where to' = N.pack
+instance Isomorphic (First a) (Maybe a) where to' = N.unpack
+instance Isomorphic (Maybe a) (First a) where to' = N.pack
+instance Isomorphic (Last a) (Maybe a) where to' = N.unpack
+instance Isomorphic (Maybe a) (Last a) where to' = N.pack
+
+-- | lift a function to any types to which it is isomorphic
+as :: (Isomorphic b d, Isomorphic c a) => (a -> b) -> c -> d
+as f = to . f . to
+as2 :: (Isomorphic c f, Isomorphic d a, Isomorphic e b) => (a -> b -> c) -> d -> e -> f
+as2 f x y = to $ f (to x) (to y)
+as3 :: (Isomorphic d h, Isomorphic e a, Isomorphic f b, Isomorphic g c) => (a -> b -> c -> d) -> e -> f -> g -> h
+as3 f x y z = to $ f (to x) (to y) (to z)
+as4 :: (Isomorphic e j, Isomorphic f a, Isomorphic g b, Isomorphic h c, Isomorphic i d) => (a -> b -> c -> d -> e) -> f -> g -> h -> i -> j
+as4 f w x y z = to $ f (to w) (to x) (to y) (to z)
+as5 :: (Isomorphic f l, Isomorphic g a, Isomorphic h b, Isomorphic i c, Isomorphic j d, Isomorphic k e) => (a -> b -> c -> d -> e -> f) -> g -> h -> i -> j -> k -> l
+as5 f v w x y z = to $ f (to v) (to w) (to x) (to y) (to z)
+
+-- | A free lens @Iso s t a b@ from the `Isomorphic` instances
+isoBi :: (Profunctor p, Isomorphic s a, Isomorphic b t, Functor f) => p a (f b) -> p s (f t)
+isoBi = dimap to (fmap to)
