diff --git a/Data/Profunctor/Product.hs b/Data/Profunctor/Product.hs
new file mode 100644
--- /dev/null
+++ b/Data/Profunctor/Product.hs
@@ -0,0 +1,351 @@
+module Data.Profunctor.Product where
+
+import Prelude hiding (id)
+import Data.Profunctor (Profunctor, dimap, lmap, WrappedArrow)
+import Data.Functor.Contravariant (Contravariant, contramap)
+-- vv TODO: don't want to have to import all those explicitly.  What to do?
+import Data.Profunctor.Product.Flatten
+-- vv and these
+import Data.Profunctor.Product.Tuples
+import Control.Category (id)
+import Control.Arrow (Arrow, (***), (<<<), arr, (&&&))
+import Control.Applicative (Applicative, liftA2, pure)
+import Data.Monoid (Monoid, mempty, (<>))
+
+-- ProductProfunctor and ProductContravariant are potentially
+-- redundant type classes.  It seems to me that these are equivalent
+-- to Profunctor with Applicative, and Contravariant with Monoid
+-- respectively:
+--
+--    import Data.Profunctor
+--    import Control.Applicative hiding (empty)
+--    import Data.Functor.Contravariant
+--    import Data.Monoid
+--
+--    empty :: (Applicative (p ())) => p () ()
+--    empty = pure ()
+--
+--    (***!) :: (Applicative (p (a, a')), Profunctor p) =>
+--                p a b -> p a' b' -> p (a, a') (b, b')
+--    p ***! p' = (,) <$> lmap fst p <*> lmap snd p'
+--
+--    point :: Monoid (f ()) => f ()
+--    point = mempty
+--
+--    (***<) :: (Monoid (f (a, b)), Contravariant f) =>
+--                f a -> f b -> f (a, b)
+--    p ***< p' = contramap fst p <> contramap snd p'
+--
+--
+-- The only thing that makes me think that they are not *completely*
+-- redundant is that (***!) and (***<) have to be defined
+-- polymorphically in the type arguments, whereas if we took the
+-- Profunctor+Applicative or Contravariant+Monoid approach we do not
+-- have a guarantee that these operations are polymorphic.
+--
+-- Previously I wanted to replace ProductProfunctor and
+-- ProductContravariant entirely.  This proved difficult as it is not
+-- possible to expand the class constraints to require Applicative and
+-- Monoid respectively.  We can't enforce a constraint 'Applicative (p
+-- a)' where 'a' does not appear in the head.  This seems closely
+-- related to the above issue of adhoc implementations.
+--
+-- There is a potential method of working around this issue using the
+-- 'constraints' package:
+-- stackoverflow.com/questions/12718268/polymorphic-constraint/12718620
+--
+-- Still, at least we now have default implementations of the class
+-- methods, which makes things simpler.
+
+class Profunctor p => ProductProfunctor p where
+  empty :: p () ()
+  (***!) :: p a b -> p a' b' -> p (a, a') (b, b')
+
+class Contravariant f => ProductContravariant f where
+  point :: f ()
+  (***<) :: f a -> f b -> f (a, b)
+
+defaultEmpty :: Applicative (p ()) => p () ()
+defaultEmpty = pure ()
+
+defaultProfunctorProduct :: (Applicative (p (a, a')), Profunctor p)
+                  => p a b -> p a' b' -> p (a, a') (b, b')
+defaultProfunctorProduct p p' = liftA2 (,) (lmap fst p) (lmap snd p')
+
+defaultPoint :: Monoid (p ()) => p ()
+defaultPoint = mempty
+
+defaultContravariantProduct :: (Contravariant f, Monoid (f (a, b)))
+                               => f a -> f b -> f (a, b)
+defaultContravariantProduct p p' = contramap fst p <> contramap snd p'
+
+newtype PPOfContravariant f a b = PPOfContravariant (f a)
+
+unPPOfContravariant :: PPOfContravariant c a a -> c a
+unPPOfContravariant (PPOfContravariant pp) = pp
+
+instance Contravariant f => Profunctor (PPOfContravariant f) where
+  dimap f _ (PPOfContravariant p) = PPOfContravariant (contramap f p)
+
+instance ProductContravariant f => ProductProfunctor (PPOfContravariant f) where
+  empty = PPOfContravariant point
+  PPOfContravariant f ***! PPOfContravariant f' = PPOfContravariant (f ***< f')
+
+instance ProductProfunctor (->) where
+  empty = id
+  (***!) = (***)
+
+instance Arrow arr => ProductProfunctor (WrappedArrow arr) where
+  empty = id
+  (***!) = (***)
+
+data AndArrow arr z a b = AndArrow { runAndArrow :: arr z b }
+
+instance Arrow arr => Profunctor (AndArrow arr z) where
+  dimap _ f (AndArrow g) = AndArrow (arr f <<< g)
+
+instance Arrow arr => ProductProfunctor (AndArrow arr z) where
+  empty = AndArrow (arr (const ()))
+  (AndArrow f) ***! (AndArrow f') = AndArrow (f &&& f')
+
+pT0 :: ProductProfunctor p => T0 -> p T0 T0
+pT0 = const empty
+
+pT1 :: ProductProfunctor p => T1 (p a1 b1) -> p (T1 a1) (T1 b1)
+pT1 = id
+
+pT2 :: ProductProfunctor p => T2 (p a1 b1) (p a2 b2) -> p (T2 a1 a2) (T2 b1 b2)
+pT2 = uncurry (***!)
+
+chain :: ProductProfunctor p => (t -> p a2 b2) -> (p a1 b1, t)
+      -> p (a1, a2) (b1, b2)
+chain rest (a, as) = pT2 (a, rest as)
+
+pT3 :: ProductProfunctor p => T3 (p a1 b1) (p a2 b2) (p a3 b3)
+       -> p (T3 a1 a2 a3) (T3 b1 b2 b3)
+pT3 = chain pT2
+
+pT4 :: ProductProfunctor p => T4 (p a1 b1) (p a2 b2) (p a3 b3) (p a4 b4)
+       -> p (T4 a1 a2 a3 a4) (T4 b1 b2 b3 b4)
+pT4 = chain pT3
+
+pT5 :: ProductProfunctor p => T5 (p a1 b1) (p a2 b2) (p a3 b3) (p a4 b4)
+                                 (p a5 b5)
+       -> p (T5 a1 a2 a3 a4 a5) (T5 b1 b2 b3 b4 b5)
+pT5 = chain pT4
+
+pT6 :: ProductProfunctor p => T6 (p a1 b1) (p a2 b2) (p a3 b3) (p a4 b4)
+                                 (p a5 b5) (p a6 b6)
+       -> p (T6 a1 a2 a3 a4 a5 a6) (T6 b1 b2 b3 b4 b5 b6)
+pT6 = chain pT5
+
+pT7 :: ProductProfunctor p => T7 (p a1 b1) (p a2 b2) (p a3 b3) (p a4 b4)
+                                 (p a5 b5) (p a6 b6) (p a7 b7)
+       -> p (T7 a1 a2 a3 a4 a5 a6 a7) (T7 b1 b2 b3 b4 b5 b6 b7)
+pT7 = chain pT6
+
+pT8 :: ProductProfunctor p => T8 (p a1 b1) (p a2 b2) (p a3 b3) (p a4 b4)
+                                 (p a5 b5) (p a6 b6) (p a7 b7) (p a8 b8)
+       -> p (T8 a1 a2 a3 a4 a5 a6 a7 a8) (T8 b1 b2 b3 b4 b5 b6 b7 b8)
+pT8 = chain pT7
+
+pT9 :: ProductProfunctor p => T9 (p a1 b1) (p a2 b2) (p a3 b3) (p a4 b4)
+                                 (p a5 b5) (p a6 b6) (p a7 b7) (p a8 b8)
+                                 (p a9 b9)
+       -> p (T9 a1 a2 a3 a4 a5 a6 a7 a8 a9)
+            (T9 b1 b2 b3 b4 b5 b6 b7 b8 b9)
+pT9 = chain pT8
+
+pT10 :: ProductProfunctor p => T10 (p a1 b1) (p a2 b2) (p a3 b3) (p a4 b4)
+                                   (p a5 b5) (p a6 b6) (p a7 b7) (p a8 b8)
+                                   (p a9 b9) (p a10 b10)
+       -> p (T10 a1 a2 a3 a4 a5 a6 a7 a8 a9 a10)
+            (T10 b1 b2 b3 b4 b5 b6 b7 b8 b9 b10)
+pT10 = chain pT9
+
+pT11 :: ProductProfunctor p => T11 (p a1 b1) (p a2 b2) (p a3 b3) (p a4 b4)
+                                   (p a5 b5) (p a6 b6) (p a7 b7) (p a8 b8)
+                                   (p a9 b9) (p a10 b10) (p a11 b11)
+       -> p (T11 a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11)
+            (T11 b1 b2 b3 b4 b5 b6 b7 b8 b9 b10 b11)
+pT11 = chain pT10
+
+pT12 :: ProductProfunctor p => T12 (p a1 b1) (p a2 b2) (p a3 b3) (p a4 b4)
+                                   (p a5 b5) (p a6 b6) (p a7 b7) (p a8 b8)
+                                   (p a9 b9) (p a10 b10) (p a11 b11)
+                                   (p a12 b12)
+       -> p (T12 a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12)
+            (T12 b1 b2 b3 b4 b5 b6 b7 b8 b9 b10 b11 b12)
+pT12 = chain pT11
+
+pT13 :: ProductProfunctor p => T13 (p a1 b1) (p a2 b2) (p a3 b3) (p a4 b4)
+                                   (p a5 b5) (p a6 b6) (p a7 b7) (p a8 b8)
+                                   (p a9 b9) (p a10 b10) (p a11 b11)
+                                   (p a12 b12) (p a13 b13)
+       -> p (T13 a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12 a13)
+            (T13 b1 b2 b3 b4 b5 b6 b7 b8 b9 b10 b11 b12 b13)
+pT13 = chain pT12
+
+pT14 :: ProductProfunctor p => T14 (p a1 b1) (p a2 b2) (p a3 b3) (p a4 b4)
+                                   (p a5 b5) (p a6 b6) (p a7 b7) (p a8 b8)
+                                   (p a9 b9) (p a10 b10) (p a11 b11)
+                                   (p a12 b12) (p a13 b13) (p a14 b14)
+       -> p (T14 a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12 a13 a14)
+            (T14 b1 b2 b3 b4 b5 b6 b7 b8 b9 b10 b11 b12 b13 b14)
+pT14 = chain pT13
+
+pT15 :: ProductProfunctor p => T15 (p a1 b1) (p a2 b2) (p a3 b3) (p a4 b4)
+                                   (p a5 b5) (p a6 b6) (p a7 b7) (p a8 b8)
+                                   (p a9 b9) (p a10 b10) (p a11 b11)
+                                   (p a12 b12) (p a13 b13) (p a14 b14)
+                                   (p a15 b15)
+       -> p (T15 a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12 a13 a14 a15)
+            (T15 b1 b2 b3 b4 b5 b6 b7 b8 b9 b10 b11 b12 b13 b14 b15)
+pT15 = chain pT14
+
+pT16 :: ProductProfunctor p => T16 (p a1 b1) (p a2 b2) (p a3 b3) (p a4 b4)
+                                   (p a5 b5) (p a6 b6) (p a7 b7) (p a8 b8)
+                                   (p a9 b9) (p a10 b10) (p a11 b11)
+                                   (p a12 b12) (p a13 b13) (p a14 b14)
+                                   (p a15 b15) (p a16 b16)
+       -> p (T16 a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12 a13 a14 a15 a16)
+            (T16 b1 b2 b3 b4 b5 b6 b7 b8 b9 b10 b11 b12 b13 b14 b15 b16)
+pT16 = chain pT15
+
+pT17 :: ProductProfunctor p => T17 (p a1 b1) (p a2 b2) (p a3 b3) (p a4 b4)
+                                   (p a5 b5) (p a6 b6) (p a7 b7) (p a8 b8)
+                                   (p a9 b9) (p a10 b10) (p a11 b11)
+                                   (p a12 b12) (p a13 b13) (p a14 b14)
+                                   (p a15 b15) (p a16 b16) (p a17 b17)
+       -> p (T17 a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12 a13 a14 a15 a16 a17)
+            (T17 b1 b2 b3 b4 b5 b6 b7 b8 b9 b10 b11 b12 b13 b14 b15 b16 b17)
+pT17 = chain pT16
+
+pT18 :: ProductProfunctor p => T18 (p a1 b1) (p a2 b2) (p a3 b3) (p a4 b4)
+                                   (p a5 b5) (p a6 b6) (p a7 b7) (p a8 b8)
+                                   (p a9 b9) (p a10 b10) (p a11 b11)
+                                   (p a12 b12) (p a13 b13) (p a14 b14)
+                                   (p a15 b15) (p a16 b16) (p a17 b17)
+                                   (p a18 b18)
+       -> p (T18 a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12 a13 a14 a15 a16 a17 a18)
+            (T18 b1 b2 b3 b4 b5 b6 b7 b8 b9 b10 b11 b12 b13 b14 b15 b16 b17 b18)
+pT18 = chain pT17
+
+convert :: Profunctor p => (a2 -> a1) -> (tp -> tTp) -> (b1 -> b2)
+                           -> (tTp -> p a1 b1)
+                           -> tp -> p a2 b2
+convert u u' f c = dimap u f . c . u'
+
+p0 :: ProductProfunctor p => () -> p () ()
+p0 = convert unflatten0 unflatten0 flatten0 pT0
+
+p1 :: ProductProfunctor p => p a1 b1 -> p a1 b1
+p1 = convert unflatten1 unflatten1 flatten1 pT1
+
+p2 :: ProductProfunctor p => (p a1 b1, p a2 b2) -> p (a1, a2) (b1, b2)
+p2 = convert unflatten2 unflatten2 flatten2 pT2
+
+p3 :: ProductProfunctor p => (p a1 b1, p a2 b2, p a3 b3)
+      -> p (a1, a2, a3) (b1, b2, b3)
+p3 = convert unflatten3 unflatten3 flatten3 pT3
+
+p4 :: ProductProfunctor p => (p a1 b1, p a2 b2, p a3 b3, p a4 b4)
+      -> p (a1, a2, a3, a4) (b1, b2, b3, b4)
+p4 = convert unflatten4 unflatten4 flatten4 pT4
+
+p5 :: ProductProfunctor p => (p a1 b1, p a2 b2, p a3 b3, p a4 b4,
+                              p a5 b5)
+      -> p (a1, a2, a3, a4, a5) (b1, b2, b3, b4, b5)
+p5 = convert unflatten5 unflatten5 flatten5 pT5
+
+p6 :: ProductProfunctor p => (p a1 b1, p a2 b2, p a3 b3, p a4 b4,
+                              p a5 b5, p a6 b6)
+      -> p (a1, a2, a3, a4, a5, a6) (b1, b2, b3, b4, b5, b6)
+p6 = convert unflatten6 unflatten6 flatten6 pT6
+
+p7 :: ProductProfunctor p => (p a1 b1, p a2 b2, p a3 b3, p a4 b4,
+                              p a5 b5, p a6 b6, p a7 b7)
+      -> p (a1, a2, a3, a4, a5, a6, a7) (b1, b2, b3, b4, b5, b6, b7)
+p7 = convert unflatten7 unflatten7 flatten7 pT7
+
+p8 :: ProductProfunctor p => (p a1 b1, p a2 b2, p a3 b3, p a4 b4,
+                              p a5 b5, p a6 b6, p a7 b7, p a8 b8)
+      -> p (a1, a2, a3, a4, a5, a6, a7, a8) (b1, b2, b3, b4, b5, b6, b7, b8)
+p8 = convert unflatten8 unflatten8 flatten8 pT8
+
+p9 :: ProductProfunctor p => (p a1 b1, p a2 b2, p a3 b3, p a4 b4,
+                              p a5 b5, p a6 b6, p a7 b7, p a8 b8,
+                              p a9 b9)
+      -> p (a1, a2, a3, a4, a5, a6, a7, a8, a9)
+           (b1, b2, b3, b4, b5, b6, b7, b8, b9)
+p9 = convert unflatten9 unflatten9 flatten9 pT9
+
+p10 :: ProductProfunctor p => (p a1 b1, p a2 b2, p a3 b3, p a4 b4,
+                              p a5 b5, p a6 b6, p a7 b7, p a8 b8,
+                              p a9 b9, p a10 b10)
+      -> p (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10)
+           (b1, b2, b3, b4, b5, b6, b7, b8, b9, b10)
+p10 = convert unflatten10 unflatten10 flatten10 pT10
+
+p11 :: ProductProfunctor p => (p a1 b1, p a2 b2, p a3 b3, p a4 b4,
+                              p a5 b5, p a6 b6, p a7 b7, p a8 b8,
+                              p a9 b9, p a10 b10, p a11 b11)
+      -> p (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11)
+           (b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11)
+p11 = convert unflatten11 unflatten11 flatten11 pT11
+
+p12 :: ProductProfunctor p => (p a1 b1, p a2 b2, p a3 b3, p a4 b4,
+                              p a5 b5, p a6 b6, p a7 b7, p a8 b8,
+                              p a9 b9, p a10 b10, p a11 b11, p a12 b12)
+      -> p (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12)
+           (b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12)
+p12 = convert unflatten12 unflatten12 flatten12 pT12
+
+p13 :: ProductProfunctor p => (p a1 b1, p a2 b2, p a3 b3, p a4 b4,
+                              p a5 b5, p a6 b6, p a7 b7, p a8 b8,
+                              p a9 b9, p a10 b10, p a11 b11, p a12 b12,
+                              p a13 b13)
+      -> p (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13)
+           (b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13)
+p13 = convert unflatten13 unflatten13 flatten13 pT13
+
+p14 :: ProductProfunctor p => (p a1 b1, p a2 b2, p a3 b3, p a4 b4,
+                              p a5 b5, p a6 b6, p a7 b7, p a8 b8,
+                              p a9 b9, p a10 b10, p a11 b11, p a12 b12,
+                              p a13 b13, p a14 b14)
+      -> p (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14)
+           (b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14)
+p14 = convert unflatten14 unflatten14 flatten14 pT14
+
+p15 :: ProductProfunctor p => (p a1 b1, p a2 b2, p a3 b3, p a4 b4,
+                              p a5 b5, p a6 b6, p a7 b7, p a8 b8,
+                              p a9 b9, p a10 b10, p a11 b11, p a12 b12,
+                              p a13 b13, p a14 b14, p a15 b15)
+      -> p (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15)
+           (b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15)
+p15 = convert unflatten15 unflatten15 flatten15 pT15
+
+p16 :: ProductProfunctor p => (p a1 b1, p a2 b2, p a3 b3, p a4 b4,
+                              p a5 b5, p a6 b6, p a7 b7, p a8 b8,
+                              p a9 b9, p a10 b10, p a11 b11, p a12 b12,
+                              p a13 b13, p a14 b14, p a15 b15, p a16 b16)
+      -> p (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16)
+           (b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16)
+p16 = convert unflatten16 unflatten16 flatten16 pT16
+
+p17 :: ProductProfunctor p => (p a1 b1, p a2 b2, p a3 b3, p a4 b4,
+                              p a5 b5, p a6 b6, p a7 b7, p a8 b8,
+                              p a9 b9, p a10 b10, p a11 b11, p a12 b12,
+                              p a13 b13, p a14 b14, p a15 b15, p a16 b16, p a17 b17)
+      -> p (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17)
+           (b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17)
+p17 = convert unflatten17 unflatten17 flatten17 pT17
+
+p18 :: ProductProfunctor p => (p a1 b1, p a2 b2, p a3 b3, p a4 b4,
+                              p a5 b5, p a6 b6, p a7 b7, p a8 b8,
+                              p a9 b9, p a10 b10, p a11 b11, p a12 b12,
+                              p a13 b13, p a14 b14, p a15 b15, p a16 b16,
+                              p a17 b17, p a18 b18)
+      -> p (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18)
+           (b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18)
+p18 = convert unflatten18 unflatten18 flatten18 pT18
diff --git a/Data/Profunctor/Product/Default.hs b/Data/Profunctor/Product/Default.hs
new file mode 100644
--- /dev/null
+++ b/Data/Profunctor/Product/Default.hs
@@ -0,0 +1,136 @@
+{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances,
+             FlexibleContexts #-}
+
+module Data.Profunctor.Product.Default where
+
+-- TODO: vv this imports a lot of names.  Should we list them all?
+import Data.Profunctor.Product
+
+class Default p a b where
+  -- Would rather call it "default", but that's a keyword
+  def :: p a b
+
+cdef :: Default (PPOfContravariant u) a a => u a
+cdef = unPPOfContravariant def
+
+instance ProductProfunctor p => Default p () () where
+  def = empty
+
+instance (ProductProfunctor p, Default p a1 b1, Default p a2 b2)
+         => Default p (a1, a2) (b1, b2) where
+  def = p2 (def, def)
+
+instance (ProductProfunctor p, Default p a1 b1, Default p a2 b2,
+          Default p a3 b3)
+         => Default p (a1, a2, a3)
+                      (b1, b2, b3) where
+  def = p3 (def, def, def)
+
+instance (ProductProfunctor p, Default p a1 b1, Default p a2 b2,
+          Default p a3 b3, Default p a4 b4)
+         => Default p (a1, a2, a3, a4)
+                      (b1, b2, b3, b4) where
+  def = p4 (def, def, def, def)
+
+instance (ProductProfunctor p, Default p a1 b1, Default p a2 b2,
+          Default p a3 b3, Default p a4 b4, Default p a5 b5)
+         => Default p (a1, a2, a3, a4, a5)
+                      (b1, b2, b3, b4, b5) where
+  def = p5 (def, def, def, def, def)
+
+instance (ProductProfunctor p, Default p a1 b1, Default p a2 b2,
+          Default p a3 b3, Default p a4 b4, Default p a5 b5,
+          Default p a6 b6)
+         => Default p (a1, a2, a3, a4, a5, a6)
+                      (b1, b2, b3, b4, b5, b6) where
+  def = p6 (def, def, def, def, def, def)
+
+instance (ProductProfunctor p, Default p a1 b1, Default p a2 b2,
+          Default p a3 b3, Default p a4 b4, Default p a5 b5,
+          Default p a6 b6, Default p a7 b7)
+         => Default p (a1, a2, a3, a4, a5, a6, a7)
+                      (b1, b2, b3, b4, b5, b6, b7) where
+  def = p7 (def, def, def, def, def, def, def)
+
+instance (ProductProfunctor p, Default p a1 b1, Default p a2 b2,
+          Default p a3 b3, Default p a4 b4, Default p a5 b5,
+          Default p a6 b6, Default p a7 b7, Default p a8 b8)
+         => Default p (a1, a2, a3, a4, a5, a6, a7, a8)
+                      (b1, b2, b3, b4, b5, b6, b7, b8) where
+  def = p8 (def, def, def, def, def, def, def, def)
+
+instance (ProductProfunctor p, Default p a1 b1, Default p a2 b2,
+          Default p a3 b3, Default p a4 b4, Default p a5 b5,
+          Default p a6 b6, Default p a7 b7, Default p a8 b8,
+          Default p a9 b9)
+         => Default p (a1, a2, a3, a4, a5, a6, a7, a8, a9)
+                      (b1, b2, b3, b4, b5, b6, b7, b8, b9) where
+  def = p9 (def, def, def, def, def, def, def, def, def)
+
+instance (ProductProfunctor p, Default p a1 b1, Default p a2 b2,
+          Default p a3 b3, Default p a4 b4, Default p a5 b5,
+          Default p a6 b6, Default p a7 b7, Default p a8 b8,
+          Default p a9 b9, Default p a10 b10)
+         => Default p (a1, a2, a3, a4, a5, a6, a7, a8,
+                       a9, a10)
+                      (b1, b2, b3, b4, b5, b6, b7, b8,
+                      b9, b10) where
+  def = p10 (def, def, def, def, def, def, def, def, def, def)
+
+instance (ProductProfunctor p, Default p a1 b1, Default p a2 b2,
+          Default p a3 b3, Default p a4 b4, Default p a5 b5,
+          Default p a6 b6, Default p a7 b7, Default p a8 b8,
+          Default p a9 b9, Default p a10 b10, Default p a11 b11)
+         => Default p (a1, a2, a3, a4, a5, a6, a7, a8,
+                       a9, a10, a11)
+                      (b1, b2, b3, b4, b5, b6, b7, b8,
+                      b9, b10, b11) where
+  def = p11 (def, def, def, def, def, def, def, def, def, def, def)
+
+instance (ProductProfunctor p, Default p a1 b1, Default p a2 b2,
+          Default p a3 b3, Default p a4 b4, Default p a5 b5,
+          Default p a6 b6, Default p a7 b7, Default p a8 b8,
+          Default p a9 b9, Default p a10 b10, Default p a11 b11,
+          Default p a12 b12)
+         => Default p (a1, a2, a3, a4, a5, a6, a7, a8,
+                       a9, a10, a11, a12)
+                      (b1, b2, b3, b4, b5, b6, b7, b8,
+                      b9, b10, b11, b12) where
+  def = p12 (def, def, def, def, def, def, def, def, def, def, def, def)
+
+instance (ProductProfunctor p, Default p a1 b1, Default p a2 b2,
+          Default p a3 b3, Default p a4 b4, Default p a5 b5,
+          Default p a6 b6, Default p a7 b7, Default p a8 b8,
+          Default p a9 b9, Default p a10 b10, Default p a11 b11,
+          Default p a12 b12, Default p a13 b13, Default p a14 b14)
+         => Default p (a1, a2, a3, a4, a5, a6, a7, a8,
+                       a9, a10, a11, a12, a13, a14)
+                      (b1, b2, b3, b4, b5, b6, b7, b8,
+                      b9, b10, b11, b12, b13, b14) where
+  def = p14 (def, def, def, def, def, def, def, def, def, def,
+             def, def, def, def)
+
+instance (ProductProfunctor p, Default p a1 b1, Default p a2 b2,
+          Default p a3 b3, Default p a4 b4, Default p a5 b5,
+          Default p a6 b6, Default p a7 b7, Default p a8 b8,
+          Default p a9 b9, Default p a10 b10, Default p a11 b11,
+          Default p a12 b12, Default p a13 b13, Default p a14 b14, Default p a15 b15)
+         => Default p (a1, a2, a3, a4, a5, a6, a7, a8,
+                       a9, a10, a11, a12, a13, a14, a15)
+                      (b1, b2, b3, b4, b5, b6, b7, b8,
+                      b9, b10, b11, b12, b13, b14, b15) where
+  def = p15 (def, def, def, def, def, def, def, def, def, def,
+             def, def, def, def, def)
+
+instance (ProductProfunctor p, Default p a1 b1, Default p a2 b2,
+          Default p a3 b3, Default p a4 b4, Default p a5 b5,
+          Default p a6 b6, Default p a7 b7, Default p a8 b8,
+          Default p a9 b9, Default p a10 b10, Default p a11 b11,
+          Default p a12 b12, Default p a13 b13, Default p a14 b14,
+          Default p a15 b15, Default p a16 b16)
+         => Default p (a1, a2, a3, a4, a5, a6, a7, a8,
+                       a9, a10, a11, a12, a13, a14, a15, a16)
+                      (b1, b2, b3, b4, b5, b6, b7, b8,
+                      b9, b10, b11, b12, b13, b14, b15, b16) where
+  def = p16 (def, def, def, def, def, def, def, def, def, def,
+             def, def, def, def, def, def)
diff --git a/Data/Profunctor/Product/Flatten.hs b/Data/Profunctor/Product/Flatten.hs
new file mode 100644
--- /dev/null
+++ b/Data/Profunctor/Product/Flatten.hs
@@ -0,0 +1,88 @@
+{-# OPTIONS_GHC -fno-warn-missing-signatures #-}
+
+module Data.Profunctor.Product.Flatten where
+
+flatten0 () = ()
+unflatten0 () = ()
+
+flatten1 a = a
+unflatten1 a = a
+
+flatten2 (a, b) = (a, b)
+unflatten2 (a, b) = (a, b)
+
+flatten3 (a, (b, c)) = (a, b, c)
+unflatten3 (a, b, c) = (a, (b, c))
+
+flatten4 (a, (b, (c, a4))) = (a, b, c, a4)
+unflatten4 (a, b, c, a4) = (a, (b, (c, a4)))
+
+flatten5 (a, (b, (c, (a4, a5)))) = (a, b, c, a4, a5)
+unflatten5 (a, b, c, a4, a5) = (a, (b, (c, (a4, a5))))
+
+flatten6 (a, (b, (c, (a4, (a5, a6))))) = (a, b, c, a4, a5, a6)
+unflatten6 (a, b, c, a4, a5, a6) = (a, (b, (c, (a4, (a5, a6)))))
+
+flatten7 (a, (b, (c, (a4, (a5, (a6, a7)))))) = (a, b, c, a4, a5, a6, a7)
+unflatten7 (a, b, c, a4, a5, a6, a7) = (a, (b, (c, (a4, (a5, (a6, a7))))))
+
+flatten8 (a, (b, (c, (a4, (a5, (a6, (a7, a8)))))))
+  = (a, b, c, a4, a5, a6, a7, a8)
+unflatten8 (a, b, c, a4, a5, a6, a7, a8)
+  = (a, (b, (c, (a4, (a5, (a6, (a7, a8)))))))
+
+flatten9 (a, (b, (c, (a4, (a5, (a6, (a7, (a8, a9))))))))
+  = (a, b, c, a4, a5, a6, a7, a8, a9)
+unflatten9 (a, b, c, a4, a5, a6, a7, a8, a9)
+  = (a, (b, (c, (a4, (a5, (a6, (a7, (a8, a9))))))))
+
+flatten10 (a, (b, (c, (a4, (a5, (a6, (a7, (a8, (a9, a10)))))))))
+  = (a, b, c, a4, a5, a6, a7, a8, a9, a10)
+unflatten10 (a, b, c, a4, a5, a6, a7, a8, a9, a10)
+  = (a, (b, (c, (a4, (a5, (a6, (a7, (a8, (a9, a10)))))))))
+
+flatten11 (a, (b, (c, (a4, (a5, (a6, (a7, (a8, (a9, (a10, a11))))))))))
+  = (a, b, c, a4, a5, a6, a7, a8, a9, a10, a11)
+unflatten11 (a, b, c, a4, a5, a6, a7, a8, a9, a10, a11)
+  = (a, (b, (c, (a4, (a5, (a6, (a7, (a8, (a9, (a10, a11))))))))))
+
+flatten12 (a, (b, (c, (a4, (a5, (a6, (a7, (a8, (a9, (a10, (a11, a12)))))))))))
+  = (a, b, c, a4, a5, a6, a7, a8, a9, a10, a11, a12)
+unflatten12 (a, b, c, a4, a5, a6, a7, a8, a9, a10, a11, a12)
+  = (a, (b, (c, (a4, (a5, (a6, (a7, (a8, (a9, (a10, (a11, a12)))))))))))
+
+flatten13 (a, (b, (c, (a4, (a5, (a6, (a7, (a8, (a9, (a10, (a11, (a12,
+           a13))))))))))))
+  = (a, b, c, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13)
+unflatten13 (a, b, c, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13)
+  = (a, (b, (c, (a4, (a5, (a6, (a7, (a8, (a9, (a10, (a11, (a12, a13))))))))))))
+
+flatten14 (a, (b, (c, (a4, (a5, (a6, (a7, (a8, (a9, (a10, (a11, (a12,
+           (a13, a14)))))))))))))
+  = (a, b, c, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14)
+unflatten14 (a, b, c, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14)
+  = (a, (b, (c, (a4, (a5, (a6, (a7, (a8, (a9, (a10, (a11, (a12, (a13, a14)))))))))))))
+
+flatten15 (a, (b, (c, (a4, (a5, (a6, (a7, (a8, (a9, (a10, (a11, (a12,
+           (a13, (a14, a15))))))))))))))
+  = (a, b, c, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15)
+unflatten15 (a, b, c, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15)
+  = (a, (b, (c, (a4, (a5, (a6, (a7, (a8, (a9, (a10, (a11, (a12, (a13, (a14, a15))))))))))))))
+
+flatten16 (a, (b, (c, (a4, (a5, (a6, (a7, (a8, (a9, (a10, (a11, (a12,
+           (a13, (a14, (a15, a16)))))))))))))))
+  = (a, b, c, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16)
+unflatten16 (a, b, c, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16)
+  = (a, (b, (c, (a4, (a5, (a6, (a7, (a8, (a9, (a10, (a11, (a12, (a13, (a14, (a15, a16)))))))))))))))
+
+flatten17 (a, (b, (c, (a4, (a5, (a6, (a7, (a8, (a9, (a10, (a11, (a12,
+           (a13, (a14, (a15, (a16, a17))))))))))))))))
+  = (a, b, c, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17)
+unflatten17 (a, b, c, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17)
+  = (a, (b, (c, (a4, (a5, (a6, (a7, (a8, (a9, (a10, (a11, (a12, (a13, (a14, (a15, (a16, a17))))))))))))))))
+
+flatten18 (a, (b, (c, (a4, (a5, (a6, (a7, (a8, (a9, (a10, (a11, (a12,
+           (a13, (a14, (a15, (a16, (a17, a18)))))))))))))))))
+  = (a, b, c, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18)
+unflatten18 (a, b, c, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18)
+  = (a, (b, (c, (a4, (a5, (a6, (a7, (a8, (a9, (a10, (a11, (a12, (a13, (a14, (a15, (a16, (a17, a18)))))))))))))))))
diff --git a/Data/Profunctor/Product/TH.hs b/Data/Profunctor/Product/TH.hs
new file mode 100644
--- /dev/null
+++ b/Data/Profunctor/Product/TH.hs
@@ -0,0 +1,299 @@
+-- | If you have a data declaration which is a polymorphic product,
+-- for example
+--
+-- @
+-- data Foo a b c = Foo a b c
+-- @
+--
+-- or
+--
+-- @
+-- data Foo a b c = Foo { foo :: a, bar :: b, baz :: c }
+-- @
+--
+-- then you can use Template Haskell to automatically derive the
+-- product-profunctor 'Default' instances and product-profunctor
+-- \"adaptor\" with the following imports and splice:
+--
+-- @
+-- import Data.Profunctor.Product (ProductProfunctor, p\<n\>)
+-- import Data.Profunctor (dimap)
+-- import Data.Profunctor.Product.Default (Default, def)
+--
+-- $(makeAdaptorAndInstance \"pFoo\" ''Foo)
+-- @
+--
+-- * \<n\> is the number of fields in your record, so for Foo \<n\> would be 3.
+--
+-- * The adaptor for a type Foo is by convention called pFoo, but in
+-- practice you can call it anything.
+--
+-- Notice that currently the Template Haskell requires you import a
+-- number of other names from other modules.  This is because it is
+-- badly written and this restriction will go away in a future
+-- release.
+--
+-- The instance generated will be
+--
+-- @
+-- instance (ProductProfunctor p, Default p a a', Default p b b', Default p c c')
+--       => Default p (Foo a b c) (Foo a' b' c')
+-- @
+--
+-- and pFoo will have the type
+--
+-- @
+-- pFoo :: ProductProfunctor p =>
+--         Foo (p a a') (p b b') (p c c') -> p (Foo a b c) (Foo a' b' c')
+-- @
+
+module Data.Profunctor.Product.TH where
+
+import Language.Haskell.TH (Dec(DataD, SigD, FunD, InstanceD),
+                            mkName, TyVarBndr(PlainTV, KindedTV),
+                            Con(RecC, NormalC),
+                            Strict(NotStrict), Clause(Clause),
+                            Type(VarT, ForallT, AppT, ArrowT, ConT),
+                            Body(NormalB), Q, Pred(ClassP),
+                            Exp(ConE, VarE, InfixE, AppE, TupE),
+                            Pat(TupP, VarP, ConP), Name,
+                            Info(TyConI), reify)
+import Control.Monad ((<=<))
+
+makeAdaptorAndInstance :: String -> Name -> Q [Dec]
+makeAdaptorAndInstance adaptorNameS = returnOrFail <=< r makeAandIE <=< reify
+  where r = (return .)
+        returnOrFail (Right decs) = return decs
+        returnOrFail (Left errMsg) = fail errMsg
+        makeAandIE = makeAdaptorAndInstanceE adaptorNameS
+
+type Error = String
+
+makeAdaptorAndInstanceE :: String -> Info -> Either Error [Dec]
+makeAdaptorAndInstanceE adaptorNameS info = do
+  (tyName, tyVars, conName, conTys) <- dataDecStuffOfInfo info
+  let numTyVars = length tyVars
+      numConTys = length conTys
+      adaptorNameN = mkName adaptorNameS
+      adaptorSig' = adaptorSig tyName numTyVars adaptorNameN
+      adaptorDefinition' = adaptorDefinition numTyVars conName adaptorNameN
+      instanceDefinition' = instanceDefinition tyName numTyVars numConTys
+                                               adaptorNameN conName
+
+  return [adaptorSig', adaptorDefinition', instanceDefinition']
+
+-- TODO: support newtypes?
+dataDecStuffOfInfo :: Info -> Either Error (Name, [Name], Name, [Name])
+dataDecStuffOfInfo (TyConI (DataD _cxt tyName tyVars constructors _deriving)) =
+  do
+    (conName, conTys) <- extractConstructorStuff constructors
+    let tyVars' = map varNameOfBinder tyVars
+    return (tyName, tyVars', conName, conTys)
+dataDecStuffOfInfo _ = Left "That doesn't look like a data declaration to me"
+
+varNameOfType :: Type -> Either Error Name
+varNameOfType (VarT n) = Right n
+varNameOfType x = Left $ "Found a non-variable type" ++ show x
+
+varNameOfBinder :: TyVarBndr -> Name
+varNameOfBinder (PlainTV n) = n
+varNameOfBinder (KindedTV n _) = n
+
+conStuffOfConstructor :: Con -> Either Error (Name, [Name])
+conStuffOfConstructor (NormalC conName st) = do
+  conTys <- mapM (varNameOfType . snd) st
+  return (conName, conTys)
+conStuffOfConstructor (RecC conName vst) = do
+  conTys <- mapM (varNameOfType . thrd) vst
+  return (conName, conTys)
+    where thrd = \(_,_,x) -> x
+conStuffOfConstructor _ = Left "I can't deal with your constructor type"
+
+constructorOfConstructors :: [Con] -> Either Error Con
+constructorOfConstructors [single] = return single
+constructorOfConstructors [] = Left "I need at least one constructor"
+constructorOfConstructors _many = Left msg
+  where msg = "I can't deal with more than one constructor"
+
+extractConstructorStuff :: [Con] -> Either Error (Name, [Name])
+extractConstructorStuff = conStuffOfConstructor <=< constructorOfConstructors
+
+-- MakeRecordT and makeRecordData were from an old interface.  We could probably
+-- delete them now.
+data MakeRecordT = MakeRecordT { typeName :: String
+                               , constructorName :: String
+                               , fieldNames :: [String]
+                               , deriving_ :: [String]
+                               , adaptorName :: String }
+
+makeRecordData :: MakeRecordT -> Q [Dec]
+makeRecordData r = return [datatype'] where
+  MakeRecordT tyName conName tyVars derivings _ = r
+  tyName' = mkName tyName
+  datatype' = datatype tyName' tyVars conName derivings
+
+makeRecord :: MakeRecordT -> Q [Dec]
+makeRecord r = return decs
+  where MakeRecordT tyName conName tyVars derivings _ = r
+        decs = [datatype', adaptorSig', adaptorDefinition', instanceDefinition']
+        tyName' = mkName tyName
+        conName' = mkName conName
+
+        adaptorName' = mkName (adaptorName r)
+
+        numTyVars = length tyVars
+
+        datatype' = datatype tyName' tyVars conName derivings
+        adaptorSig' = adaptorSig tyName' numTyVars adaptorName'
+        adaptorDefinition' = adaptorDefinition numTyVars conName' adaptorName'
+        instanceDefinition' = instanceDefinition tyName' numTyVars numTyVars
+                                                 adaptorName' conName'
+
+-- The implementations of the datatype (only used in the old makeRecord),
+-- instance and adaptor follow.
+datatype :: Name -> [String] -> String -> [String] -> Dec
+datatype tyName tyVars conName derivings = datatype'
+  where datatype' = DataD [] tyName tyVars' [con] derivings'
+        fields = map toField tyVars
+        tyVars' = map (PlainTV . mkName) tyVars
+        con = RecC (mkName conName) fields
+        toField s = (mkName s, NotStrict, VarT (mkName s))
+        derivings' = map mkName derivings
+
+instanceDefinition :: Name -> Int -> Int -> Name -> Name -> Dec
+instanceDefinition tyName' numTyVars numConVars adaptorName' conName=instanceDec
+  where instanceDec = InstanceD instanceCxt instanceType [defDefinition]
+        instanceCxt = map (uncurry ClassP) (pClass:defClasses)
+        pClass = (mkName "ProductProfunctor", [varTS "p"])
+
+        defaultPredOfVar :: String -> (Name, [Type])
+        defaultPredOfVar fn = (mkName "Default", [varTS "p",
+                                                  mkTySuffix "0" fn,
+                                                  mkTySuffix "1" fn])
+
+        defClasses = map defaultPredOfVar (allTyVars numTyVars)
+
+        pArg :: String -> Type
+        pArg s = pArg' tyName' s numTyVars
+
+        instanceType = appTAll (conTS "Default")
+                               [varTS "p", pArg "0", pArg "1"]
+
+        defDefinition = FunD (mkName "def") [Clause [] defBody []]
+        defBody = NormalB(VarE adaptorName' `AppE` appEAll (ConE conName) defsN)
+        defsN = replicate numConVars (varS "def")
+
+adaptorSig :: Name -> Int -> Name -> Dec
+adaptorSig tyName' numTyVars = flip SigD adaptorType
+  where adaptorType = ForallT scope adaptorCxt adaptorAfterCxt
+        adaptorAfterCxt = before `appArrow` after
+        adaptorCxt = [ClassP (mkName "ProductProfunctor")
+                            [VarT (mkName "p")]]
+        before = appTAll (ConT tyName') pArgs
+        pType = VarT (mkName "p")
+        pArgs = map pApp tyVars
+        pApp :: String  -> Type
+        pApp v = appTAll pType [mkVarTsuffix "0" v, mkVarTsuffix "1" v]
+
+
+        tyVars = allTyVars numTyVars
+
+        pArg :: String -> Type
+        pArg s = pArg' tyName' s numTyVars
+
+        after = appTAll pType [pArg "0", pArg "1"]
+
+        scope = concat [ [PlainTV (mkName "p")]
+                       , map (mkTyVarsuffix "0") tyVars
+                       , map (mkTyVarsuffix "1") tyVars ]
+
+adaptorDefinition :: Int -> Name -> Name -> Dec
+adaptorDefinition numConVars conName = flip FunD [clause]
+  where clause = Clause [] body wheres
+        toTupleN = mkName "toTuple"
+        fromTupleN = mkName "fromTuple"
+        toTupleE = VarE toTupleN
+        fromTupleE = VarE fromTupleN
+        theDimap = appEAll (varS "dimap") [toTupleE, fromTupleE]
+        pN = VarE (mkName ("p" ++ show numConVars))
+        body = NormalB (theDimap `o` pN `o` toTupleE)
+        wheres = [toTuple conName (toTupleN, numConVars),
+                  fromTuple conName (fromTupleN, numConVars)]
+
+xTuple :: ([Pat] -> Pat) -> ([Exp] -> Exp) -> (Name, Int) -> Dec
+xTuple patCon retCon (funN, numTyVars) = FunD funN [clause]
+  where clause = Clause [pat] body []
+        pat = patCon varPats
+        body = NormalB (retCon varExps)
+        varPats = map varPS (allTyVars numTyVars)
+        varExps = map varS (allTyVars numTyVars)
+
+fromTuple :: Name -> (Name, Int) -> Dec
+fromTuple conName = xTuple patCon retCon
+  where patCon = TupP
+        retCon = appEAll (ConE conName)
+
+toTuple :: Name -> (Name, Int) -> Dec
+toTuple conName = xTuple patCon retCon
+  where patCon = ConP conName
+        retCon = TupE
+
+{-
+Note that we can also do the instance definition like this, but it would
+require pulling the to/fromTuples to the top level
+
+instance (ProductProfunctor p, Default p a a', Default p b b',
+          Default p c c', Default p d d', Default p e e',
+          Default p f f', Default p g g', Default p h h')
+         => Default p (LedgerRow' a b c d e f g h)
+                      (LedgerRow' a' b' c' d' e' f' g' h') where
+  def = dimap tupleOfLedgerRow lRowOfTuple def
+-}
+
+pArg' :: Name -> String -> Int -> Type
+pArg' tn s = appTAll (ConT tn) . map (varTS . (++s)) . allTyVars
+
+allTyVars :: Int -> [String]
+allTyVars numTyVars = map varA tyNums
+  where varA i = "a" ++ show i ++ "_"
+        tyNums :: [Int]
+        tyNums = [1..numTyVars]
+
+o :: Exp -> Exp -> Exp
+o x y = InfixE (Just x) (varS ".") (Just y)
+
+varS :: String -> Exp
+varS = VarE . mkName
+
+conES :: String -> Exp
+conES = ConE . mkName
+
+conPS :: String -> [Pat] -> Pat
+conPS = ConP . mkName
+
+varPS :: String -> Pat
+varPS = VarP . mkName
+
+mkTyVarsuffix :: String -> String -> TyVarBndr
+mkTyVarsuffix s = PlainTV . mkName . (++s)
+
+mkTySuffix :: String -> String -> Type
+mkTySuffix s = varTS . (++s)
+
+mkVarTsuffix :: String -> String -> Type
+mkVarTsuffix s = VarT . mkName . (++s)
+
+varTS :: String -> Type
+varTS = VarT . mkName
+
+conTS :: String -> Type
+conTS = ConT . mkName
+
+appTAll :: Type -> [Type] -> Type
+appTAll = foldl AppT
+
+appEAll :: Exp -> [Exp] -> Exp
+appEAll = foldl AppE
+
+appArrow :: Type -> Type -> Type
+appArrow l r = appTAll ArrowT [l, r]
diff --git a/Data/Profunctor/Product/Tuples.hs b/Data/Profunctor/Product/Tuples.hs
new file mode 100644
--- /dev/null
+++ b/Data/Profunctor/Product/Tuples.hs
@@ -0,0 +1,27 @@
+module Data.Profunctor.Product.Tuples where
+
+type T0 = ()
+type T1 a = a
+type T2 a b = (a, T1 b)
+type T3 a b c = (a, T2 b c)
+type T4 a b c d = (a, T3 b c d)
+type T5 a b c d e = (a, T4 b c d e)
+type T6 a b c d e f = (a, T5 b c d e f)
+type T7 a b c d e f g = (a, T6 b c d e f g)
+type T8 a b c d e f g h = (a, T7 b c d e f g h)
+type T9 a b c d e f g h a9 = (a, T8 b c d e f g h a9)
+type T10 a b c d e f g h a9 a10 = (a, T9 b c d e f g h a9 a10)
+type T11 a b c d e f g h a9 a10 a11 = (a, T10 b c d e f g h a9 a10 a11)
+type T12 a b c d e f g h a9 a10 a11 a12 = (a, T11 b c d e f g h a9 a10 a11 a12)
+type T13 a b c d e f g h a9 a10 a11 a12 a13 =
+  (a, T12 b c d e f g h a9 a10 a11 a12 a13)
+type T14 a b c d e f g h a9 a10 a11 a12 a13 a14 =
+  (a, T13 b c d e f g h a9 a10 a11 a12 a13 a14)
+type T15 a b c d e f g h a9 a10 a11 a12 a13 a14 a15 =
+  (a, T14 b c d e f g h a9 a10 a11 a12 a13 a14 a15)
+type T16 a b c d e f g h a9 a10 a11 a12 a13 a14 a15 a16 =
+  (a, T15 b c d e f g h a9 a10 a11 a12 a13 a14 a15 a16)
+type T17 a b c d e f g h a9 a10 a11 a12 a13 a14 a15 a16 a17 =
+  (a, T16 b c d e f g h a9 a10 a11 a12 a13 a14 a15 a16 a17)
+type T18 a b c d e f g h a9 a10 a11 a12 a13 a14 a15 a16 a17 a18 =
+  (a, T17 b c d e f g h a9 a10 a11 a12 a13 a14 a15 a16 a17 a18)
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,29 @@
+Copyright (c) 2013, Karamaan Group LLC
+
+All rights reserved.
+
+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 Karamaan Group LLC 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 KARAMAAN GROUP LLC OR ITS AFFILIATES 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/product-profunctors.cabal b/product-profunctors.cabal
new file mode 100644
--- /dev/null
+++ b/product-profunctors.cabal
@@ -0,0 +1,28 @@
+name:          product-profunctors
+version:       0.5
+synopsis:      product-profunctors
+description:   Product profunctors
+homepage:      https://github.com/tomjaguarpaw/product-profunctors
+license:       BSD3
+license-File:  LICENSE
+author:        Purely Agile
+maintainer:    Purely Agile
+category:      Control, Category
+build-type:    Simple
+cabal-version: >= 1.6
+
+source-repository head
+  Type:     git
+  Location: https://github.com/tomjaguarpaw/product-profunctors
+
+library
+  build-depends:   base >= 4 && < 5
+                 , profunctors >= 4.0 && < 4.3
+                 , contravariant >= 0.4 && < 1.3
+                 , template-haskell
+  exposed-modules: Data.Profunctor.Product,
+                   Data.Profunctor.Product.Default,
+                   Data.Profunctor.Product.Flatten,
+                   Data.Profunctor.Product.TH,
+                   Data.Profunctor.Product.Tuples
+  ghc-options:     -Wall
