diff --git a/app/App/Commands/CreateIndex.hs b/app/App/Commands/CreateIndex.hs
--- a/app/App/Commands/CreateIndex.hs
+++ b/app/App/Commands/CreateIndex.hs
@@ -1,28 +1,30 @@
-{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE BangPatterns     #-}
+{-# LANGUAGE DataKinds        #-}
+{-# LANGUAGE TypeApplications #-}
 
 module App.Commands.CreateIndex
   ( cmdCreateIndex
   ) where
 
 import App.Char
-import App.Commands.Options.Type
 import Control.Lens
 import Control.Monad
+import Data.Generics.Product.Any
 import Data.Semigroup                    ((<>))
 import HaskellWorks.Data.ByteString.Lazy
 import Options.Applicative               hiding (columns)
 
+import qualified App.Commands.Options.Type         as Z
 import qualified App.IO                            as IO
-import qualified App.Lens                          as L
 import qualified Data.ByteString.Builder           as B
 import qualified Data.ByteString.Lazy              as LBS
 import qualified HaskellWorks.Data.Dsv.Lazy.Cursor as SVL
 import qualified System.IO                         as IO
 
-runCreateIndex :: CreateIndexOptions -> IO ()
+runCreateIndex :: Z.CreateIndexOptions -> IO ()
 runCreateIndex opts = do
-  let !filePath   = opts ^. L.filePath
-  let !delimiter  = opts ^. L.delimiter
+  let !filePath   = opts ^. the @"filePath"
+  let !delimiter  = opts ^. the @"delimiter"
 
   !bs <- IO.readInputFile filePath
 
@@ -46,8 +48,8 @@
 
   return ()
 
-optsCreateIndex :: Parser CreateIndexOptions
-optsCreateIndex = CreateIndexOptions
+optsCreateIndex :: Parser Z.CreateIndexOptions
+optsCreateIndex = Z.CreateIndexOptions
   <$> strOption
         (   long "input"
         <>  short 'i'
diff --git a/app/App/Commands/Generate.hs b/app/App/Commands/Generate.hs
--- a/app/App/Commands/Generate.hs
+++ b/app/App/Commands/Generate.hs
@@ -1,17 +1,20 @@
+{-# LANGUAGE DataKinds        #-}
+{-# LANGUAGE TypeApplications #-}
+
 module App.Commands.Generate
   ( cmdGenerate
   ) where
 
-import App.Commands.Options.Type
 import Control.Lens
+import Data.Generics.Product.Any
 import Data.List
 import Data.Semigroup            ((<>))
 import Options.Applicative       hiding (columns)
 
-import qualified App.Gen        as G
-import qualified App.Lens       as L
-import qualified Hedgehog.Gen   as G
-import qualified Hedgehog.Range as R
+import qualified App.Commands.Options.Type as Z
+import qualified App.Gen                   as G
+import qualified Hedgehog.Gen              as G
+import qualified Hedgehog.Range            as R
 
 printField :: String -> String
 printField cs = if any invalid cs
@@ -20,19 +23,19 @@
   where invalid c = c == ',' || c == '\r' || c == '\n' || c == '"'
         escape    = ("\"" <>) . (<> "\"") . concatMap (\c -> if c == '"' then "\"\"" else [c])
 
-runGenerate :: GenerateOptions -> IO ()
+runGenerate :: Z.GenerateOptions -> IO ()
 runGenerate opts = do
-  let fields  = opts ^. L.fields
-  let rows    = opts ^. L.rows
+  let fields  = opts ^. the @"fields"
+  let rows    = opts ^. the @"rows"
 
-  csv <- G.sample (G.list (R.singleton rows) (G.list (R.singleton fields) (G.field))) :: IO [[String]]
+  csv <- G.sample (G.list (R.singleton rows) (G.list (R.singleton fields) G.field)) :: IO [[String]]
 
   putStr $ unlines $ map (intercalate "," . map printField) csv
 
   return ()
 
-optsGenerate :: Parser GenerateOptions
-optsGenerate = GenerateOptions
+optsGenerate :: Parser Z.GenerateOptions
+optsGenerate = Z.GenerateOptions
   <$> option auto
         (   long "fields"
         <>  help "Number of fields"
diff --git a/app/App/Commands/IndexWord8s.hs b/app/App/Commands/IndexWord8s.hs
--- a/app/App/Commands/IndexWord8s.hs
+++ b/app/App/Commands/IndexWord8s.hs
@@ -1,48 +1,51 @@
+{-# LANGUAGE DataKinds        #-}
+{-# LANGUAGE TypeApplications #-}
+
 module App.Commands.IndexWord8s
   ( cmdIndexWord8s
   ) where
 
-import App.Commands.Options.Type
 import Control.Lens
+import Data.Generics.Product.Any
 import Data.Semigroup                      ((<>))
 import HaskellWorks.Data.Vector.AsVector64
 import Options.Applicative
 
+import qualified App.Commands.Options.Type               as Z
 import qualified App.IO                                  as IO
-import qualified App.Lens                                as L
 import qualified Data.ByteString.Lazy                    as LBS
 import qualified HaskellWorks.Data.ByteString            as BS
 import qualified HaskellWorks.Data.ByteString.Lazy       as LBS
 import qualified HaskellWorks.Data.Simd.Comparison.Avx2  as AVX2
 import qualified HaskellWorks.Data.Simd.Comparison.Stock as STOCK
 
-runIndexWord8sNormal :: IndexWord8sOptions -> IO ()
+runIndexWord8sNormal :: Z.IndexWord8sOptions -> IO ()
 runIndexWord8sNormal opts = do
-  contents <- IO.readInputFile (opts ^. L.source)
+  contents <- IO.readInputFile (opts ^. the @"source")
 
-  IO.writeOutputFile (opts ^. L.target) $ contents
+  IO.writeOutputFile (opts ^. the @"target") $ contents
     & LBS.toLazyByteString
     . fmap (STOCK.cmpEqWord8s 44 . asVector64)
     . BS.resegmentPadded 64
     . LBS.toChunks
 
-runIndexWord8sSimd :: IndexWord8sOptions -> IO ()
+runIndexWord8sSimd :: Z.IndexWord8sOptions -> IO ()
 runIndexWord8sSimd opts = do
-  contents <- IO.readInputFile (opts ^. L.source)
+  contents <- IO.readInputFile (opts ^. the @"source")
 
-  IO.writeOutputFile (opts ^. L.target) $ contents
+  IO.writeOutputFile (opts ^. the @"target") $ contents
     & LBS.toLazyByteString
     . fmap (AVX2.cmpEqWord8s 44 . asVector64)
     . BS.resegmentPadded 64
     . LBS.toChunks
 
-runIndexWord8s :: IndexWord8sOptions -> IO ()
+runIndexWord8s :: Z.IndexWord8sOptions -> IO ()
 runIndexWord8s opts
-  | opts ^. L.simd  = runIndexWord8sSimd   opts
+  | opts ^. the @"simd"  = runIndexWord8sSimd   opts
   | otherwise       = runIndexWord8sNormal opts
 
-optsIndexWord8s :: Parser IndexWord8sOptions
-optsIndexWord8s = IndexWord8sOptions
+optsIndexWord8s :: Parser Z.IndexWord8sOptions
+optsIndexWord8s = Z.IndexWord8sOptions
   <$> strOption
         (   long "input"
         <>  help "Input file"
diff --git a/app/App/Commands/Options/Type.hs b/app/App/Commands/Options/Type.hs
--- a/app/App/Commands/Options/Type.hs
+++ b/app/App/Commands/Options/Type.hs
@@ -1,43 +1,47 @@
+{-# LANGUAGE DeriveGeneric         #-}
+{-# LANGUAGE DuplicateRecordFields #-}
+
 module App.Commands.Options.Type where
 
-import GHC.Word (Word8)
+import GHC.Generics
+import GHC.Word     (Word8)
 
 data CreateIndexOptions = CreateIndexOptions
-  { _createIndexOptionsFilePath  :: FilePath
-  , _createIndexOptionsDelimiter :: Word8
-  } deriving (Eq, Show)
+  { filePath  :: FilePath
+  , delimiter :: Word8
+  } deriving (Eq, Show, Generic)
 
 data QueryStrictOptions = QueryStrictOptions
-  { _queryStrictOptionsColumns        :: [Int]
-  , _queryStrictOptionsFilePath       :: FilePath
-  , _queryStrictOptionsOutputFilePath :: FilePath
-  , _queryStrictOptionsDelimiter      :: Word8
-  , _queryStrictOptionsOutDelimiter   :: Word8
-  , _queryStrictOptionsUseIndex       :: Bool
-  } deriving (Eq, Show)
+  { columns        :: [Int]
+  , filePath       :: FilePath
+  , outputFilePath :: FilePath
+  , delimiter      :: Word8
+  , outDelimiter   :: Word8
+  , useIndex       :: Bool
+  } deriving (Eq, Show, Generic)
 
 data GenerateOptions = GenerateOptions
-  { _generateOptionsFields :: Int
-  , _generateOptionsRows   :: Int
-  } deriving (Eq, Show)
+  { fields :: Int
+  , rows   :: Int
+  } deriving (Eq, Show, Generic)
 
 data CatOptions = CatOptions
-  { _catOptionsSource :: FilePath
-  , _catOptionsTarget :: FilePath
-  , _catOptionsSimd   :: Bool
-  } deriving (Eq, Show)
+  { source :: FilePath
+  , target :: FilePath
+  , simd   :: Bool
+  } deriving (Eq, Show, Generic)
 
 data IndexWord8sOptions = IndexWord8sOptions
-  { _indexWord8sOptionsSource :: FilePath
-  , _indexWord8sOptionsTarget :: FilePath
-  , _indexWord8sOptionsSimd   :: Bool
-  } deriving (Eq, Show)
+  { source :: FilePath
+  , target :: FilePath
+  , simd   :: Bool
+  } deriving (Eq, Show, Generic)
 
 data QueryLazyOptions = QueryLazyOptions
-  { _queryLazyOptionsColumns        :: [Int]
-  , _queryLazyOptionsFilePath       :: FilePath
-  , _queryLazyOptionsOutputFilePath :: FilePath
-  , _queryLazyOptionsDelimiter      :: Word8
-  , _queryLazyOptionsOutDelimiter   :: Word8
-  , _queryLazyOptionsMethod         :: String
-  } deriving (Eq, Show)
+  { columns        :: [Int]
+  , filePath       :: FilePath
+  , outputFilePath :: FilePath
+  , delimiter      :: Word8
+  , outDelimiter   :: Word8
+  , method         :: String
+  } deriving (Eq, Show, Generic)
diff --git a/app/App/Commands/QueryLazy.hs b/app/App/Commands/QueryLazy.hs
--- a/app/App/Commands/QueryLazy.hs
+++ b/app/App/Commands/QueryLazy.hs
@@ -1,25 +1,27 @@
 {-# LANGUAGE BangPatterns        #-}
+{-# LANGUAGE DataKinds           #-}
 {-# LANGUAGE OverloadedStrings   #-}
 {-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TypeApplications    #-}
 
 module App.Commands.QueryLazy
   ( cmdQueryLazy
   ) where
 
 import App.Char
-import App.Commands.Options.Type
 import Control.Applicative
 import Control.Lens
 import Control.Monad
 import Control.Monad.IO.Class       (liftIO)
 import Control.Monad.Trans.Resource
+import Data.Generics.Product.Any
 import Data.List
 import Data.Semigroup               ((<>))
 import Options.Applicative          hiding (columns)
 import Text.Read                    (readEither)
 
+import qualified App.Commands.Options.Type         as Z
 import qualified App.IO                            as IO
-import qualified App.Lens                          as L
 import qualified Data.ByteString                   as BS
 import qualified Data.ByteString.Builder           as B
 import qualified Data.ByteString.Lazy              as LBS
@@ -31,8 +33,8 @@
 defaultMethod :: String
 defaultMethod = "lazy-traverse"
 
-runQueryLazy :: QueryLazyOptions -> IO ()
-runQueryLazy opts = case opts ^. L.method of
+runQueryLazy :: Z.QueryLazyOptions -> IO ()
+runQueryLazy opts = case opts ^. the @"method" of
   "lazy-traverse"     -> runQueryLazyFast opts
   "strict-traverse"   -> runQueryLazySlow opts
   "strict-bytestring" -> runQueryLazyStrict opts
@@ -40,18 +42,18 @@
     IO.hPutStrLn IO.stderr $ "Unknown method: " <> method
     IO.exitFailure
 
-runQueryLazySlow :: QueryLazyOptions -> IO ()
+runQueryLazySlow :: Z.QueryLazyOptions -> IO ()
 runQueryLazySlow opts = do
-  !bs <- IO.readInputFile (opts ^. L.filePath)
+  !bs <- IO.readInputFile (opts ^. the @"filePath")
 
-  let !c = SVL.makeCursor (opts ^. L.delimiter) bs
+  let !c = SVL.makeCursor (opts ^. the @"delimiter") bs
   let !rows = SVL.toListVector c
-  let !outDelimiterBuilder = B.word8 (opts ^. L.outDelimiter)
+  let !outDelimiterBuilder = B.word8 (opts ^. the @"outDelimiter")
 
   runResourceT $ do
-    (_, hOut) <- IO.openOutputFile (opts ^. L.outputFilePath) Nothing
+    (_, hOut) <- IO.openOutputFile (opts ^. the @"outputFilePath") Nothing
     forM_ rows $ \row -> do
-      let fieldStrings = columnToFieldString row <$> (opts ^. L.columns)
+      let fieldStrings = columnToFieldString row <$> (opts ^. the @"columns")
 
       liftIO $ B.hPutBuilder hOut $ mconcat (intersperse outDelimiterBuilder fieldStrings) <> B.word8 10
 
@@ -62,18 +64,18 @@
           then B.lazyByteString (DV.unsafeIndex fields i)
           else B.lazyByteString LBS.empty
 
-runQueryLazyStrict :: QueryLazyOptions -> IO ()
+runQueryLazyStrict :: Z.QueryLazyOptions -> IO ()
 runQueryLazyStrict opts = do
-  !bs <- IO.readInputFile (opts ^. L.filePath)
+  !bs <- IO.readInputFile (opts ^. the @"filePath")
 
-  let !c = SVL.makeCursor (opts ^. L.delimiter) bs
+  let !c = SVL.makeCursor (opts ^. the @"delimiter") bs
   let !rows = SVL.toListVectorStrict c
-  let !outDelimiterBuilder = B.word8 (opts ^. L.outDelimiter)
+  let !outDelimiterBuilder = B.word8 (opts ^. the @"outDelimiter")
 
   runResourceT $ do
-    (_, hOut) <- IO.openOutputFile (opts ^. L.outputFilePath) Nothing
+    (_, hOut) <- IO.openOutputFile (opts ^. the @"outputFilePath") Nothing
     forM_ rows $ \row -> do
-      let fieldStrings = columnToFieldString row <$> (opts ^. L.columns)
+      let fieldStrings = columnToFieldString row <$> (opts ^. the @"columns")
 
       liftIO $ B.hPutBuilder hOut $ mconcat (intersperse outDelimiterBuilder fieldStrings) <> B.word8 10
 
@@ -84,17 +86,17 @@
           then B.byteString (DV.unsafeIndex fields i)
           else B.byteString BS.empty
 
-runQueryLazyFast :: QueryLazyOptions -> IO ()
+runQueryLazyFast :: Z.QueryLazyOptions -> IO ()
 runQueryLazyFast opts = do
-  !bs <- IO.readInputFile (opts ^. L.filePath)
+  !bs <- IO.readInputFile (opts ^. the @"filePath")
 
-  let !c = SVL.makeCursor (opts ^. L.delimiter) bs
-  let !sel = opts ^. L.columns
+  let !c = SVL.makeCursor (opts ^. the @"delimiter") bs
+  let !sel = opts ^. the @"columns"
   let !rows = SVL.selectListVector sel c
-  let !outDelimiterBuilder = B.word8 (opts ^. L.outDelimiter)
+  let !outDelimiterBuilder = B.word8 (opts ^. the @"outDelimiter")
 
   runResourceT $ do
-    (_, hOut) <- IO.openOutputFile (opts ^. L.outputFilePath) Nothing
+    (_, hOut) <- IO.openOutputFile (opts ^. the @"outputFilePath") Nothing
     forM_ rows $ \row -> do
       let fieldStrings = fmap B.lazyByteString row
 
@@ -113,8 +115,8 @@
     then Left "cannot index column 0"
     else Right (a - 1)
 
-optsQueryLazy :: Parser QueryLazyOptions
-optsQueryLazy = QueryLazyOptions
+optsQueryLazy :: Parser Z.QueryLazyOptions
+optsQueryLazy = Z.QueryLazyOptions
     <$> many
         ( nonZeroOneBased
           (   long "column"
diff --git a/app/App/Commands/QueryStrict.hs b/app/App/Commands/QueryStrict.hs
--- a/app/App/Commands/QueryStrict.hs
+++ b/app/App/Commands/QueryStrict.hs
@@ -1,44 +1,46 @@
 {-# LANGUAGE BangPatterns        #-}
+{-# LANGUAGE DataKinds           #-}
 {-# LANGUAGE OverloadedStrings   #-}
 {-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TypeApplications    #-}
 
 module App.Commands.QueryStrict
   ( cmdQueryStrict
   ) where
 
 import App.Char
-import App.Commands.Options.Type
 import Control.Applicative
 import Control.Lens
 import Control.Monad
 import Control.Monad.IO.Class       (liftIO)
 import Control.Monad.Trans.Resource
+import Data.Generics.Product.Any
 import Data.List
 import Data.Semigroup               ((<>))
 import Options.Applicative
 import Text.Read                    (readEither)
 
+import qualified App.Commands.Options.Type           as Z
 import qualified App.IO                              as IO
-import qualified App.Lens                            as L
 import qualified Data.ByteString                     as BS
 import qualified Data.ByteString.Builder             as B
 import qualified Data.Vector                         as DV
 import qualified HaskellWorks.Data.Dsv.Strict.Cursor as SVS
 
-runQueryStrict :: QueryStrictOptions -> IO ()
+runQueryStrict :: Z.QueryStrictOptions -> IO ()
 runQueryStrict opts = do
-  let delimiter     = opts ^. L.delimiter
-  let inputFilePath = opts ^. L.filePath
-  let useIndex      = opts ^. L.useIndex
+  let delimiter     = opts ^. the @"delimiter"
+  let inputFilePath = opts ^. the @"filePath"
+  let useIndex      = opts ^. the @"useIndex"
   c <- SVS.mmapCursor delimiter useIndex inputFilePath
 
   let !rows = SVS.toListVector c
-  let !outDelimiterBuilder = B.word8 (opts ^. L.outDelimiter)
+  let !outDelimiterBuilder = B.word8 (opts ^. the @"outDelimiter")
 
   runResourceT $ do
-    (_, hOut) <- IO.openOutputFile (opts ^. L.outputFilePath) Nothing
+    (_, hOut) <- IO.openOutputFile (opts ^. the @"outputFilePath") Nothing
     forM_ rows $ \row -> do
-      let fieldStrings = columnToFieldString row <$> (opts ^. L.columns)
+      let fieldStrings = columnToFieldString row <$> (opts ^. the @"columns")
 
       liftIO $ B.hPutBuilder hOut $ mconcat (intersperse outDelimiterBuilder fieldStrings) <> B.word8 10
 
@@ -60,8 +62,8 @@
     then Left "cannot index column 0"
     else Right (a - 1)
 
-optsQueryStrict :: Parser QueryStrictOptions
-optsQueryStrict = QueryStrictOptions
+optsQueryStrict :: Parser Z.QueryStrictOptions
+optsQueryStrict = Z.QueryStrictOptions
     <$> many
         ( nonZeroOneBased
           (   long "column"
diff --git a/app/App/Lens.hs b/app/App/Lens.hs
deleted file mode 100644
--- a/app/App/Lens.hs
+++ /dev/null
@@ -1,18 +0,0 @@
-{-# LANGUAGE FlexibleInstances      #-}
-{-# LANGUAGE FunctionalDependencies #-}
-{-# LANGUAGE MultiParamTypeClasses  #-}
-{-# LANGUAGE TemplateHaskell        #-}
-{-# LANGUAGE TypeFamilies           #-}
-{-# LANGUAGE TypeSynonymInstances   #-}
-
-module App.Lens where
-
-import App.Commands.Options.Type
-import Control.Lens
-
-makeFields ''CatOptions
-makeFields ''IndexWord8sOptions
-makeFields ''CreateIndexOptions
-makeFields ''GenerateOptions
-makeFields ''QueryLazyOptions
-makeFields ''QueryStrictOptions
diff --git a/hw-dsv.cabal b/hw-dsv.cabal
--- a/hw-dsv.cabal
+++ b/hw-dsv.cabal
@@ -1,9 +1,6 @@
--- This file has been generated from package.yaml by hpack version 0.18.1.
---
--- see: https://github.com/sol/hpack
-
+cabal-version:  2.2
 name:           hw-dsv
-version:        0.3.4
+version:        0.3.5
 synopsis:       Unbelievably fast streaming DSV file parser
 description:    Please see the README on Github at <https://github.com/haskell-works/hw-dsv#readme>
 category:       Text, CSV, SIMD, Succinct Data Structures, Data Structures
@@ -12,16 +9,14 @@
 author:         John Ky
 maintainer:     newhoggy@gmail.com
 copyright:      2018 John Ky
-license:        BSD3
+license:        BSD-3-Clause
 license-file:   LICENSE
-tested-with:    GHC == 8.6.1, GHC == 8.4.3, GHC == 8.2.2, GHC == 8.0.2, GHC == 7.10.3
+tested-with:    GHC == 8.6.4, GHC == 8.4.4, GHC == 8.2.2, GHC == 8.0.2
 build-type:     Simple
-cabal-version:  >= 1.10
-
 extra-source-files:
+    README.md
     ChangeLog.md
     data/bench/data-0001000.csv
-    README.md
 
 source-repository head
   type: git
@@ -42,40 +37,67 @@
   manual: False
   default: True
 
-library
-  hs-source-dirs:
-      src
-  ghc-options: -O2 -Wall
-  build-depends:
-      base                  >= 4.7      && < 5
-    , bits-extra            >= 0.0.1.2  && < 0.1
-    , bytestring            >= 0.10     && < 0.11
-    , deepseq               >= 1.4      && < 1.5
-    , ghc-prim
-    , hw-bits               >= 0.7.0.2  && < 0.8
-    , hw-rankselect         >= 0.12.0.2 && < 0.13
-    , hw-rankselect-base    >= 0.3.2.0  && < 0.4
-    , hw-prim               >= 0.6.2.14 && < 0.7
-    , hw-simd               >= 0.1.1.3  && < 0.2
-    , vector                >= 0.12.0.1 && < 0.13
+common base                 { build-depends: base                 >= 4.7        && < 5      }
+
+common bits-extra           { build-depends: bits-extra           >= 0.0.1.2    && < 0.1    }
+common bytestring           { build-depends: bytestring           >= 0.10       && < 0.11   }
+common cassava              { build-depends: cassava              >= 0.5.1.0    && < 0.6    }
+common criterion            { build-depends: criterion            >= 1.4.1.0    && < 1.6    }
+common deepseq              { build-depends: deepseq              >= 1.4        && < 1.5    }
+common directory            { build-depends: directory            >= 1.2.2      && < 1.4    }
+common generic-lens         { build-depends: generic-lens         >= 1.0.0.0    && < 1.2    }
+common ghc-prim             { build-depends: ghc-prim                                       }
+common hedgehog             { build-depends: hedgehog             >= 0.5        && < 0.7    }
+common hspec                { build-depends: hspec                >= 2.4        && < 3      }
+common hw-bits              { build-depends: hw-bits              >= 0.7.0.2    && < 0.8    }
+common hw-hspec-hedgehog    { build-depends: hw-hspec-hedgehog    >= 0.1.0.4    && < 0.2    }
+common hw-prim              { build-depends: hw-prim              >= 0.6.2.14   && < 0.7    }
+common hw-rankselect        { build-depends: hw-rankselect        >= 0.12.0.2   && < 0.14   }
+common hw-rankselect-base   { build-depends: hw-rankselect-base   >= 0.3.2.0    && < 0.4    }
+common hw-simd              { build-depends: hw-simd              >= 0.1.1.3    && < 0.2    }
+common lens                 { build-depends: lens                 >= 4.15       && < 5      }
+common mmap                 { build-depends: mmap                 >= 0.5.9      && < 0.6    }
+common optparse-applicative { build-depends: optparse-applicative >= 0.13       && < 0.15   }
+common resourcet            { build-depends: resourcet            >= 1.1        && < 1.3    }
+common text                 { build-depends: text                 >= 1.2.2      && < 2.0    }
+common vector               { build-depends: vector               >= 0.12.0.1   && < 0.13   }
+common weigh                { build-depends: weigh                >= 0.0.6      && < 0.1    }
+
+common semigroups           {  if (!impl(ghc >= 8.0.1)) { build-depends: semigroups   >= 0.8.4  && <  0.19  } }
+common transformers         {  if (!impl(ghc >= 8.0.1)) { build-depends: transformers >= 0.4    && <  0.6   } }
+
+common common
+  default-language:   Haskell2010
+  ghc-options:        -O2 -Wall
   if flag(sse42)
     ghc-options: -msse4.2
     cc-options: -msse4.2
   if flag(bmi2)
     cc-options: -mbmi2 -DBMI2_ENABLED
-  if (flag(bmi2)) && (impl(ghc >=8.4.1))
-    ghc-options: -mbmi2 -msse4.2
   if flag(avx2)
     cc-options: -mavx2 -DAVX2_ENABLED
+  if (impl(ghc >=8.0.1))
+    ghc-options: -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns -Wredundant-constraints
   if (flag(avx2)) && (impl(ghc >=8.4.1))
     ghc-options: -mbmi2 -msse4.2
     cpp-options: -DBMI2_ENABLED -DAVX2_ENABLED
-  if (impl(ghc >=8.0.1))
-    ghc-options: -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns -Wredundant-constraints
-  if (!impl(ghc >=8.0.1))
-    build-depends:
-        transformers          >= 0.4        && < 0.6
-      , semigroups            >= 0.8.4      && < 0.19
+  if (flag(bmi2)) && (impl(ghc >=8.4.1))
+    ghc-options: -mbmi2 -msse4.2
+
+library
+  import:   base, common
+          , bits-extra
+          , bytestring
+          , deepseq
+          , ghc-prim
+          , hw-bits
+          , hw-prim
+          , hw-rankselect
+          , hw-rankselect-base
+          , hw-simd
+          , semigroups
+          , transformers
+          , vector
   exposed-modules:
       HaskellWorks.Data.Dsv.Internal.Bits
       HaskellWorks.Data.Dsv.Internal.Broadword
@@ -89,50 +111,30 @@
       HaskellWorks.Data.Dsv.Strict.Cursor.Internal
       HaskellWorks.Data.Dsv.Strict.Cursor.Internal.Reference
       HaskellWorks.Data.Dsv.Strict.Cursor.Type
-  other-modules:
-      Paths_hw_dsv
-  default-language: Haskell2010
+  other-modules:    Paths_hw_dsv
+  autogen-modules:  Paths_hw_dsv
+  hs-source-dirs:   src
 
 executable hw-dsv
-  main-is: Main.hs
-  hs-source-dirs:
-      app
-  ghc-options: -O2 -Wall -threaded -rtsopts -with-rtsopts=-N
-  build-depends:
-      base                  >= 4.7      && < 5
-    , bits-extra            >= 0.0.1.2  && < 0.1
-    , bytestring            >= 0.10     && < 0.11
-    , deepseq               >= 1.4      && < 1.5
-    , ghc-prim
-    , hw-bits               >= 0.7.0.2  && < 0.8
-    , hw-rankselect         >= 0.12.0.2 && < 0.13
-    , hw-rankselect-base    >= 0.3.2.0  && < 0.4
-    , hw-prim               >= 0.6.2.14 && < 0.7
-    , hw-simd               >= 0.1.1.3  && < 0.2
-    , vector                >= 0.12.0.1 && < 0.13
-    , hw-dsv
-    , hedgehog                >= 0.5        && < 0.7
-    , lens                    >= 4.15       && < 5
-    , optparse-applicative    >= 0.13       && < 0.15
-    , resourcet               >= 1.1        && < 1.3
-  if flag(sse42)
-    ghc-options: -msse4.2
-    cc-options: -msse4.2
-  if flag(bmi2)
-    cc-options: -mbmi2 -DBMI2_ENABLED
-  if (flag(bmi2)) && (impl(ghc >=8.4.1))
-    ghc-options: -mbmi2 -msse4.2
-  if flag(avx2)
-    cc-options: -mavx2 -DAVX2_ENABLED
-  if (flag(avx2)) && (impl(ghc >=8.4.1))
-    ghc-options: -mbmi2 -msse4.2
-    cpp-options: -DBMI2_ENABLED -DAVX2_ENABLED
-  if (impl(ghc >=8.0.1))
-    ghc-options: -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns -Wredundant-constraints
-  if (!impl(ghc >=8.0.1))
-    build-depends:
-        transformers          >= 0.4        && < 0.6
-      , semigroups            >= 0.8.4      && < 0.19
+  import:   base, common
+          , bits-extra
+          , bytestring
+          , deepseq
+          , generic-lens
+          , ghc-prim
+          , hedgehog
+          , hw-bits
+          , hw-prim
+          , hw-rankselect
+          , hw-rankselect-base
+          , hw-simd
+          , lens
+          , optparse-applicative
+          , resourcet
+          , semigroups
+          , transformers
+          , vector
+  main-is:          Main.hs
   other-modules:
       App.Char
       App.Commands
@@ -144,139 +146,86 @@
       App.Commands.QueryStrict
       App.Gen
       App.IO
-      App.Lens
-  default-language: Haskell2010
+      Paths_hw_dsv
+  autogen-modules:  Paths_hw_dsv
+  hs-source-dirs:   app
+  ghc-options:      -threaded -rtsopts -with-rtsopts=-N
+  build-depends:    hw-dsv
 
 test-suite hw-dsv-space
-  type: exitcode-stdio-1.0
-  main-is: Space.hs
-  hs-source-dirs:
-      weigh
-  ghc-options: -O2 -Wall
-  build-depends:
-      base                  >= 4.7      && < 5
-    , bits-extra            >= 0.0.1.2  && < 0.1
-    , bytestring            >= 0.10     && < 0.11
-    , deepseq               >= 1.4      && < 1.5
-    , ghc-prim
-    , hw-bits               >= 0.7.0.2  && < 0.8
-    , hw-rankselect         >= 0.12.0.2 && < 0.13
-    , hw-rankselect-base    >= 0.3.2.0  && < 0.4
-    , hw-prim               >= 0.6.2.14 && < 0.7
-    , hw-simd               >= 0.1.1.3  && < 0.2
-    , vector                >= 0.12.0.1 && < 0.13
-    , cassava               >= 0.5.1.0    && < 0.6
-    , hw-dsv
-    , weigh                 >= 0.0.6      && < 0.1
-  if flag(sse42)
-    ghc-options: -msse4.2
-    cc-options: -msse4.2
-  if flag(bmi2)
-    cc-options: -mbmi2 -DBMI2_ENABLED
-  if (flag(bmi2)) && (impl(ghc >=8.4.1))
-    ghc-options: -mbmi2 -msse4.2
-  if flag(avx2)
-    cc-options: -mavx2 -DAVX2_ENABLED
-  if (flag(avx2)) && (impl(ghc >=8.4.1))
-    ghc-options: -mbmi2 -msse4.2
-    cpp-options: -DBMI2_ENABLED -DAVX2_ENABLED
-  if (impl(ghc >=8.0.1))
-    ghc-options: -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns -Wredundant-constraints
-  if (!impl(ghc >=8.0.1))
-    build-depends:
-        transformers          >= 0.4        && < 0.6
-      , semigroups            >= 0.8.4      && < 0.19
-  default-language: Haskell2010
+  import:   base, common
+          , bits-extra
+          , bytestring
+          , cassava
+          , deepseq
+          , ghc-prim
+          , hw-bits
+          , hw-prim
+          , hw-rankselect
+          , hw-rankselect-base
+          , hw-simd
+          , vector
+          , weigh
+  type:             exitcode-stdio-1.0
+  main-is:          Space.hs
+  build-depends:    hw-dsv
+  other-modules:    Paths_hw_dsv
+  autogen-modules:  Paths_hw_dsv
+  hs-source-dirs:   weigh
+  build-tool-depends: hspec-discover:hspec-discover
 
 test-suite hw-dsv-test
-  type: exitcode-stdio-1.0
-  main-is: Spec.hs
-  hs-source-dirs:
-      test
-  ghc-options: -O2 -Wall -threaded -rtsopts -with-rtsopts=-N
-  build-depends:
-      base                  >= 4.7      && < 5
-    , bits-extra            >= 0.0.1.2  && < 0.1
-    , bytestring            >= 0.10     && < 0.11
-    , deepseq               >= 1.4      && < 1.5
-    , ghc-prim
-    , hw-bits               >= 0.7.0.2  && < 0.8
-    , hw-rankselect         >= 0.12.0.2 && < 0.13
-    , hw-rankselect-base    >= 0.3.2.0  && < 0.4
-    , hw-prim               >= 0.6.2.14 && < 0.7
-    , hw-simd               >= 0.1.1.3  && < 0.2
-    , vector                >= 0.12.0.1 && < 0.13
-    , directory               >= 1.2.2      && < 1.4
-    , hw-dsv
-    , hedgehog                >= 0.5        && < 0.7
-    , hspec                   >= 2.4        && < 3
-    , hw-hspec-hedgehog       >= 0.1.0.4    && < 0.2
-    , text                    >= 1.2.2      && < 2.0
-  if flag(sse42)
-    ghc-options: -msse4.2
-    cc-options: -msse4.2
-  if flag(bmi2)
-    cc-options: -mbmi2 -DBMI2_ENABLED
-  if (flag(bmi2)) && (impl(ghc >=8.4.1))
-    ghc-options: -mbmi2 -msse4.2
-  if flag(avx2)
-    cc-options: -mavx2 -DAVX2_ENABLED
-  if (flag(avx2)) && (impl(ghc >=8.4.1))
-    ghc-options: -mbmi2 -msse4.2
-    cpp-options: -DBMI2_ENABLED -DAVX2_ENABLED
-  if (impl(ghc >=8.0.1))
-    ghc-options: -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns -Wredundant-constraints
-  if (!impl(ghc >=8.0.1))
-    build-depends:
-        transformers          >= 0.4        && < 0.6
-      , semigroups            >= 0.8.4      && < 0.19
+  import:   base, common
+          , bits-extra
+          , bytestring
+          , deepseq
+          , directory
+          , ghc-prim
+          , hedgehog
+          , hspec
+          , hw-bits
+          , hw-hspec-hedgehog
+          , hw-prim
+          , hw-rankselect
+          , hw-rankselect-base
+          , hw-simd
+          , text
+          , vector
+  ghc-options:      -threaded -rtsopts -with-rtsopts=-N
+  build-depends:    hw-dsv
+  type:             exitcode-stdio-1.0
+  main-is:          Spec.hs
   other-modules:
       HaskellWorks.Data.Dsv.BroadwordSpec
       HaskellWorks.Data.Dsv.Gen
       HaskellWorks.Data.Dsv.Lazy.CursorSpec
       HaskellWorks.Data.Dsv.Strict.Cursor.InternalSpec
       HaskellWorks.Data.DsvSpec
-  default-language: Haskell2010
+      Paths_hw_dsv
+  autogen-modules:  Paths_hw_dsv
+  hs-source-dirs:   test
+  build-tool-depends: hspec-discover:hspec-discover
 
 benchmark bench
-  type: exitcode-stdio-1.0
-  main-is: Main.hs
-  hs-source-dirs:
-      bench
-  ghc-options: -O2 -Wall -msse4.2
-  build-depends:
-      base                  >= 4.7      && < 5
-    , bits-extra            >= 0.0.1.2  && < 0.1
-    , bytestring            >= 0.10     && < 0.11
-    , deepseq               >= 1.4      && < 1.5
-    , ghc-prim
-    , hw-bits               >= 0.7.0.2  && < 0.8
-    , hw-rankselect         >= 0.12.0.2 && < 0.13
-    , hw-rankselect-base    >= 0.3.2.0  && < 0.4
-    , hw-prim               >= 0.6.2.14 && < 0.7
-    , hw-simd               >= 0.1.1.3  && < 0.2
-    , vector                >= 0.12.0.1 && < 0.13
-    , cassava               >= 0.5.1.0    && < 0.6
-    , criterion             >= 1.4.1.0    && < 1.6
-    , directory             >= 1.3.1.5    && < 1.4
-    , hw-dsv
-    , mmap                  >= 0.5.9      && < 0.6
-  if flag(sse42)
-    ghc-options: -msse4.2
-    cc-options: -msse4.2
-  if flag(bmi2)
-    cc-options: -mbmi2 -DBMI2_ENABLED
-  if (flag(bmi2)) && (impl(ghc >=8.4.1))
-    ghc-options: -mbmi2 -msse4.2
-  if flag(avx2)
-    cc-options: -mavx2 -DAVX2_ENABLED
-  if (flag(avx2)) && (impl(ghc >=8.4.1))
-    ghc-options: -mbmi2 -msse4.2
-    cpp-options: -DBMI2_ENABLED -DAVX2_ENABLED
-  if (impl(ghc >=8.0.1))
-    ghc-options: -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns -Wredundant-constraints
-  if (!impl(ghc >=8.0.1))
-    build-depends:
-        transformers          >= 0.4        && < 0.6
-      , semigroups            >= 0.8.4      && < 0.19
-  default-language: Haskell2010
+  import:   base, common
+          , bits-extra
+          , bytestring
+          , cassava
+          , criterion
+          , deepseq
+          , directory
+          , ghc-prim
+          , hw-bits
+          , hw-prim
+          , hw-rankselect
+          , hw-rankselect-base
+          , hw-simd
+          , mmap
+          , vector
+  type:             exitcode-stdio-1.0
+  main-is:          Main.hs
+  other-modules:    Paths_hw_dsv
+  autogen-modules:  Paths_hw_dsv
+  hs-source-dirs:   bench
+  ghc-options:      -msse4.2
+  build-depends:    hw-dsv
