diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,26 @@
+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 Author name here 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/README.rst b/README.rst
new file mode 100644
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,7 @@
+Utilities for ``Control.Lens.Cons``
+===================================
+
+This library provides a few utility types and functions for working
+with ``Control.Lens.Cons``.  Rewrite rules are employed to make
+common conversions perform as well as the "native" but non-generic
+conversion (e.g. ``String`` to ``Text``, lazy to strict, etc).
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/concise.cabal b/concise.cabal
new file mode 100644
--- /dev/null
+++ b/concise.cabal
@@ -0,0 +1,52 @@
+name:                concise
+version:             0.1.0.0
+synopsis:            Utilities for Control.Lens.Cons
+description:
+  concise provides a handful of functions to extend what you can
+  do with Control.Lens.Cons.
+
+homepage:            https://github.com/frasertweedal/hs-concise
+bug-reports:         https://github.com/frasertweedal/hs-concise/issues
+license:             BSD3
+license-file:        LICENSE
+author:              Fraser Tweedale
+maintainer:          frase@frase.id.au
+copyright:           Copyright (C) 2016  Fraser Tweedale
+category:            Data, Lenses
+build-type:          Simple
+extra-source-files:  README.rst
+cabal-version:       >=1.10
+
+source-repository head
+  type: git
+  location: https://github.com/frasertweedale/hs-concise
+
+library
+  hs-source-dirs:      src
+  default-language:    Haskell2010
+  ghc-options:  -O2 -Wall
+  exposed-modules:
+    Control.Lens.Cons.Extras
+  build-depends:
+    base >= 4 && < 5
+    , lens
+    , text
+    , bytestring
+
+test-suite tests
+  default-language:    Haskell2010
+  type: exitcode-stdio-1.0
+  ghc-options: -O2 -threaded
+  hs-source-dirs: test
+  main-is: Prop.hs
+  build-depends:
+    base >= 4 && < 5
+    , bytestring
+    , lens
+    , text
+    , concise
+
+    , tasty
+    , tasty-quickcheck
+    , QuickCheck
+    , quickcheck-instances
diff --git a/src/Control/Lens/Cons/Extras.hs b/src/Control/Lens/Cons/Extras.hs
new file mode 100644
--- /dev/null
+++ b/src/Control/Lens/Cons/Extras.hs
@@ -0,0 +1,106 @@
+-- This file is part of hs-concise
+-- Copyright (C) 2016  Fraser Tweedale
+--
+-- 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 Author name here 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.
+
+{-# LANGUAGE NoImplicitPrelude #-}
+
+module Control.Lens.Cons.Extras
+  (
+    recons
+  , unfoldr
+  ) where
+
+import Data.Function (id)
+import Data.Maybe (Maybe)
+import Data.String (String)
+import Data.Word (Word8)
+
+import Control.Lens
+import Control.Lens ((#))
+import Control.Lens.Cons (Cons, cons, uncons)
+import Control.Lens.Empty (AsEmpty(..))
+import Control.Lens.Fold (foldrOf, unfolded)
+
+import Control.Lens.Iso (lazy, strict)
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Lazy as L
+import Data.ByteString.Lens (packedBytes, unpackedBytes)
+import qualified Data.Text as T
+import Data.Text.Lens (packed, unpacked)
+import qualified Data.Text.Lazy as TL
+
+-- | Convert one type with a 'Cons' instance into the other.
+--
+-- Rewrite rules are provided for efficient conversion between
+-- 'String' and 'Text', @['Word8']@ and 'ByteString', and lazy and
+-- strict 'Text' and 'ByteString'.  Programs must be compiled
+-- with @-O@ to use them.
+--
+-- Although the type does not prove it, if @(recons . recons)@
+-- exists it should obey:
+--
+-- > recons . recons ≡ id
+--
+{-# NOINLINE [2] recons #-}
+recons :: (Cons s1 s1 a a, Cons s2 s2 a a, AsEmpty s2) => Getter s1 s2
+recons = to (unfoldr uncons)
+
+-- | > unfoldr f = foldrOf (unfolded f) cons (_Empty # ())
+--
+unfoldr :: (Cons s2 s2 a a, AsEmpty s2) => (s1 -> Maybe (a, s1)) -> s1 -> s2
+unfoldr f = foldrOf (unfolded f) cons (_Empty # ())
+
+{-# RULES
+"recons/id"
+  recons = id
+"recons/string-text"
+  recons = packed :: Getter String T.Text
+"recons/text-string"
+  recons = unpacked :: Getter T.Text String
+"recons/string-lazytext"
+  recons = packed :: Getter String TL.Text
+"recons/lazytext-string"
+  recons = unpacked :: Getter TL.Text String
+"recons/text-strict"
+  recons = strict :: Getter TL.Text T.Text
+"recons/text-lazy"
+  recons = lazy :: Getter T.Text TL.Text
+"recons/list-bs"
+  recons = packedBytes :: Getter [Word8] B.ByteString
+"recons/bs-list"
+  recons = unpackedBytes :: Getter B.ByteString [Word8]
+"recons/list-lazybs"
+  recons = packedBytes :: Getter [Word8] L.ByteString
+"recons/lazybs-list"
+  recons = unpackedBytes :: Getter L.ByteString [Word8]
+"recons/bs-strict"
+  recons = strict :: Getter L.ByteString B.ByteString
+"recons/bs-lazy"
+  recons = lazy :: Getter B.ByteString L.ByteString
+ #-}
diff --git a/test/Prop.hs b/test/Prop.hs
new file mode 100644
--- /dev/null
+++ b/test/Prop.hs
@@ -0,0 +1,54 @@
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+
+{-# OPTIONS_GHC -ddump-rule-rewrites #-}
+
+import Data.Word (Word8)
+
+import Control.Lens (AsEmpty(..), Cons(..), prism, view)
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Lazy as L
+import qualified Data.Text as T
+import qualified Data.Text.Lazy as TL
+
+import Test.QuickCheck
+import Test.QuickCheck.Instances ()
+import Test.Tasty
+import Test.Tasty.QuickCheck
+
+import Control.Lens.Cons.Extras
+
+main :: IO ()
+main = defaultMain $ testGroup "Properties"
+  [ testProperty "[a] -> [a]" (\s -> view recons (s :: String) == s)
+  , testProperty "T -> T" (\s -> view recons (s :: T.Text) == s)
+  , testProperty "TL -> TL" (\s -> view recons (s :: TL.Text) == s)
+  , testProperty "B -> B" (\s -> view recons (s :: B.ByteString) == s)
+  , testProperty "L -> L" (\s -> view recons (s :: L.ByteString) == s)
+  , testProperty "Str -> T -> Str" (\s -> view recons (view recons (s :: String) :: T.Text) == s)
+  , testProperty "Str -> TL -> Str" (\s -> view recons (view recons (s :: String) :: TL.Text) == s)
+  , testProperty "T -> TL -> T" (\s -> view recons (view recons (s :: T.Text) :: TL.Text) == s)
+  , testProperty "[W8] -> B -> [W8]" (\s -> view recons (view recons (s :: [Word8]) :: B.ByteString) == s)
+  , testProperty "[W8] -> L -> [W8]" (\s -> view recons (view recons (s :: [Word8]) :: L.ByteString) == s)
+  , testProperty "B -> L -> B" (\s -> view recons (view recons (s :: B.ByteString) :: L.ByteString) == s)
+  , testProperty "[a] -> List a -> [a]" (\s -> view recons (view recons (s :: [Int]) :: List Int) == s)
+  ]
+
+-- Let's implement a type *without* rewrite rules to ensure that the
+-- default implementation is correct
+--
+data List a = Nil | Cons a (List a)
+  deriving (Eq)
+
+instance AsEmpty (List a) where
+  _Empty = prism (const Nil) f
+    where
+      f Nil = Right ()
+      f xs = Left xs
+
+instance Cons (List a) (List b) a b where
+  _Cons = prism (uncurry Cons) f
+    where
+      f Nil = Left Nil
+      f (Cons x xs) = Right (x, xs)
