diff --git a/.ghci b/.ghci
new file mode 100644
--- /dev/null
+++ b/.ghci
@@ -0,0 +1,1 @@
+:set -isrc -idist/build/autogen -optP-include -optPdist/build/autogen/cabal_macros.h
diff --git a/.gitignore b/.gitignore
new file mode 100644
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,9 @@
+dist
+docs
+wiki
+TAGS
+tags
+wip
+.DS_Store
+.*.swp
+.*.swo
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,32 @@
+language: haskell
+before_install:
+  # Uncomment whenever hackage is down.
+  # - mkdir -p ~/.cabal && cp travis/config ~/.cabal/config && cabal update
+
+  # grab lens 3.8
+  - git clone https://github.com/ekmett/lens.git
+  - cd lens
+  - travis/cabal-apt-install --only-dependencies --force-reinstall
+  - cabal install
+  - cd ..
+
+  # Try installing some of the build-deps with apt-get for speed.
+  - travis/cabal-apt-install --only-dependencies --force-reinstall $mode
+
+install:
+  - cabal configure $mode
+  - cabal build
+
+script:
+  - $script
+
+notifications:
+  irc:
+    channels:
+      - "irc.freenode.org#haskell-lens"
+    skip_join: true
+    template:
+      - "\x0313tables\x03/\x0306%{branch}\x03 \x0314%{commit}\x03 %{build_url} %{message}"
+
+env:
+  - mode="--enable-tests" script="cabal test --show-details=always"
diff --git a/.vim.custom b/.vim.custom
new file mode 100644
--- /dev/null
+++ b/.vim.custom
@@ -0,0 +1,31 @@
+" Add the following to your .vimrc to automatically load this on startup
+
+" if filereadable(".vim.custom")
+"     so .vim.custom
+" endif
+
+function StripTrailingWhitespace()
+  let myline=line(".")
+  let mycolumn = col(".")
+  silent %s/  *$//
+  call cursor(myline, mycolumn)
+endfunction
+
+" enable syntax highlighting
+syntax on
+
+" search for the tags file anywhere between here and /
+set tags=TAGS;/
+
+" highlight tabs and trailing spaces
+set listchars=tab:‗‗,trail:‗
+set list
+
+" f2 runs hasktags
+map <F2> :exec ":!hasktags -x -c --ignore src"<CR><CR>
+
+" strip trailing whitespace before saving
+" au BufWritePre *.hs,*.markdown silent! cal StripTrailingWhitespace()
+
+" rebuild hasktags after saving
+au BufWritePost *.hs silent! :exec ":!hasktags -x -c --ignore src"
diff --git a/CHANGELOG.markdown b/CHANGELOG.markdown
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.markdown
@@ -0,0 +1,4 @@
+0.1
+---
+* Initial public release
+* Repository initialized
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright 2012-2013 Edward Kmett
+
+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 the author nor the names of his contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``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 AUTHORS 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.markdown b/README.markdown
new file mode 100644
--- /dev/null
+++ b/README.markdown
@@ -0,0 +1,88 @@
+Tables
+======
+
+[![Build Status](https://secure.travis-ci.org/lens/tables.png?branch=master)](http://travis-ci.org/lens/tables)
+
+This package provides simple in memory data tables with multiple indices.
+
+Examples
+--------
+
+So if load `examples/Foo.hs` into `ghci`, we start with:
+
+```haskell
+>>> test
+fromList [ Foo {fooId = 1, fooBar = "One", fooBaz = 1.0}
+         , Foo {fooId = 2, fooBar = "Two", fooBaz = 2.0}
+         , Foo {fooId = 3, fooBar = "Three", fooBaz = 3.0}
+         , Foo {fooId = 4, fooBar = "Four", fooBaz = 4.0}
+         , Foo {fooId = 5, fooBar = "Five", fooBaz = 5.0} ]
+```
+
+We use uppercase constructor names to match on built-in keys
+
+```haskell
+>>> test ^. with FooId (<) 3
+fromList [ Foo {fooId = 1, fooBar = "One", fooBaz = 1.0}
+         , Foo {fooId = 2, fooBar = "Two", fooBaz = 2.0} ]
+```
+
+Then we can use any lowercase field accessor (or any other function) to do a non-keyed lookup or filter
+
+```haskell
+>>> test ^. with (length . fooBar) (<=) 3
+fromList [ Foo {fooId = 1, fooBar = "One", fooBaz = 1.0}
+         , Foo {fooId = 2, fooBar = "Two", fooBaz = 2.0} ]
+```
+
+You can delete by assigning to that filtered table:
+
+```haskell
+>>> test & with (length . fooBar) (<=) 3 .~ empty
+fromList [ Foo {fooId = 3, fooBar = "Three", fooBaz = 3.0}
+         , Foo {fooId = 4, fooBar = "Four", fooBaz = 4.0}
+         , Foo {fooId = 5, fooBar = "Five", fooBaz = 5.0} ]
+```
+
+You can edit the actual type of the fields if the table is configured to allow it:
+
+```haskell
+>>> test & rows.fooBar_ %~ length
+fromList [ Foo {fooId = 1, fooBar = 3, fooBaz = 1.0}
+         , Foo {fooId = 2, fooBar = 3, fooBaz = 2.0}
+         , Foo {fooId = 3, fooBar = 5, fooBaz = 3.0}
+         , Foo {fooId = 4, fooBar = 4, fooBaz = 4.0}
+         , Foo {fooId = 5, fooBar = 4, fooBaz = 5.0} ]
+```
+
+If you edit multiple fields, the edits all take place at the same time. so we can offset or swap a bunch of keys:
+
+```haskell
+>>> test & with FooId (>=) 2.rows.fooId_ +~ 1
+fromList [ Foo {fooId = 1, fooBar = "One", fooBaz = 1.0}
+         , Foo {fooId = 3, fooBar = "Two", fooBaz = 2.0}
+         , Foo {fooId = 4, fooBar = "Three", fooBaz = 3.0}
+         , Foo {fooId = 5, fooBar = "Four", fooBaz = 4.0}
+         , Foo {fooId = 6, fooBar = "Five", fooBaz = 5.0} ]
+```
+
+We can do grouping by arbitrary functions or fields similarly
+
+```haskell
+>>> test ^@.. group (length.fooBar)
+[ (3, fromList [ Foo {fooId = 1, fooBar = "One", fooBaz = 1.0}
+               , Foo {fooId = 2, fooBar = "Two", fooBaz = 2.0} ])
+, (4, fromList [ Foo {fooId = 4, fooBar = "Four", fooBaz = 4.0}
+               , Foo {fooId = 5, fooBar = "Five", fooBaz = 5.0} ])
+, (5, fromList [Foo {fooId = 3, fooBar = "Three", fooBaz = 3.0} ])
+]
+```
+
+Contact Information
+-------------------
+
+Contributions and bug reports are welcome!
+
+Please feel free to contact me through github or on the #haskell or #haskell-lens IRC channels on irc.freenode.net.
+
+-Edward Kmett
diff --git a/Setup.lhs b/Setup.lhs
new file mode 100644
--- /dev/null
+++ b/Setup.lhs
@@ -0,0 +1,42 @@
+#!/usr/bin/runhaskell
+\begin{code}
+module Main (main) where
+
+import Data.List ( nub )
+import Data.Version ( showVersion )
+import Distribution.Package ( PackageName(PackageName), PackageId, InstalledPackageId, packageVersion, packageName )
+import Distribution.Simple ( defaultMainWithHooks, UserHooks(..), simpleUserHooks )
+import Distribution.Simple.Utils ( rewriteFile, createDirectoryIfMissingVerbose )
+import Distribution.Verbosity ( Verbosity )
+import Distribution.Simple.BuildPaths ( autogenModulesDir )
+import Distribution.Simple.Setup ( BuildFlags(buildVerbosity), fromFlag )
+import Distribution.Simple.LocalBuildInfo ( LocalBuildInfo(libraryConfig, testSuiteConfigs), ComponentLocalBuildInfo(componentPackageDeps) )
+import System.FilePath ( (</>) )
+
+main :: IO ()
+main = defaultMainWithHooks simpleUserHooks
+  { buildHook = \pkg lbi hooks flags -> do
+     generateBuildModule "doctests" (fromFlag (buildVerbosity flags)) lbi
+     buildHook simpleUserHooks pkg lbi hooks flags
+  }
+
+generateBuildModule :: String -> Verbosity -> LocalBuildInfo -> IO ()
+generateBuildModule testSuite verbosity lbi = do
+  let dir = autogenModulesDir lbi
+  createDirectoryIfMissingVerbose verbosity True dir
+  rewriteFile (dir </> "Build_" ++ testSuite ++ ".hs") $ unlines
+    [ "module Build_" ++ testSuite ++ " where"
+    , "deps :: [String]"
+    , "deps = " ++ (show $ formatdeps (testDeps testSuite lbi))
+    ]
+  where
+    formatdeps = map (formatone . snd)
+    formatone p = case packageName p of
+      PackageName n -> n ++ "-" ++ showVersion (packageVersion p)
+
+testDeps :: String -> LocalBuildInfo -> [(InstalledPackageId, PackageId)]
+testDeps testSuite lbi = nub $
+     maybe [] componentPackageDeps (lookup testSuite (testSuiteConfigs lbi))
+  ++ maybe [] componentPackageDeps (libraryConfig lbi)
+
+\end{code}
diff --git a/examples/Foo.hs b/examples/Foo.hs
new file mode 100644
--- /dev/null
+++ b/examples/Foo.hs
@@ -0,0 +1,58 @@
+{-# LANGUAGE GADTs #-}
+{-# LANGUAGE RankNTypes #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE KindSignatures #-}
+{-# LANGUAGE TemplateHaskell #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE LiberalTypeSynonyms #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE FunctionalDependencies #-}
+module Foo where
+
+import Control.Applicative hiding (empty)
+import Control.Lens
+import Data.Data
+import Data.Foldable as Foldable
+import Data.Function (on)
+import Data.Functor.Identity
+import Data.List ((\\))
+import Data.Map (Map)
+import qualified Data.Map as Map
+import Data.Monoid
+import Data.Table
+import Data.Traversable
+import qualified Prelude
+import Prelude hiding (null)
+
+-- * Example Table
+
+data Foo a = Foo { fooId :: Int, fooBar :: a, fooBaz :: Double }
+  deriving (Eq,Ord,Show,Read,Data,Typeable)
+
+makeLensesWith ?? ''Foo $ defaultRules & lensField .~ \x -> Just (x ++ "_")
+
+instance Tabular (Foo a) where
+  type PKT (Foo a) = Int
+  data Key k (Foo a) b where
+    FooId  :: Key Primary   (Foo a) Int
+    FooBaz :: Key Candidate (Foo a) Double
+  data Tab (Foo a) i = FooTab (i Primary Int) (i Candidate Double)
+
+  fetch FooId  = fooId
+  fetch FooBaz = fooBaz
+
+  primary = FooId
+  primarily FooId r = r
+
+  mkTab f               = FooTab <$> f FooId   <*> f FooBaz
+  forTab (FooTab x y) f = FooTab <$> f FooId x <*> f FooBaz y
+  ixTab (FooTab x _) FooId  = x
+  ixTab (FooTab _ x) FooBaz = x
+
+  autoTab = autoIncrement fooId_
+
+test :: Table (Foo String)
+test = [Foo 0 "One" 1.0, Foo 0 "Two" 2.0, Foo 0 "Three" 3.0, Foo 0 "Four" 4.0, Foo 0 "Five" 5.0]^.table
diff --git a/src/Data/Table.hs b/src/Data/Table.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Table.hs
@@ -0,0 +1,894 @@
+{-# LANGUAGE FunctionalDependencies #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE UndecidableInstances #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE LiberalTypeSynonyms #-}
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE DeriveTraversable #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE DeriveFoldable #-}
+{-# LANGUAGE KindSignatures #-}
+{-# LANGUAGE EmptyDataDecls #-}
+{-# LANGUAGE DeriveFunctor #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE RankNTypes #-}
+{-# LANGUAGE GADTs #-}
+{-# LANGUAGE CPP #-}
+{-# OPTIONS_GHC -fno-warn-name-shadowing #-}
+
+#ifndef MIN_VERSION_containers
+#define MIN_VERSION_containers(x,y,z) 1
+#endif
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  Data.Table
+-- Copyright   :  (C) 2012-2013 Edward Kmett,
+-- License     :  BSD-style (see the file LICENSE)
+--
+-- Maintainer  :  Edward Kmett <ekmett@gmail.com>
+-- Stability   :  experimental
+-- Portability :  non-portable
+--
+-- This module provides tables with multiple indices that support a simple
+-- API based on the lenses and traversals from the @lens@ package.
+----------------------------------------------------------------------------
+module Data.Table
+  (
+  -- * Tables
+    Table(..)
+  , Tabular(..)
+  , Tab(..)
+  , Key(..)
+  -- ** Table Construction
+  , empty
+  , singleton
+  , table
+  , fromList
+  -- ** Reading and Writing
+  , null
+  , count
+  , With(..)
+  , Withal(..)
+  , Group(..)
+  , insert
+  , delete
+  , rows
+  , rows'
+  -- * Esoterica
+  , Auto(..)
+  , autoKey
+  , auto
+  , autoIncrement
+  -- * Implementation Details
+  , IsKeyType(..)
+  , KeyType(..)
+  , Primary
+  , Candidate, CandidateInt, CandidateHash
+  , Supplemental, SupplementalInt, SupplementalHash
+  , Inverted, InvertedInt, InvertedHash
+  , AnIndex(..)
+  ) where
+
+import Control.Applicative hiding (empty)
+import Control.Comonad
+import Control.Lens
+import Control.Monad
+import Control.Monad.Fix
+import Data.Data
+import Data.Foldable as F
+import Data.Function (on)
+import Data.Functor.Identity
+import Data.Hashable
+import Data.HashMap.Strict (HashMap)
+import qualified Data.HashMap.Strict as HM
+import Data.HashSet (HashSet)
+import qualified Data.HashSet as HS
+import Data.IntMap (IntMap)
+import qualified Data.IntMap as IM
+import Data.IntSet (IntSet)
+import qualified Data.IntSet as IS
+import Data.Map (Map)
+import qualified Data.Map as M
+import Data.Maybe
+import Data.Monoid
+import Data.Set (Set)
+import qualified Data.Set as S
+import Data.Traversable
+import qualified Prelude as P
+import Prelude hiding (null)
+
+{-# ANN module "HLint: ignore Reduce duplication" #-}
+{-# ANN module "HLint: ignore Eta reduce" #-}
+
+-- | This class describes how to index a user-defined data type.
+class Ord (PKT t) => Tabular (t :: *) where
+  -- | The primary key type
+  type PKT t
+
+  -- | Used to store indices
+  data Tab t m
+
+  -- | The type used internally for columns
+  data Key (k :: *) t :: * -> *
+
+  -- | Extract the value of a 'Key'
+  fetch :: Key k t a -> t -> a
+
+  -- | Every 'Table' has one 'Primary' 'Key'
+  primary :: Key Primary t (PKT t)
+
+  -- | ... and so if you find one, it had better be that one!
+  primarily :: Key Primary t a -> ((a ~ PKT t) => r) -> r
+
+  -- | Construct a 'Tab' given a function from key to index.
+  mkTab :: Applicative h => (forall k a. IsKeyType k a => Key k t a -> h (i k a)) -> h (Tab t i)
+
+  -- | Lookup an index in a 'Tab'
+  ixTab :: Tab t i -> Key k t a -> i k a
+
+  -- | Loop over each index in a 'Tab'
+  forTab :: Applicative h => Tab t i -> (forall k a . IsKeyType k a => Key k t a -> i k a -> h (j k a)) -> h (Tab t j)
+
+  -- | Adjust a record using meta-information about the table allowing for auto-increments.
+  autoTab :: t -> Maybe (Tab t (AnIndex t) -> t)
+  autoTab _ = Nothing
+  {-# INLINE autoTab #-}
+
+-- | This lets you define 'autoKey' to increment to 1 greater than the existing maximum key in a table.
+--
+-- In order to support this you need a numeric primary key, and the ability to update the primary key in a record, indicated by a
+-- lens to the field.
+--
+-- To enable auto-increment for a table with primary key @primaryKeyField@, set:
+--
+-- @'autoKey' = 'autoIncrement' primaryKeyField@
+autoIncrement :: (Tabular t, PKT t ~ Int) => ALens' t Int -> t -> Maybe (Tab t (AnIndex t) -> t)
+autoIncrement pk t
+  | t ^# pk == 0 = Just $ \ tb -> t & pk #~ 1 + fromMaybe 0 (tb ^? primaryMap.traverseMax.asIndex)
+  | otherwise    = Nothing
+{-# INLINE autoIncrement #-}
+
+-- | This is used to store a single index.
+data AnIndex t k a where
+  PrimaryMap          ::                       Map (PKT t) t      -> AnIndex t Primary          a
+  CandidateIntMap     ::                       IntMap t           -> AnIndex t CandidateInt     Int
+  CandidateHashMap    :: (Eq a, Hashable a) => HashMap a t        -> AnIndex t CandidateHash    a
+  CandidateMap        :: Ord a              => Map a t            -> AnIndex t Candidate        a
+  InvertedIntMap      ::                       IntMap [t]         -> AnIndex t InvertedInt      IntSet
+  InvertedHashMap     :: (Eq a, Hashable a) => HashMap a [t]      -> AnIndex t InvertedHash     (HashSet a)
+  InvertedMap         :: Ord a              => Map a [t]          -> AnIndex t Inverted         (Set a)
+  SupplementalIntMap  ::                       IntMap [t]         -> AnIndex t SupplementalInt  Int
+  SupplementalHashMap :: (Eq a, Hashable a) => HashMap a [t]      -> AnIndex t SupplementalHash a
+  SupplementalMap     :: Ord a              => Map a [t]          -> AnIndex t Supplemental     a
+
+-- | Find the primary key index a tab
+primaryMap :: Tabular t => Lens' (Tab t (AnIndex t)) (Map (PKT t) t)
+primaryMap f t = case ixTab t primary of
+  PrimaryMap m -> f m <&> \u -> runIdentity $ forTab t $ \k o -> Identity $ case o of
+    PrimaryMap _ -> primarily k (PrimaryMap u)
+    _              -> o
+{-# INLINE primaryMap #-}
+
+-- * Overloaded keys
+
+------------------------------------------------------------------------------
+-- Table
+------------------------------------------------------------------------------
+
+-- | Every 'Table' has a 'Primary' 'key' and may have 'Candidate',
+-- 'Supplemental' or 'Inverted' keys, plus their variants.
+data Table t where
+  EmptyTable ::                                   Table t
+  Table      :: Tabular t => Tab t (AnIndex t) -> Table t
+  deriving Typeable
+
+instance (Tabular t, Data t) => Data (Table t) where
+  gfoldl f z im = z fromList `f` toList im
+  toConstr _ = fromListConstr
+  gunfold k z c = case constrIndex c of
+    1 -> k (z fromList)
+    _ -> error "gunfold"
+  dataTypeOf _ = tableDataType
+  dataCast1 f = gcast1 f
+
+fromListConstr :: Constr
+fromListConstr = mkConstr tableDataType "fromList" [] Prefix
+
+tableDataType :: DataType
+tableDataType = mkDataType "Data.Table.Table" [fromListConstr]
+
+instance Monoid (Table t) where
+  mempty = EmptyTable
+  {-# INLINE mempty #-}
+
+  EmptyTable `mappend` r          = r
+  r          `mappend` EmptyTable = r
+  r@Table{}  `mappend` s          = F.foldl' (flip insert) r s
+  {-# INLINE mappend #-}
+
+instance Eq t => Eq (Table t) where
+  (==) = (==) `on` toList
+  {-# INLINE (==) #-}
+
+instance Ord t => Ord (Table t) where
+  compare = compare `on` toList
+  {-# INLINE compare #-}
+
+instance Show t => Show (Table t) where
+  showsPrec d t = showParen (d > 10) $ showString "fromList " . showsPrec 11 (toList t)
+
+instance (Tabular t, Read t) => Read (Table t) where
+  readsPrec d = readParen (d > 10) $ \r -> do
+    ("fromList",s) <- lex r
+    (m, t) <- readsPrec 11 s
+    return (fromList m, t)
+
+instance Foldable Table where
+  foldMap _ EmptyTable = mempty
+  foldMap f (Table m)  = foldMapOf (primaryMap.folded) f m
+  {-# INLINE foldMap #-}
+
+type instance Index (Table t) = PKT t
+type instance IxValue (Table t) = t
+
+instance Gettable f => Contains f (Table t) where
+  contains k f EmptyTable = coerce $ indexed f k False
+  contains k f (Table m) = Table <$> primaryMap (contains k f) m
+
+instance Applicative f => Ixed f (Table t) where
+  ix _ _ EmptyTable = pure EmptyTable
+  ix k f (Table m) = Table <$> primaryMap (ix k f) m
+  {-# INLINE ix #-}
+
+instance Tabular t => At (Table t) where
+  at k f EmptyTable = maybe EmptyTable singleton <$> indexed f k Nothing
+  at k f (Table m)  = Table <$> primaryMap (at k f) m
+  {-# INLINE at #-}
+
+deleteCollisions :: Table t -> [t] -> Table t
+deleteCollisions EmptyTable _ = EmptyTable
+deleteCollisions (Table tab) ts = Table $ runIdentity $ forTab tab $ \k i -> Identity $ case i of
+  PrimaryMap idx          -> PrimaryMap $ primarily k $ F.foldl' (flip (M.delete . fetch primary)) idx ts
+  CandidateMap idx        -> CandidateMap             $ F.foldl' (flip (M.delete . fetch k)) idx ts
+  CandidateIntMap idx     -> CandidateIntMap          $ F.foldl' (flip (IM.delete . fetch k)) idx ts
+  CandidateHashMap idx    -> CandidateHashMap         $ F.foldl' (flip (HM.delete . fetch k)) idx ts
+  SupplementalMap idx     -> SupplementalMap $ M.foldlWithKey' ?? idx ?? M.fromListWith (++) [ (fetch k t, [t]) | t <- ts ] $ \m ky ys ->
+    m & at ky . anon [] P.null %~ let pys = fetch primary <$> ys in filter (\e -> fetch primary e `P.notElem` pys)
+  SupplementalIntMap idx  -> SupplementalIntMap $ IM.foldlWithKey' ?? idx ?? IM.fromListWith (++) [ (fetch k t, [t]) | t <- ts ] $ \m ky ys ->
+    m & at ky . anon [] P.null %~ let pys = fetch primary <$> ys in filter (\e -> fetch primary e `P.notElem` pys)
+  SupplementalHashMap idx -> SupplementalHashMap $ HM.foldlWithKey' ?? idx ?? HM.fromListWith (++) [ (fetch k t, [t]) | t <- ts ] $ \m ky ys ->
+    m & at ky . anon [] P.null %~ let pys = fetch primary <$> ys in filter (\e -> fetch primary e `P.notElem` pys)
+  InvertedMap idx         -> InvertedMap     $ M.foldlWithKey' ?? idx ?? M.fromListWith (++) [ (f, [t]) | t <- ts, f <- S.toList $ fetch k t ] $ \m ky ys ->
+    m & at ky . anon [] P.null %~ let pys = fetch primary <$> ys in filter (\e -> fetch primary e `P.notElem` pys)
+  InvertedIntMap idx      -> InvertedIntMap  $ IM.foldlWithKey' ?? idx ?? IM.fromListWith (++) [ (f, [t]) | t <- ts, f <- IS.toList $ fetch k t ] $ \m ky ys ->
+    m & at ky . anon [] P.null %~ let pys = fetch primary <$> ys in filter (\e -> fetch primary e `P.notElem` pys)
+  InvertedHashMap idx     -> InvertedHashMap $ HM.foldlWithKey' ?? idx ?? HM.fromListWith (++) [ (f, [t]) | t <- ts, f <- HS.toList $ fetch k t ] $ \m ky ys ->
+    m & at ky . anon [] P.null %~ let pys = fetch primary <$> ys in filter (\e -> fetch primary e `P.notElem` pys)
+
+{-# INLINE deleteCollisions #-}
+
+emptyTab :: Tabular t => Tab t (AnIndex t)
+emptyTab = runIdentity $ mkTab $ \k -> Identity $ case keyType k of
+  Primary          -> primarily k (PrimaryMap M.empty)
+  Candidate        -> CandidateMap        M.empty
+  CandidateHash    -> CandidateHashMap    HM.empty
+  CandidateInt     -> CandidateIntMap     IM.empty
+  Inverted         -> InvertedMap         M.empty
+  InvertedHash     -> InvertedHashMap     HM.empty
+  InvertedInt      -> InvertedIntMap      IM.empty
+  Supplemental     -> SupplementalMap     M.empty
+  SupplementalHash -> SupplementalHashMap HM.empty
+  SupplementalInt  -> SupplementalIntMap  IM.empty
+{-# INLINE emptyTab #-}
+
+-- * Public API
+
+-- | Construct an empty relation
+empty :: Table t
+empty = EmptyTable
+{-# INLINE empty #-}
+
+-- | Check to see if the relation is empty
+null :: Table t -> Bool
+null EmptyTable = True
+null (Table m)  = M.null (m^.primaryMap)
+{-# INLINE null #-}
+
+-- | Construct a relation with a single row
+singleton :: Tabular t => t -> Table t
+singleton row = Table $ runIdentity $ mkTab $ \ k -> Identity $ case keyType k of
+  Primary          -> primarily k $ PrimaryMap $ M.singleton  (fetch k row) row
+  Candidate        -> CandidateMap             $ M.singleton  (fetch k row) row
+  CandidateInt     -> CandidateIntMap          $ IM.singleton (fetch k row) row
+  CandidateHash    -> CandidateHashMap         $ HM.singleton (fetch k row) row
+  Supplemental     -> SupplementalMap          $ M.singleton  (fetch k row) [row]
+  SupplementalInt  -> SupplementalIntMap       $ IM.singleton (fetch k row) [row]
+  SupplementalHash -> SupplementalHashMap      $ HM.singleton (fetch k row) [row]
+#if MIN_VERSION_containers(0,5,0)
+  Inverted         -> InvertedMap              $ M.fromSet  (const [row]) (fetch k row)
+  InvertedInt      -> InvertedIntMap           $ IM.fromSet (const [row]) (fetch k row)
+#else
+  Inverted         -> InvertedMap              $ M.fromDistinctAscList  [ (e, [row]) | e <- S.toAscList  (fetch k row) ]
+  InvertedInt      -> InvertedIntMap           $ IM.fromDistinctAscList [ (e, [row]) | e <- IS.toAscList (fetch k row) ]
+#endif
+  InvertedHash     -> InvertedHashMap          $ HS.foldl' (\m k -> HM.insert k [row] m) HM.empty (fetch k row)
+{-# INLINE singleton #-}
+
+-- | Return the set of rows that would be delete by deleting or inserting this row
+collisions :: t -> Table t -> [t]
+collisions _ EmptyTable = []
+collisions t (Table m)  = getConst $ forTab m $ \k i -> Const $ case i of
+  PrimaryMap idx       -> primarily k $ idx^..ix (fetch k t)
+  CandidateMap idx     ->               idx^..ix (fetch k t)
+  CandidateIntMap idx  ->               idx^..ix (fetch k t)
+  CandidateHashMap idx ->               idx^..ix (fetch k t)
+  _                  -> []
+{-# INLINE collisions #-}
+
+-- | Delete this row from the database. This will remove any row that collides with the specified
+-- row on any primary or candidate key.
+delete :: t -> Table t -> Table t
+delete t m = deleteCollisions m (collisions t m)
+{-# INLINE delete #-}
+
+-- | Insert a row into a relation, removing collisions.
+insert :: Tabular t => t -> Table t -> Table t
+insert t0 r = case autoTab t0 of
+  Just p -> case r of
+    EmptyTable -> go (p emptyTab)
+    Table m    -> go (p m)
+  Nothing -> go t0
+  where
+  go t = case delete t r of
+    EmptyTable -> singleton t
+    Table m -> Table $ runIdentity $ forTab m $ \k i -> Identity $ case i of
+      PrimaryMap idx          -> primarily k $ PrimaryMap $ idx & at (fetch k t) ?~ t
+      CandidateMap idx        -> CandidateMap             $ idx & at (fetch k t) ?~ t
+      CandidateIntMap idx     -> CandidateIntMap          $ idx & at (fetch k t) ?~ t
+      CandidateHashMap idx    -> CandidateHashMap         $ idx & at (fetch k t) ?~ t
+      SupplementalMap idx     -> SupplementalMap          $ idx & at (fetch k t) . anon [] P.null %~ (t:)
+      SupplementalIntMap idx  -> SupplementalIntMap       $ idx & at (fetch k t) . anon [] P.null %~ (t:)
+      SupplementalHashMap idx -> SupplementalHashMap      $ idx & at (fetch k t) . anon [] P.null %~ (t:)
+      InvertedMap idx         -> InvertedMap              $ idx & flip (F.foldr $ \ik -> at ik . anon [] P.null %~ (t:)) (fetch k t)
+      InvertedIntMap idx      -> InvertedIntMap           $ idx & flip (IS.foldr $ \ik -> at ik . anon [] P.null %~ (t:)) (fetch k t)
+      InvertedHashMap idx     -> InvertedHashMap          $ idx & flip (F.foldr $ \ik -> at ik . anon [] P.null %~ (t:)) (fetch k t)
+  {-# INLINE go #-}
+{-# INLINE insert #-}
+
+-- | Retrieve a row count.
+count :: Table t -> Int
+count EmptyTable = 0
+count (Table m)  = M.size (m^.primaryMap)
+
+-- | Convert a list to and from a 'Table'.
+--
+-- The real isomorphism laws hold if the original list makes no use of the auto-increment
+-- functionality of the table, has no duplicates and is sorted according to the primary key.
+--
+-- However,
+--
+-- @'from' 'table' '.' 'table' ≡ 'id'@
+--
+-- always holds.
+table :: Tabular t => Iso' [t] (Table t)
+table = iso fromList toList
+{-# INLINE table #-}
+
+instance (Tabular b, Applicative f, PKT a ~ PKT b) => Each f (Table a) (Table b) a b where
+  each _ EmptyTable = pure EmptyTable
+  each f (Table m)  = P.foldr insert empty <$> sequenceA (M.foldrWithKey (\i a r -> indexed f i a : r) [] $ m^.primaryMap)
+
+-- | Traverse all of the rows in a table without changing any types
+rows' :: Traversal' (Table t) t
+rows' _ EmptyTable = pure EmptyTable
+rows' f r@Table{} = P.foldr insert empty <$> traverse f (toList r)
+{-# INLINE rows' #-}
+
+-- | Traverse all of the rows in a table, potentially changing table types completely.
+rows :: Tabular t => Traversal (Table s) (Table t) s t
+rows f r = P.foldr insert empty <$> traverse f (toList r)
+{-# INLINE rows #-}
+
+class Group f q t i | q -> t i where
+  -- | Group by a given key or arbitrary function.
+  group :: Ord i => q -> IndexedLensLike' i f (Table t) (Table t)
+
+instance Applicative f => Group f (t -> a) t a where
+  group _  _ EmptyTable = pure EmptyTable
+  group ky f (Table m)  = traverse (\(k,vs) -> indexed f k (fromList vs)) (M.toList idx) <&> mconcat where
+    idx = M.fromListWith (++) (m^..primaryMap.folded.to(\v -> (ky v, [v])))
+  {-# INLINE group #-}
+
+instance Applicative f => Group f (Key Primary t a) t a where
+  group _  _ EmptyTable = pure EmptyTable
+  group ky f (Table m)  = case ixTab m ky of
+    PrimaryMap idx -> primarily ky $ for (toList idx) (\v -> indexed f (fetch primary v) (singleton v)) <&> mconcat
+  {-# INLINE group #-}
+
+instance Applicative f => Group f (Key Candidate t a) t a where
+  group _  _ EmptyTable = pure EmptyTable
+  group ky f (Table m)  = case ixTab m ky of
+    CandidateMap idx -> traverse (\(k,v) -> indexed f k (singleton v)) (M.toList idx) <&> mconcat
+  {-# INLINE group #-}
+
+instance (Applicative f, a ~ Int) => Group f (Key CandidateInt t a) t a where
+  group _  _ EmptyTable = pure EmptyTable
+  group ky f (Table m)  = case ixTab m ky of
+    CandidateIntMap idx -> traverse (\(k,v) -> indexed f k (singleton v)) (IM.toList idx) <&> mconcat
+  {-# INLINE group #-}
+
+instance Applicative f => Group f (Key CandidateHash t a) t a where
+  group _  _ EmptyTable = pure EmptyTable
+  group ky f (Table m)  = case ixTab m ky of
+    CandidateHashMap idx -> traverse (\(k,v) -> indexed f k (singleton v)) (HM.toList idx) <&> mconcat
+  {-# INLINE group #-}
+
+instance Applicative f => Group f (Key Supplemental t a) t a where
+  group _  _ EmptyTable = pure EmptyTable
+  group ky f (Table m)  = case ixTab m ky of
+    SupplementalMap idx -> traverse (\(k,vs) -> indexed f k (fromList vs)) (M.toList idx) <&> mconcat
+  {-# INLINE group #-}
+
+instance (Applicative f, a ~ Int) => Group f (Key SupplementalInt t a) t a where
+  group _  _ EmptyTable = pure EmptyTable
+  group ky f (Table m)  = case ixTab m ky of
+    SupplementalIntMap idx -> traverse (\(k,vs) -> indexed f k (fromList vs)) (IM.toList idx) <&> mconcat
+  {-# INLINE group #-}
+
+instance Applicative f => Group f (Key SupplementalHash t a) t a where
+  group _  _ EmptyTable = pure EmptyTable
+  group ky f (Table m)  = case ixTab m ky of
+    SupplementalHashMap idx -> traverse (\(k,vs) -> indexed f k (fromList vs)) (HM.toList idx) <&> mconcat
+  {-# INLINE group #-}
+
+instance (Applicative f, Gettable f) => Group f (Key Inverted t (Set a)) t a where
+  group _  _ EmptyTable = pure EmptyTable
+  group ky f (Table m)  = case ixTab m ky of
+    InvertedMap idx -> coerce $ traverse (\(k,vs) -> indexed f k (fromList vs)) $ M.toList idx
+
+instance (Applicative f, Gettable f, a ~ Int) => Group f (Key InvertedInt t IntSet) t a where
+  group _  _ EmptyTable = pure EmptyTable
+  group ky f (Table m)  = case ixTab m ky of
+    InvertedIntMap idx -> coerce $ traverse (\(k,vs) -> indexed f k (fromList vs)) $ IM.toList idx
+
+instance (Applicative f, Gettable f) => Group f (Key InvertedHash t (HashSet a)) t a where
+  group _  _ EmptyTable = pure EmptyTable
+  group ky f (Table m)  = case ixTab m ky of
+    InvertedHashMap idx -> coerce $ traverse (\(k,vs) -> indexed f k (fromList vs)) $ HM.toList idx
+
+-- | Search inverted indices
+class Withal q s t | q -> s t where
+  withAny :: q -> s -> Lens' (Table t) (Table t)
+  withAll ::q -> s -> Lens' (Table t) (Table t)
+
+  deleteWithAny :: q -> s -> Table t -> Table t
+  deleteWithAny p as t = set (withAny p as) empty t
+  {-# INLINE deleteWithAny #-}
+
+  deleteWithAll :: q -> s -> Table t -> Table t
+  deleteWithAll p as t = set (withAll p as) empty t
+  {-# INLINE deleteWithAll #-}
+
+instance Ord a => Withal (t -> [a]) [a] t where
+  withAny _ _  f EmptyTable  = f EmptyTable
+  withAny k as f r@(Table m) = go $ m^..primaryMap.folded.filtered (P.any (\e -> ss^.contains e) . k)
+    where go xs = f (xs^.table) <&> mappend (deleteCollisions r xs)
+          ss = S.fromList as
+  {-# INLINE withAny #-}
+
+  withAll _ _  f EmptyTable  = f EmptyTable
+  withAll k as f r@(Table m) = go $ m^..primaryMap.folded.filtered (P.all (\e -> ss^.contains e) . k)
+    where go xs = f (xs^.table) <&> mappend (deleteCollisions r xs)
+          ss = S.fromList as
+  {-# INLINE withAll #-}
+
+instance Ord a => Withal (Key Inverted t (Set a)) [a] t where
+  withAny _  _  f EmptyTable  = f EmptyTable
+  withAny ky as f r@(Table m) = go $ case ixTab m ky of
+    InvertedMap idx -> as >>= \a -> idx^..ix a.folded
+    where go xs = f (xs^.table) <&> mappend (deleteCollisions r xs)
+  {-# INLINE withAny #-}
+
+  withAll _  _  f EmptyTable  = f EmptyTable
+  withAll _  [] f r           = f r -- every row has all of an empty list of keywords
+  withAll ky (a:as) f r@(Table m) = case ixTab m ky of
+    InvertedMap idx -> let mkm c = M.fromList [ (fetch primary v, v) | v <- idx^..ix c.folded ]
+                         in go $ F.toList $ F.foldl' (\r -> M.intersection r . mkm) (mkm a) as
+    where go xs = f (xs^.table) <&> mappend (deleteCollisions r xs)
+  {-# INLINE withAll #-}
+
+instance Withal (Key InvertedInt t (IntSet)) [Int] t where
+  withAny _  _  f EmptyTable  = f EmptyTable
+  withAny ky as f r@(Table m) = go $ case ixTab m ky of
+    InvertedIntMap idx -> as >>= \a -> idx^..ix a.folded
+    where go xs = f (xs^.table) <&> mappend (deleteCollisions r xs)
+  {-# INLINE withAny #-}
+
+  withAll _  _  f EmptyTable  = f EmptyTable
+  withAll _  [] f r           = f r -- every row has all of an empty list of keywords
+  withAll ky (a:as) f r@(Table m) = case ixTab m ky of
+    InvertedIntMap idx -> let mkm c = M.fromList [ (fetch primary v, v) | v <- idx^..ix c.folded ]
+                          in go $ F.toList $ F.foldl' (\r -> M.intersection r . mkm) (mkm a) as
+    where go xs = f (xs^.table) <&> mappend (deleteCollisions r xs)
+  {-# INLINE withAll #-}
+
+instance (Eq a, Hashable a) =>Withal (Key InvertedHash t (HashSet a)) [a] t where
+  withAny _  _  f EmptyTable  = f EmptyTable
+  withAny ky as f r@(Table m) = go $ case ixTab m ky of
+    InvertedHashMap idx -> as >>= \a -> idx^..ix a.folded
+    where go xs = f (xs^.table) <&> mappend (deleteCollisions r xs)
+  {-# INLINE withAny #-}
+
+  withAll _  _  f EmptyTable  = f EmptyTable
+  withAll _  [] f r           = f r -- every row has all of an empty list of keywords
+  withAll ky (a:as) f r@(Table m) = case ixTab m ky of
+    InvertedHashMap idx -> let mkm c = M.fromList [ (fetch primary v, v) | v <- idx^..ix c.folded ]
+                          in go $ F.toList $ F.foldl' (\r -> M.intersection r . mkm) (mkm a) as
+    where go xs = f (xs^.table) <&> mappend (deleteCollisions r xs)
+  {-# INLINE withAll #-}
+
+
+class With q t | q -> t where
+  -- | Select a smaller, updateable subset of the rows of a table using an index or an arbitrary function.
+  with :: Ord a => q a -> (forall x. Ord x => x -> x -> Bool) -> a -> Lens' (Table t) (Table t)
+
+  -- | Delete selected rows from a table
+  --
+  -- @'deleteWith' p cmp a t ≡ 'set' ('with' p cmp a) 'empty' t@
+  deleteWith :: Ord a => q a -> (forall x. Ord x => x -> x -> Bool) -> a -> Table t -> Table t
+  deleteWith p cmp a t = set (with p cmp a) empty t
+  {-# INLINE deleteWith #-}
+
+instance With ((->) t) t where
+  with _  _   _ f EmptyTable  = f EmptyTable
+  with ky cmp a f r@(Table m)
+    | lt && eq && gt = f r
+    | lt || eq || gt = go $ m^..primaryMap.folded.filtered (\row -> cmp (ky row) a)
+    | otherwise      = f EmptyTable <&> mappend r
+    where
+      lt = cmp LT EQ
+      eq = cmp EQ EQ
+      gt = cmp GT EQ
+      go xs = f (xs^.table) <&> mappend (deleteCollisions r xs)
+  {-# INLINE with #-}
+
+instance With (Key Primary t) t where
+  with _   _   _ f EmptyTable  = f EmptyTable
+  with ky cmp a f r@(Table m)
+    | lt && eq && gt = f r
+    | not lt && eq && not gt = primarily ky $ go $ m^..primaryMap.ix a
+    | lt || eq || gt = primarily ky $ go $ case M.splitLookup a (m^.primaryMap) of
+        (l,e,g) -> (if lt then F.toList l else []) ++ (if eq then F.toList e else []) ++ (if gt then F.toList g else [])
+    | otherwise = f EmptyTable <&> mappend r
+    where
+      lt = cmp LT EQ
+      eq = cmp EQ EQ
+      gt = cmp GT EQ
+      go xs = f (xs^.table) <&> mappend (deleteCollisions r xs)
+  {-# INLINE with #-}
+
+instance With (Key Candidate t) t where
+  with _   _   _ f EmptyTable  = f EmptyTable
+  with ky cmp a f r@(Table m)
+    | lt && eq && gt = f r
+    | not lt && eq && not gt = case ixTab m ky of
+      CandidateMap idx    -> go $ idx^..ix a
+    | lt || eq || gt = case ixTab m ky of
+      CandidateMap idx -> go $ case M.splitLookup a idx of
+        (l,e,g) -> (if lt then F.toList l else []) ++ (if eq then F.toList e else []) ++ (if gt then F.toList g else [])
+    | otherwise = f EmptyTable <&> mappend r -- no match
+    where
+        lt = cmp LT EQ
+        eq = cmp EQ EQ
+        gt = cmp GT EQ
+        go xs = f (xs^.table) <&> mappend (deleteCollisions r xs)
+  {-# INLINE with #-}
+
+instance With (Key CandidateInt t) t where
+  with _   _   _ f EmptyTable  = f EmptyTable
+  with ky cmp a f r@(Table m)
+    | lt && eq && gt = f r
+    | not lt && eq && not gt = case ixTab m ky of
+      CandidateIntMap idx    -> go $ idx^..ix a
+    | lt || eq || gt = case ixTab m ky of
+      CandidateIntMap idx -> go $ case IM.splitLookup a idx of
+        (l,e,g) -> (if lt then F.toList l else []) ++ (if eq then F.toList e else []) ++ (if gt then F.toList g else [])
+    | otherwise = f EmptyTable <&> mappend r -- no match
+    where
+        lt = cmp LT EQ
+        eq = cmp EQ EQ
+        gt = cmp GT EQ
+        go xs = f (xs^.table) <&> mappend (deleteCollisions r xs)
+  {-# INLINE with #-}
+
+instance With (Key CandidateHash t) t where
+  with _   _   _ f EmptyTable  = f EmptyTable
+  with ky cmp a f r@(Table m)
+    | lt     && eq && gt     = f r
+    | not lt && eq && not gt = case ixTab m ky of CandidateHashMap idx    -> go $ idx^..ix a
+    | lt     || eq || gt     = go $ m^..primaryMap.folded.filtered (\row -> cmp (fetch ky row) a) -- table scan
+    | otherwise              = f EmptyTable <&> mappend r -- no match
+    where
+        lt = cmp LT EQ
+        eq = cmp EQ EQ
+        gt = cmp GT EQ
+        go xs = f (xs^.table) <&> mappend (deleteCollisions r xs)
+  {-# INLINE with #-}
+
+instance With (Key Supplemental t) t where
+  with _   _   _ f EmptyTable  = f EmptyTable
+  with ky cmp a f r@(Table m)
+    | lt && eq && gt = f r -- all rows
+    | not lt && eq && not gt = case ixTab m ky of
+      SupplementalMap idx -> go $ idx^..ix a.folded
+    | lt || eq || gt = go $ case ixTab m ky of
+      SupplementalMap idx -> case M.splitLookup a idx of
+        (l,e,g) -> (if lt then F.concat l else []) ++ (if eq then F.concat e else []) ++ (if gt then F.concat g else [])
+    | otherwise      = f EmptyTable <&> mappend r -- no match
+    where
+        lt = cmp LT EQ
+        eq = cmp EQ EQ
+        gt = cmp GT EQ
+        go xs = f (xs^.table) <&> mappend (deleteCollisions r xs)
+  {-# INLINE with #-}
+
+instance With (Key SupplementalInt t) t where
+  with _   _   _ f EmptyTable  = f EmptyTable
+  with ky cmp a f r@(Table m)
+    | lt && eq && gt = f r -- all rows
+    | not lt && eq && not gt = case ixTab m ky of
+      SupplementalIntMap idx -> go $ idx^..ix a.folded
+    | lt || eq || gt = go $ case ixTab m ky of
+      SupplementalIntMap idx -> case IM.splitLookup a idx of
+        (l,e,g) -> (if lt then F.concat l else []) ++ (if eq then F.concat e else []) ++ (if gt then F.concat g else [])
+    | otherwise      = f EmptyTable <&> mappend r -- no match
+    where
+        lt = cmp LT EQ
+        eq = cmp EQ EQ
+        gt = cmp GT EQ
+        go xs = f (xs^.table) <&> mappend (deleteCollisions r xs)
+  {-# INLINE with #-}
+
+instance With (Key SupplementalHash t) t where
+  with _   _   _ f EmptyTable  = f EmptyTable
+  with ky cmp a f r@(Table m)
+    |     lt && eq &&     gt = f r -- all rows
+    | not lt && eq && not gt = case ixTab m ky of SupplementalHashMap idx -> go $ idx^..ix a.folded
+    |     lt || eq ||     gt = go $ m^..primaryMap.folded.filtered (\row -> cmp (fetch ky row) a) -- table scan
+    | otherwise              = f EmptyTable <&> mappend r -- no match
+    where
+        lt = cmp LT EQ
+        eq = cmp EQ EQ
+        gt = cmp GT EQ
+        go xs = f (xs^.table) <&> mappend (deleteCollisions r xs)
+  {-# INLINE with #-}
+
+-- | Build up a table from a list
+fromList :: Tabular t => [t] -> Table t
+fromList = foldl' (flip insert) empty
+{-# INLINE fromList #-}
+
+-- * Lifting terms to types
+
+-- | Value-level key types
+data KeyType t a where
+  Primary          :: Ord a              => KeyType Primary          a
+  Candidate        :: Ord a              => KeyType Candidate        a
+  CandidateInt     ::                       KeyType CandidateInt     Int
+  CandidateHash    :: (Eq a, Hashable a) => KeyType CandidateHash    a
+  Supplemental     :: Ord a              => KeyType Supplemental     a
+  SupplementalInt  ::                       KeyType SupplementalInt  Int
+  SupplementalHash :: (Eq a, Hashable a) => KeyType SupplementalHash a
+  Inverted         :: Ord a              => KeyType Inverted         (Set a)
+  InvertedInt      ::                       KeyType InvertedInt      IntSet
+  InvertedHash     :: (Eq a, Hashable a) => KeyType InvertedHash     (HashSet a)
+
+-- |  Type level key types
+data Primary
+data Candidate
+data CandidateInt
+data CandidateHash
+data Supplemental
+data SupplementalInt
+data SupplementalHash
+data Inverted
+data InvertedInt
+data InvertedHash
+
+class IsKeyType k a where
+  keyType :: Key k t a -> KeyType k a
+
+instance Ord a => IsKeyType Primary a where
+  keyType _ = Primary
+  {-# INLINE keyType #-}
+
+instance Ord a => IsKeyType Candidate a where
+  keyType _ = Candidate
+  {-# INLINE keyType #-}
+
+instance a ~ Int => IsKeyType CandidateInt a where
+  keyType _ = CandidateInt
+  {-# INLINE keyType #-}
+
+instance (Eq a, Hashable a)=> IsKeyType CandidateHash a where
+  keyType _ = CandidateHash
+  {-# INLINE keyType #-}
+
+instance Ord a => IsKeyType Supplemental a where
+  keyType _ = Supplemental
+  {-# INLINE keyType #-}
+
+instance a ~ Int => IsKeyType SupplementalInt a where
+  keyType _ = SupplementalInt
+  {-# INLINE keyType #-}
+
+instance (Eq a, Hashable a)=> IsKeyType SupplementalHash a where
+  keyType _ = SupplementalHash
+  {-# INLINE keyType #-}
+
+instance Ord a => IsKeyType Inverted (Set a) where
+  keyType _ = Inverted
+  {-# INLINE keyType #-}
+
+instance a ~ [Int] => IsKeyType InvertedInt IntSet where
+  keyType _ = InvertedInt
+  {-# INLINE keyType #-}
+
+instance (Eq a, Hashable a)=> IsKeyType InvertedHash (HashSet a) where
+  keyType _ = InvertedHash
+  {-# INLINE keyType #-}
+
+class HasValue p q f s t a b | s -> a, t -> b, s b -> t, t a -> s where
+  value :: Overloading p q f s t a b
+
+------------------------------------------------------------------------------
+-- A simple table with an auto-incremented key
+------------------------------------------------------------------------------
+
+-- | Generate a row with an auto-incremented key
+auto :: a -> Auto a
+auto = Auto 0
+
+instance Field1 (Auto a) (Auto a) Int Int where
+  _1 f (Auto k a) = indexed f (0 :: Int) k <&> \k' -> Auto k' a
+
+instance Field2 (Auto a) (Auto b) a b where
+  _2 f (Auto k a) = indexed f (1 :: Int) a <&> Auto k
+
+type instance Index (Auto a) = Int
+
+instance (a ~ Int, b ~ Int, Applicative f) => Each f (Auto a) (Auto b) a b where
+  each f (Auto k a) = Auto <$> indexed f (0 :: Int) k <*> indexed f (1 :: Int) a
+
+data Auto a = Auto !Int a
+  deriving (Eq,Ord,Show,Read,Functor,Foldable,Traversable,Data,Typeable)
+
+autoKey :: Lens' (Auto a) Int
+autoKey f (Auto k a) = f k <&> \k' -> Auto k' a
+
+instance (Indexable Int p, q ~ (->), Functor f) => HasValue p q f (Auto a) (Auto b) a b where
+  value f (Auto k a) = indexed f k a <&> Auto k
+
+instance FunctorWithIndex Int Auto where
+  imap f (Auto k a) = Auto k (f k a)
+
+instance FoldableWithIndex Int Auto where
+  ifoldMap f (Auto k a) = f k a
+
+instance TraversableWithIndex Int Auto where
+  itraverse f (Auto k a) = Auto k <$> f k a
+
+instance Comonad Auto where
+  extract (Auto _ a) = a
+  extend f w@(Auto k _) = Auto k (f w)
+
+instance Tabular (Auto a) where
+  type PKT (Auto a) = Int
+  data Tab (Auto a) i = AutoTab (i Primary Int)
+  data Key p (Auto a) b where
+    AutoKey :: Key Primary (Auto a) Int
+  fetch AutoKey (Auto k _) = k
+  primary = AutoKey
+  primarily AutoKey r = r
+  mkTab f = AutoTab <$> f AutoKey
+  ixTab (AutoTab x) AutoKey = x
+  forTab (AutoTab x) f = AutoTab <$> f AutoKey x
+  autoTab = autoIncrement autoKey
+
+------------------------------------------------------------------------------
+-- A simple key-value pair, indexed on the key
+------------------------------------------------------------------------------
+
+instance (Indexable k p, q ~ (->), Functor f) => HasValue p q f (k, a) (k, b) a b where
+  value f (k, a) = indexed f k a <&> (,) k
+
+-- | Simple (key, value) pairs
+instance Ord k => Tabular (k,v) where
+  type PKT (k,v) = k
+  data Tab (k,v) i = KVTab (i Primary k)
+  data Key p (k,v) b where
+    Fst :: Key Primary (k,v) k
+  fetch Fst = fst
+  primary = Fst
+  primarily Fst r = r
+  mkTab f = KVTab <$> f Fst
+  ixTab (KVTab x) Fst = x
+  forTab (KVTab x) f = KVTab <$> f Fst x
+
+------------------------------------------------------------------------------
+-- Set-like tables with Identity
+------------------------------------------------------------------------------
+
+instance (Profunctor p, Functor f, p ~ q) => HasValue p q f (Identity a) (Identity b) a b where
+  value = unwrapped
+
+instance Ord a => Tabular (Identity a) where
+  type PKT (Identity a) = a
+  data Tab (Identity a) i = IdentityTab (i Primary a)
+  data Key p (Identity a) b where
+    Id :: Key Primary (Identity a) a
+  fetch Id = extract
+  primary = Id
+  primarily Id r = r
+  mkTab f = IdentityTab <$> f Id
+  ixTab (IdentityTab x) Id = x
+  forTab (IdentityTab x) f = IdentityTab <$> f Id x
+
+-----------------------------------------------------------------------------
+-- A simple value for set-like tables.
+-----------------------------------------------------------------------------
+
+instance Field1 (Value a) (Value b) a b where
+  _1 f (Value a) = Value <$> indexed f (0 :: Int) a
+
+type instance Index (Value a) = ()
+type instance IxValue (Value a) = a
+
+instance Functor f => Each f (Value a) (Value b) a b where
+  each f (Value a) = Value <$> indexed f () a
+
+instance Gettable f => Contains f (Value a) where
+  contains () pafb _ = coerce (indexed pafb () True)
+
+instance Functor f => Ixed f (Value a) where
+  ix () pafb (Value a) = Value <$> indexed pafb () a
+
+instance Wrapped a b (Value a) (Value b) where
+  wrapped = iso Value $ \(Value a) -> a
+
+data Value a = Value a
+  deriving (Eq,Ord,Show,Read,Functor,Foldable,Traversable,Data,Typeable)
+
+instance Applicative Value where
+  pure = Value
+  Value f <*> Value a = Value (f a)
+
+instance Monad Value where
+  return = Value
+  Value a >>= f = f a
+
+instance MonadFix Value where
+  mfix f = let m = f (extract m) in m
+
+instance Comonad Value where
+  extract (Value a) = a
+  extend f w@(Value _) = Value (f w)
+
+instance ComonadApply Value where
+  Value f <@> Value a = Value (f a)
+
+instance (Profunctor p, Functor f, p ~ q) => HasValue p q f (Value a) (Value b) a b where
+   value = unwrapped
+
+instance Ord a => Tabular (Value a) where
+  type PKT (Value a) = a
+  data Tab (Value a) i = ValueTab (i Primary a)
+  data Key p (Value a) b where
+    Val :: Key Primary (Value a) a
+  fetch Val = extract
+  primary = Val
+  primarily Val r = r
+  mkTab f = ValueTab <$> f Val
+  ixTab (ValueTab x) Val = x
+  forTab (ValueTab x) f = ValueTab <$> f Val x
diff --git a/tables.cabal b/tables.cabal
new file mode 100644
--- /dev/null
+++ b/tables.cabal
@@ -0,0 +1,81 @@
+name:          tables
+category:      Data, Lenses
+version:       0.1
+license:       BSD3
+cabal-version: >= 1.8
+license-file:  LICENSE
+author:        Edward A. Kmett
+maintainer:    Edward A. Kmett <ekmett@gmail.com>
+stability:     provisional
+homepage:      http://github.com/ekmett/tables/
+bug-reports:   http://github.com/ekmett/tables/issues
+copyright:     Copyright (C) 2012-2013 Edward A. Kmett
+synopsis:      In-memory storage with multiple keys using lenses and traversals
+description:
+  In-memory storage with multiple keys using lenses and traversals
+  .
+  For a quick tour, see <https://github.com/ekmett/tables#examples>
+
+build-type:    Custom
+tested-with:   GHC == 7.4.2
+extra-source-files:
+  .travis.yml
+  .ghci
+  .gitignore
+  .vim.custom
+  travis/config
+  travis/cabal-apt-install
+  examples/*.hs
+  README.markdown
+  CHANGELOG.markdown
+
+source-repository head
+  type: git
+  location: git://github.com/ekmett/tables.git
+
+flag test-properties
+  default: True
+  manual: True
+
+flag transformers2
+  default: False
+  manual: False
+
+library
+  build-depends:
+    base                 >= 4.3 && < 5,
+    comonad              >= 3   && < 4,
+    containers           >= 0.4 && < 0.6,
+    hashable             >= 1.1 && < 1.3,
+    lens                 >= 3.8 && < 4,
+    profunctors          >= 3.2 && < 4,
+    transformers         >= 0.2 && < 0.4,
+    transformers-compat  >= 0.1 && < 1,
+    unordered-containers == 0.2.*
+
+  exposed-modules:
+    Data.Table
+
+  if impl(ghc>=7.2)
+    other-extensions: Trustworthy
+    cpp-options: -DDEFAULT_SIGNATURES=1
+
+  ghc-options: -Wall -fwarn-tabs -O2 -fdicts-cheap -funbox-strict-fields
+  hs-source-dirs: src
+
+test-suite doctests
+  type:    exitcode-stdio-1.0
+  main-is: doctests.hs
+  build-depends:
+    base,
+    containers,
+    directory >= 1.0,
+    doctest >= 0.9.1,
+    filepath,
+    lens,
+    transformers,
+    unordered-containers
+  ghc-options: -Wall -threaded
+  if impl(ghc<7.6.1)
+    ghc-options: -Werror
+  hs-source-dirs: tests
diff --git a/tests/doctests.hs b/tests/doctests.hs
new file mode 100644
--- /dev/null
+++ b/tests/doctests.hs
@@ -0,0 +1,30 @@
+module Main where
+
+import Build_doctests (deps)
+import Control.Applicative
+import Control.Monad
+import Data.List
+import System.Directory
+import System.FilePath
+import Test.DocTest
+
+main :: IO ()
+main = getSources >>= \sources -> doctest $
+    "-isrc"
+  : "-idist/build/autogen"
+  : "-optP-include"
+  : "-optPdist/build/autogen/cabal_macros.h"
+  : "-hide-all-packages"
+  : map ("-package="++) deps ++ sources
+
+getSources :: IO [FilePath]
+getSources = filter (isSuffixOf ".hs") <$> go "src"
+  where
+    go dir = do
+      (dirs, files) <- getFilesAndDirectories dir
+      (files ++) . concat <$> mapM go dirs
+
+getFilesAndDirectories :: FilePath -> IO ([FilePath], [FilePath])
+getFilesAndDirectories dir = do
+  c <- map (dir </>) . filter (`notElem` ["..", "."]) <$> getDirectoryContents dir
+  (,) <$> filterM doesDirectoryExist c <*> filterM doesFileExist c
diff --git a/travis/cabal-apt-install b/travis/cabal-apt-install
new file mode 100644
--- /dev/null
+++ b/travis/cabal-apt-install
@@ -0,0 +1,16 @@
+#!/bin/sh
+set -eu
+
+sudo apt-get -q update
+sudo apt-get -q -y install dctrl-tools
+
+# Try installing some of the build-deps with apt-get for speed.
+eval "$(
+  printf '%s' "grep-aptavail -n -sPackage '(' -FFALSE -X FALSE ')'"
+  2>/dev/null cabal install "$@" --dry-run -v | \
+  sed -nre "s/^([^ ]+)-[0-9.]+ \(.*$/ -o '(' -FPackage -X libghc-\1-dev ')'/p" | \
+  xargs -d'\n'
+)" | sort -u | xargs -d'\n' sudo apt-get -q -y install -- libghc-quickcheck2-dev
+
+# Install whatever is still needed with cabal.
+cabal install "$@"
diff --git a/travis/config b/travis/config
new file mode 100644
--- /dev/null
+++ b/travis/config
@@ -0,0 +1,16 @@
+-- This provides a custom ~/.cabal/config file for use when hackage is down that should work on unix
+--
+-- This is particularly useful for travis-ci to get it to stop complaining
+-- about a broken build when everything is still correct on our end.
+--
+-- This uses Luite Stegeman's mirror of hackage provided by his 'hdiff' site instead
+--
+-- To enable this, uncomment the before_script in .travis.yml
+
+remote-repo: hdiff.luite.com:http://hdiff.luite.com/packages/archive
+remote-repo-cache: ~/.cabal/packages
+world-file: ~/.cabal/world
+build-summary: ~/.cabal/logs/build.log
+remote-build-reporting: anonymous
+install-dirs user
+install-dirs global
