diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,16 @@
 # Changelog and Acknowledgments
 
+## 1.0.2
+* Added microlens (or, with `lens` flag, lens) dependency.
+  * `uncurriedN` and `uncurriedN'` `Lens`es (or `Iso`s)
+* Added strict tuple instances to achieve parity with `Pair`.
+  * `Each`
+  * `Field1`, `Field2`, etc., however many are offered by the library providing optics
+  * many more
+* Fixed build issue with older template-haskell versions.
+
+## 1.0.1
+* Fixed build issue with older base versions that don't export `foldl'` in the Prelude.
+
 ## 1.0.0
 * Initial release.
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -8,7 +8,8 @@
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE TypeApplications #-}
 
-import Data.Tuple.Classes (curryN', uncurryN')
+import Data.Tuple.Classes (uncurriedN')
+import Lens.Micro         (over)
 
 -- Function wrapper that expects a unary function
 printArgAndRun :: (Show a) => (a -> IO b) -> a -> IO b
@@ -22,10 +23,9 @@
     putStrLn title
     print $ i + j
 
--- We can adapt them
+-- We can adapt it
 printArgsTitleAndSum :: String -> Int -> Int -> IO ()
-printArgsTitleAndSum =
-    curryN' @3 $ printArgAndRun $ uncurryN' @3 printTitleAndSum
+printArgsTitleAndSum = over (uncurriedN' @3) printArgAndRun printTitleAndSum
 
 main :: IO ()
 main = printArgsTitleAndSum "Important identity" 26885 15184
@@ -46,9 +46,10 @@
 
 consTuple :: (TupleAt 1 a t u) => a -> t -> u
 consTuple = tupleInsert @1
+infixr `consTuple`
 
 main :: IO ()
-main = print $ consTuple 'x' $ consTuple False ("sequitur", "quodlibet")
+main = print $ 'x' `consTuple` False `consTuple` ("sequitur", "quodlibet")
 ```
 
 ```
diff --git a/src/Data/Tuple/Classes.hs b/src/Data/Tuple/Classes.hs
--- a/src/Data/Tuple/Classes.hs
+++ b/src/Data/Tuple/Classes.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE AllowAmbiguousTypes #-}
+{-# LANGUAGE CPP #-}
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE FunctionalDependencies #-}
 {-# LANGUAGE ImplicitParams #-}
@@ -8,14 +9,14 @@
 {-# OPTIONS_GHC -Wno-orphans #-}
 
 module Data.Tuple.Classes (
-    -- | We present utilities here for generalized handling of tuples:
+    -- | Generalized tuple-related utilities:
     --
     -- * inserting and removing fields
     -- * converting /n/-ary functions to unary and vice versa
     -- * re-exports of smaller tuples
     -- * larger strict tuples with relevant instances
     --
-    -- We consider `Identity` the canonical strict tuple insofar as it adds no extra laziness.
+    -- We consider `Identity` the canonical strict 1-tuple.
     --
     -- === __Discussion__
     --
@@ -45,7 +46,7 @@
     --     Arity a        = 0
     --
     -- arity :: (KnownNat (Arity f)) => f -> Natural
-    -- arity _ = natVal $ Proxy @(Arity a)
+    -- arity _ = natVal $ Proxy @(Arity f)
     -- @
     --
     -- but this falls short.
@@ -56,7 +57,7 @@
     -- >>> arity print
     -- 1
     -- >>> arity id
-    --         • No instance for ‘KnownNat (1 + Arity a0)’
+    --         • No instance for ‘KnownNat (1 + Arity f0)’
     --
     -- For more food for thought, see [this sketch](https://bin.mangoiv.com/note?id=d204d07f-6292-4eb7-9aff-4cabc78394b9) for Haskell keyword arguments.
     --
@@ -68,6 +69,8 @@
 
     -- * Generalized uncurrying
     UncurryN(..),
+    uncurriedN,
+    uncurriedN',
 
     -- * Re-exports
     Solo(..),
@@ -85,10 +88,15 @@
     StrictTuple9(..),
 ) where
 
+import Data.Strict.Tuple qualified as Strict
+import Data.Tuple        (Solo(..))
+import GHC.TypeNats      (Nat)
+#if defined(LENS)
+import Control.Lens
+#else
 import Data.Functor.Identity (Identity(..))
-import Data.Strict.Tuple     qualified as Strict
-import Data.Tuple            (Solo(..))
-import GHC.TypeNats          (Nat)
+import Lens.Micro
+#endif
 
 import Data.Tuple.Classes.TH
 
@@ -104,29 +112,54 @@
     -- | A unary function isomorphic to @c@.
     -- The argument is a lazy tuple.
     type UncurriedN n c
+
     -- | Same, but the tuple is strict.
     type UncurriedN' n c
+
     -- | Convert a function taking @n@ arguments to a function taking a single lazy @n@-tuple.
     --
     -- > uncurryN :: (a -> b -> ... -> r) -> (a, b, ...) -> r
     --
-    -- If the function @cur _ _ ... = k@ ignores its arguments, then @uncurryN cur ⊥ = k@.
+    -- Instances defined in this library ensure, given a function @cur _ _ ... = k@ which ignores its arguments, that @uncurryN cur ⊥ = k@.
     uncurryN :: c -> UncurriedN n c
+
     -- | Convert a function taking a single lazy @n@-tuple to a function taking @n@ arguments.
     --
     -- > curryN :: ((a, b, ...) -> r) -> a -> b -> ... -> r
     curryN :: UncurriedN n c -> c
+
     -- | Convert a function taking @n@ arguments to a function taking a single strict @n@-tuple.
     --
     -- > uncurryN' :: (a -> b -> ... -> r) -> StrictTupleN a b ... -> r
     --
-    -- If the function @cur _ _ ... = k@ ignores its arguments, then @uncurryN' cur ⊥ = k@.
+    -- Instances defined in this library ensure, given a function @cur _ _ ... = k@ which ignores its arguments, that @uncurryN' cur ⊥ = k@.
     uncurryN' :: c -> UncurriedN' n c
+
     -- | Convert a function taking a single strict @n@-tuple to a function taking @n@ arguments.
     --
     -- > curryN' :: (StrictTupleN a b ... -> r) -> a -> b -> ... -> r
     curryN' :: UncurriedN' n c -> c
 
+-- | Operate on an @n@-ary function as a unary function taking a lazy tuple argument.
+uncurriedN :: forall n c d. (UncurryN n c, UncurryN n d) =>
+#if defined(LENS)
+    Iso c d (UncurriedN n c) (UncurriedN n d)
+uncurriedN = iso (uncurryN @n) (curryN @n)
+#else
+    Lens c d (UncurriedN n c) (UncurriedN n d)
+uncurriedN f = fmap (curryN @n) . f . (uncurryN @n)
+#endif
+
+-- | Same, but the tuple is strict.
+uncurriedN' :: forall n c d. (UncurryN n c, UncurryN n d) =>
+#if defined(LENS)
+    Iso c d (UncurriedN' n c) (UncurriedN' n d)
+uncurriedN' = iso (uncurryN' @n) (curryN' @n)
+#else
+    Lens c d (UncurriedN' n c) (UncurriedN' n d)
+uncurriedN' f = fmap (curryN' @n) . f . (uncurryN' @n)
+#endif
+
 -- | Inserting and removing tuple fields.
 -- Specify @i@ via @TypeApplications@/@DataKinds@.
 --
@@ -139,6 +172,8 @@
 -- > uncurry (tupleInsert @i) . tupleRemove @i = id
 --
 -- The valid range of @i@ is \[1, /n/], where /n/ is the width of @u@.
+--
+-- We do not provide an instance for inserting into the zero-tuple @()@, because the strictness is ambiguous.
 class TupleAt (i :: Nat) a t u | i a t -> u, i u -> a t where
     -- | Insert an @a@ into @t@ at @i@ and return the widened tuple @u@.
     -- Upon insertion, any fields at indices >=@i@ will slide to the right to make room.
@@ -148,10 +183,16 @@
     -- >>> tupleInsert @2 'x' (Identity 67)
     -- 67 :!: 'x'
     --
-    -- For lazy tuples, @`ith` \@i \(tupleInsert \@i x ⊥) = x@.
+    -- Instances defined in this library behave differently based on strictness.
+    -- Given a function @cur _ _ ... = k@ which ignores its arguments, we ensure:
     --
-    -- For strict tuples, @ith \@i \(tupleInsert \@i x ⊥) = &#x22A5;@.
+    -- +--------------+----------------------------------------+
+    -- | Lazy tuple   | @`ith` \@i \(tupleInsert \@i x ⊥) = x@ |
+    -- +--------------+----------------------------------------+
+    -- | Strict tuple | @ith \@i \(tupleInsert \@i x ⊥) = ⊥@   |
+    -- +--------------+----------------------------------------+
     tupleInsert :: a -> t -> u
+
     -- | Remove an @a@ from @u@ at @i@ and return both it and the remaining tuple @t@.
     -- Upon removal, any fields at indices >@i@ will slide to the left to close the gap.
     --
@@ -166,8 +207,7 @@
 -- Foo
 --
 -- This is a convenience function defined as @fst . tupleRemove \@i@.
--- For typeclasses purpose-built for accessing tuple fields, consider [lens\'s @Field1@ and the like](https://hackage-content.haskell.org/package/lens-5.3.6/docs/Control-Lens-Tuple.html).
--- We do not wish to duplicate that here; it is already duplicated by (the excellent) [microlens](https://hackage-content.haskell.org/package/microlens-0.5.0.0/docs/Lens-Micro-FieldN.html).
+-- For purpose-built tuple field accessors, consider `_1` and the like.
 ith :: forall i a t u. (TupleAt i a t u) => u -> a
 ith = fst . tupleRemove @i
 
diff --git a/src/Data/Tuple/Classes/TH.hs b/src/Data/Tuple/Classes/TH.hs
--- a/src/Data/Tuple/Classes/TH.hs
+++ b/src/Data/Tuple/Classes/TH.hs
@@ -1,5 +1,6 @@
+{-# LANGUAGE CPP #-}
 {-# LANGUAGE ImplicitParams #-}
-{-# LANGUAGE TemplateHaskellQuotes #-}
+{-# LANGUAGE TemplateHaskell #-}
 
 module Data.Tuple.Classes.TH (
     makeStrictTupleAndInsts,
@@ -7,23 +8,43 @@
     makeTupleAtInsts,
 ) where
 
-import Control.DeepSeq       (NFData(..), NFData1, NFData2(..))
-import Control.Monad         (forM)
-import Data.Bifoldable       (Bifoldable(..))
+import Control.DeepSeq            (NFData(..), NFData1, NFData2(..))
+import Control.Monad              (forM)
+import Data.Bifoldable            (Bifoldable(..))
+import Data.Bifunctor.Swap        (Swap(..))
+import Data.Binary                (Binary(..))
+import Data.Bitraversable         (Bitraversable(..))
+import Data.Data                  (Data)
+import Data.Functor.Classes
+import Data.Hashable              (Hashable(..))
+import Data.Hashable.Lifted       (Hashable1, Hashable2(..))
+import Data.Ix                    (Ix)
+import Data.Strict.Classes        (Strict(..))
+import Data.Strict.Tuple          (Pair(..))
+import GHC.Generics               (Generic, Generic1)
+import GHC.Read                   qualified as Read
+import Language.Haskell.TH        hiding (Strict)
+import Language.Haskell.TH.Syntax hiding (Strict)
+import Text.Read                  (Read(..))
+import Text.Read                  qualified as Read
+#if !MIN_VERSION_base(4,20,0)
+import Data.Foldable (foldl')
+#endif
+#if defined(LENS)
+import Control.Lens hiding (op)
+#else
 import Data.Bifunctor        (Bifunctor(..))
-import Data.Bifunctor.Swap   (Swap(..))
-import Data.Binary           (Binary(..))
-import Data.Bitraversable    (Bitraversable(..))
-import Data.Foldable         (foldl')
-import Data.Functor.Classes  (Eq1(..), Eq2(..))
 import Data.Functor.Identity (Identity(..))
-import Data.Hashable         (Hashable)
-import Data.Hashable.Lifted  (Hashable1)
-import Data.Strict.Classes   (Strict(..))
-import Data.Strict.Tuple     (Pair(..))
-import GHC.Generics          (Generic, Generic1)
-import Language.Haskell.TH   hiding (Strict, strictType)
+import Lens.Micro
+import Lens.Micro.Internal   qualified as Lens.Micro
+#if !MIN_VERSION_microlens(0,5,0)
+import Lens.Micro.Internal hiding (Strict)
+#endif
+#endif
 
+dropLast :: Int -> [a] -> [a]
+dropLast n = reverse . drop n . reverse
+
 strictTName :: Int -> Name
 strictTName 0 = ''()
 strictTName 1 = ''Identity
@@ -36,31 +57,33 @@
 strictDName 2 = '(:!:)
 strictDName n = strictTName n
 
-dropLast :: (?n :: Int) => Int -> [a] -> [a]
-dropLast u = take $ ?n - u
+-- "StrictTuple3"
+strictTupleSE :: (?n :: Int) => ExpQ
+strictTupleSE = stringE $ nameBase $ strictDName ?n
 
--- StrictTuple3
-mkNames :: (?n :: Int) => (Enum a) => a -> (a -> String) -> [Name]
+mkNames :: (?n :: Int, Enum a) => a -> (a -> String) -> [Name]
 mkNames x toName = map (mkName . toName) $ take ?n $ enumFrom x
 
 abc, xs, ys :: (?n :: Int) => [Name]
 -- a, b, c
 abc = mkNames 'a' (: [])
--- x0, x1, x2
-xs = mkNames (0 :: Int) $ ('x' :) . show
--- y0, y1, y2
-ys = mkNames (0 :: Int) $ ('y' :) . show
+-- x1, x2, x3
+xs = mkNames (1 :: Int) $ ('x' :) . show
+-- y1, y2, y3
+ys = mkNames (1 :: Int) $ ('y' :) . show
 
-fName, gName, cur, unc :: Name
-fName = mkName "f"
-gName = mkName "g"
-cur = mkName "cur"
-unc = mkName "unc"
+cur, unc :: Name
+(cur, unc) = ("cur", "unc") & both %~ mkName
 
 -- !a
 banged :: Name -> BangTypeQ
 banged a = bangType (bang noSourceUnpackedness sourceStrict) (varT a)
 
+-- contextT 1 ''Eq = (Eq a, Eq b)
+contextT :: (?n :: Int) => Int -> Name -> TypeQ
+contextT u ctor = foldl' appT (tupleT $ ?n - u) $
+    map (\a -> conT ctor `appT` varT a) $ dropLast u abc
+
 -- StrictTuple3 a
 strictTupleUnsatT :: (?n :: Int) => Int -> TypeQ
 strictTupleUnsatT u =
@@ -74,27 +97,83 @@
 lazyTupleSatT :: (?n :: Int) => TypeQ
 lazyTupleSatT = foldl' appT (tupleT ?n) $ map varT abc
 
--- StrictTuple3 x0 x1 x2
+-- a
+aT, bT :: TypeQ
+(aT, bT) = ("a", "b") & both %~ varT . mkName
+
+-- StrictTuple3 x1 x2 x3
 strictTupleP :: (?n :: Int) => [Name] -> PatQ
 strictTupleP = conP (strictDName ?n) . map varP
 
--- StrictTuple3 x0
+-- StrictTuple3 x1
 strictTupleUnsatE :: (?n :: Int) => Int -> ExpQ
 strictTupleUnsatE u =
     foldl' appE (conE $ strictDName ?n) $ dropLast u $ map varE xs
 
--- StrictTuple3 x0 x1 x2
+-- StrictTuple3 x1 x2 x3
 strictTupleSatE :: (?n :: Int) => ExpQ
 strictTupleSatE = strictTupleUnsatE 0
 
+-- StrictTuple3 a a a
+strictTupleAllSameT :: (?n :: Int) => TypeQ -> TypeQ
+strictTupleAllSameT a = foldl' appT (conT $ strictTName ?n) (replicate ?n a)
+
+-- {-# INLINE method #-}
+inlineD :: Name -> DecQ
+inlineD name = pragInlD name Inline FunLike AllPhases
+
+-- StrictTuple3 <$> exp1 <*> exp2 <*> ...
+applicativeE :: (?n :: Int) => [ExpQ] -> ExpQ
+applicativeE expQs = foldl' accOpE (conE $ strictDName ?n) $ zip ops expQs where
+    accOpE acc (op, expQ) = uInfixE acc op expQ
+    ops = [| (<$>) |] : repeat [| (<*>) |]
+
+-- x <> y
+sappendE :: Name -> Name -> ExpQ
+sappendE x y = [| $(varE x) <> $(varE y) |]
+
+-- (1, ''Field1, '_1), (2, ''Field2, '_2), ...
+fieldNNames :: [(Int, Name, Name)]
+fieldNNames = $(
+    let go i = do
+            fieldI <- lookupTypeName $ "Field" ++ show i
+            _i <- lookupValueName $ "_" ++ show i
+            case liftA2 (i, , ) fieldI _i of
+                Nothing        -> return []
+                Just iFieldI_i -> (iFieldI_i :) <$> go (i + 1)
+
+        liftFieldNNames :: [(Int, Name, Name)] -> ExpQ
+#if MIN_VERSION_template_haskell(2,22,1)
+        liftFieldNNames = lift
+#else
+        liftFieldNNames = listE . map liftTriple
+
+        liftTriple (i, fieldI, _i) = tupE [lift i, liftName fieldI, liftName _i]
+
+        liftName (Name (OccName on) (NameG ns (PkgName pn) (ModName mn))) = [|
+            Name
+                (OccName $(stringE on))
+                (NameG
+                    $(liftNameSpace ns)
+                    (PkgName $(stringE pn))
+                    (ModName $(stringE mn)))
+            |]
+        liftName _ = fail "not implemented"
+
+        liftNameSpace VarName   = [| VarName |]
+        liftNameSpace TcClsName = [| TcClsName |]
+        liftNameSpace _         = fail "not implemented"
+#endif
+    in go (1 :: Int) >>= liftFieldNNames)
+
 {-
 instance UncurryN 3 (a -> b -> c -> d) where
     type UncurriedN 3 (a -> b -> c -> d) = (a, b, c) -> d
     type UncurriedN' 3 (a -> b -> c -> d) = StrictTuple3 a b c -> d
-    uncurryN cur ~(x0, x1, x2) = cur x0 x1 x2
-    curryN unc x0 x1 x2 = unc (x0, x1, x2)
-    uncurryN' cur ~(StrictTuple3 x0 x1 x2) = cur x0 x1 x2
-    curryN' unc x0 x1 x2 = unc (StrictTuple3 x0 x1 x2)
+    uncurryN cur ~(x1, x2, x3) = cur x1 x2 x3
+    curryN unc x1 x2 x3 = unc (x1, x2, x3)
+    uncurryN' cur ~(StrictTuple3 x1 x2 x3) = cur x1 x2 x3
+    curryN' unc x1 x2 x3 = unc (StrictTuple3 x1 x2 x3)
 -}
 makeUncurryNInst :: (?n :: Int) => DecQ
 makeUncurryNInst =
@@ -109,22 +188,19 @@
             (map varP $ unc : xs)
             (normalB $ varE unc `appE` foldl' appE (conE ctor) (map varE xs))
             []
-    in instanceD
-        (pure [])
-        [t| $(conT $ mkName "UncurryN") $nT $curT |]
-        [
-            tySynInstD $ tySynEqn
-                Nothing
-                [t| $(conT $ mkName "UncurriedN") $nT $curT |]
-                [t| $lazyTupleSatT -> $(varT d) |],
-            tySynInstD $ tySynEqn
-                Nothing
-                [t| $(conT $ mkName "UncurriedN'") $nT $curT |]
-                [t| $strictTupleSatT -> $(varT d) |],
-            funD (mkName "uncurryN") [uncurryClause $ tupleDataName ?n],
-            funD (mkName "curryN") [curryClause $ tupleDataName ?n],
-            funD (mkName "uncurryN'") [uncurryClause $ strictDName ?n],
-            funD (mkName "curryN'") [curryClause $ strictDName ?n]]
+    in instanceD (pure []) [t| $(conT $ mkName "UncurryN") $nT $curT |] [
+        tySynInstD $ tySynEqn
+            Nothing
+            [t| $(conT $ mkName "UncurriedN") $nT $curT |]
+            [t| $lazyTupleSatT -> $(varT d) |],
+        tySynInstD $ tySynEqn
+            Nothing
+            [t| $(conT $ mkName "UncurriedN'") $nT $curT |]
+            [t| $strictTupleSatT -> $(varT d) |],
+        funD (mkName "uncurryN") [uncurryClause $ tupleDataName ?n],
+        funD (mkName "curryN") [curryClause $ tupleDataName ?n],
+        funD (mkName "uncurryN'") [uncurryClause $ strictDName ?n],
+        funD (mkName "curryN'") [curryClause $ strictDName ?n]]
 
 makeTupleAtInsts :: (?n :: Int) => DecsQ
 makeTupleAtInsts = fmap concat $ forM [1 .. ?n] $ \i -> do
@@ -136,11 +212,11 @@
     let tupleAtInst :: Name -> Name -> Name -> Name -> DecQ
         tupleAtInst removeType removeData insertType insertData = instanceD
             (pure [])
-            (foldl' appT (conT $ mkName "TupleAt") [
-                litT $ numTyLit $ fromIntegral i,
-                varT a,
-                foldl' appT (conT removeType) $ map varT abc',
-                foldl' appT (conT insertType) $ map varT abc])
+            (conT (mkName "TupleAt")
+                `appT` litT (numTyLit $ fromIntegral i)
+                `appT` varT a
+                `appT` foldl' appT (conT removeType) (map varT abc')
+                `appT` foldl' appT (conT insertType) (map varT abc))
             [
                 funD (mkName "tupleInsert") [clause
                     [varP x, tildeP $ conP removeData $ map varP xs']
@@ -155,9 +231,9 @@
 
     sequence [
         {-
-        instance TupleAt 1 b (a, c) (a, b, c) where
-            tupleInsert x1 ~(x0, x2) = (x0, x1, x2)
-            tupleRemove (x0, x1, x2) = (x1, (x0, x2))
+        instance TupleAt 2 b (a, c) (a, b, c) where
+            tupleInsert x2 ~(x1, x3) = (x1, x2, x3)
+            tupleRemove (x1, x2, x3) = (x2, (x1, x3))
         -}
         tupleAtInst
             (tupleTypeName $ ?n - 1)
@@ -168,9 +244,9 @@
         -- TODO This tilde makes implementation simpler and probably has no
         -- impact, but let's confirm that with benchmarks.
         {-
-        instance TupleAt 1 b (Pair a c) (StrictTuple3 a b c) where
-            tupleInsert x1 ~(x0 :!: x2) = StrictTuple3 x0 x1 x2
-            tupleRemove (StrictTuple3 x0 x1 x2) = (x1, x0 :!: x2)
+        instance TupleAt 2 b (Pair a c) (StrictTuple3 a b c) where
+            tupleInsert x2 ~(x1 :!: x3) = StrictTuple3 x1 x2 x3
+            tupleRemove (StrictTuple3 x1 x2 x3) = (x2, x1 :!: x3)
         -}
         tupleAtInst
             (strictTName $ ?n - 1)
@@ -180,12 +256,12 @@
 
 makeStrictTupleAndInsts :: (?n :: Int) => DecsQ
 makeStrictTupleAndInsts = do
-    (x1, x2) <- case drop (?n - 2) xs of
-        [x1, x2] -> return (x1, x2)
-        _        -> fail "makeStrictTupleAndInsts: ?n < 2"
+    ((x1, y1), (x2, y2)) <- case drop (?n - 2) $ zip xs ys of
+        [xy1, xy2] -> return (xy1, xy2)
+        _          -> fail "makeStrictTupleAndInsts: ?n < 2"
 
     {-
-    data StrictTuple4 a b c = StrictTuple4 a b c
+    data StrictTuple3 a b c = StrictTuple3 a b c
         deriving (...)
     -}
     strictTupleDef <- dataD
@@ -199,196 +275,298 @@
             ''Ord,
             ''Read,
             ''Show,
+            ''Bounded,
+            ''Ix,
             ''Foldable,
             ''Functor,
             ''Traversable,
             ''Generic,
-            ''Generic1]]
+            ''Generic1,
+            ''Data]]
 
     {-
-    instance (Hashable a, Hashable b, Hashable c) => Hashable (StrictTuple3 a b c)
+    instance Field2 (StrictTuple3 a b c) (StrictTuple3 a b' c) b b' where
+        _2 f ~(StrictTuple3 x1 x2 x3) = f x2 <&> \x2' -> StrictTuple3 x1 x2' x3
+        {-# INLINE _2 #-}
     -}
-    hashableInst <- instanceD
-        (cxt [[t| Hashable $(varT a) |] | a <- abc])
-        [t| Hashable $strictTupleSatT |]
-        []
+    fieldNInsts <- forM (take ?n fieldNNames) $ \(i, fieldI, _i) -> do
+        ((a, x), (a', x'), (a'bc, xs')) <- case splitAt (i - 1) $ zip abc xs of
+            (_,  [])      -> fail "fieldNInsts: i > ?n"
+            (ls, ax : rs) -> return (ax, a'x', unzip $ ls ++ a'x' : rs) where
+                a'x' = ax & both %~ \name -> mkName $ nameBase name ++ "'"
+        f <- newName "f"
+        instanceD
+            (pure [])
+            (conT fieldI
+                `appT` strictTupleSatT
+                `appT` foldl' appT (conT $ strictTName ?n) (map varT a'bc)
+                `appT` varT a
+                `appT` varT a')
+            [
+                funD _i [clause
+                    [varP f, tildeP $ strictTupleP xs]
+                    (normalB $ [| fmap |]
+                        `appE` lam1E
+                            (varP x')
+                            (foldl' appE (conE $ strictDName ?n) $ map varE xs')
+                        `appE` (varE f `appE` varE x))
+                    []],
+                inlineD _i]
 
-    {-
-    instance (Hashable a, Hashable b) => Hashable1 (StrictTuple3 a b)
-    -}
-    hashable1Inst <- instanceD
-        (cxt [[t| Hashable $(varT a) |] | a <- init abc])
-        [t| Hashable1 $(strictTupleUnsatT 1) |]
-        []
+    otherInsts <- [d|
+        {-
+        instance (Eq a, Eq b) => Eq1 (StrictTuple3 a b) where
+        -}
+        instance $(contextT 1 ''Eq) => Eq1 $(strictTupleUnsatT 1)
 
-    {-
-    instance (Eq a, Eq b) => Eq1 (StrictTuple3 a b) where
-        liftEq = liftEq2 (==)
-    -}
-    eq1Inst <- instanceD
-        (cxt [[t| Eq $(varT a) |] | a <- init abc])
-        [t| Eq1 $(strictTupleUnsatT 1) |]
-        [funD 'liftEq [clause [] (normalB [| liftEq2 (==) |]) []]]
+        {-
+        instance (Eq a) => Eq2 (StrictTuple3 a) where
+            liftEq2 f g (StrictTuple3 x1 x2 x3) (StrictTuple3 y1 y2 y3) =
+                x1 == y1 && f x2 y2 && g x3 y3
+        -}
+        instance $(contextT 2 ''Eq) => Eq2 $(strictTupleUnsatT 2) where
+            liftEq2 f g $(strictTupleP xs) $(strictTupleP ys) = $(foldr
+                (\expQ r -> [| $expQ && $r |])
+                [| f $(varE x1) $(varE y1) && g $(varE x2) $(varE y2) |]
+                (dropLast 2 $
+                    let eqE x y = [| $(varE x) == $(varE y) |]
+                    in zipWith eqE xs ys))
 
-    {-
-    instance (Eq a) => Eq2 (StrictTuple3 a) where
-        liftEq2 f g (StrictTuple3 x0 x1 x2) (StrictTuple3 y0 y1 y2) =
-            x0 == y0 && f x1 y1 && g x2 y2
-    -}
-    eq2Inst <- instanceD
-        (cxt [[t| Eq $(varT a) |] | a <- dropLast 2 abc])
-        [t| Eq2 $(strictTupleUnsatT 2) |]
-        [funD 'liftEq2 [clause
-            [varP fName, varP gName, strictTupleP xs, strictTupleP ys]
-            (normalB $ foldr
-                (\boolE r -> [| $boolE && $r |])
-                [|
-                    $(varE fName) $(varE x1) $(varE $ ys !! (?n - 2)) &&
-                    $(varE gName) $(varE x2) $(varE $ ys !! (?n - 1)) |]
+        {-
+        instance (Ord a, Ord b) => Ord1 (StrictTuple3 a b) where
+        -}
+        instance $(contextT 1 ''Ord) => Ord1 $(strictTupleUnsatT 1)
+
+        {-
+        instance (Ord a) => Ord2 (StrictTuple3 a) where
+            liftCompare2 f g (StrictTuple3 x1 x2 x3) (StrictTuple3 y1 y2 y3) =
+                x1 `compare` y1 <> f x2 y2 <> g x3 y3
+        -}
+        instance $(contextT 2 ''Ord) => Ord2 $(strictTupleUnsatT 2) where
+            liftCompare2 f g $(strictTupleP xs) $(strictTupleP ys) = $(foldr
+                (\expQ r -> [| $expQ <> $r |])
+                [| f $(varE x1) $(varE y1) <> g $(varE x2) $(varE y2) |]
                 (dropLast 2 $
-                    zipWith (\x y -> [| $(varE x) == $(varE y) |]) xs ys))
-            []]]
+                    let compareE x y = [| $(varE x) `compare` $(varE y) |]
+                    in zipWith compareE xs ys))
 
-    {-
-    instance (NFData a, NFData b, NFData c) => NFData (StrictTuple3 a b c)
-    -}
-    nfDataInst <- instanceD
-        (cxt [[t| NFData $(varT a) |] | a <- abc])
-        [t| NFData $strictTupleSatT |]
-        []
+        {-
+        instance (Show a, Show b) => Show1 (StrictTuple3 a b)
+        -}
+        instance $(contextT 1 ''Show) => Show1 $(strictTupleUnsatT 1)
 
-    {-
-    instance (NFData a, NFData b) => NFData1 (StrictTuple3 a b)
-    -}
-    nfData1Inst <- instanceD
-        (cxt [[t| NFData $(varT a) |] | a <- init abc])
-        [t| NFData1 $(strictTupleUnsatT 1) |]
-        []
+        {-
+        instance (Show a) => Show2 (StrictTuple3 a) where
+            liftShowsPrec2 f _ g _ prec (StrictTuple3 x1 x2 x3) =
+                showParen (prec > 10) $ showString "StrictTuple3"
+                    . showChar ' ' . showsPrec 11 x1
+                    . showChar ' ' . f 11 x2
+                    . showChar ' ' . g 11 x3
+        -}
+        instance $(contextT 2 ''Show) => Show2 $(strictTupleUnsatT 2) where
+            liftShowsPrec2 f _ g _ prec $(strictTupleP xs) =
+                showParen (prec > 10) $(
+                    let unwordsE fE r = [| $fE . showChar ' ' . $r |]
+                        showsCtorE = [| showString $strictTupleSE |]
+                        showsPrecE x = [| showsPrec 11 $(varE x) |]
+                        mostFsE = map showsPrecE $ dropLast 2 xs
+                    in foldr1 unwordsE $ showsCtorE : mostFsE ++ [
+                        [| f 11 $(varE x1) |],
+                        [| g 11 $(varE x2) |]])
 
-    {-
-    instance (NFData a) => NFData2 (StrictTuple3 a) where
-        liftRnf2 r s (StrictTuple3 x0 x1 x2) = x0 `seq` r x1 `seq` s x2
-    -}
-    nfData2Inst <- instanceD
-        (cxt [[t| NFData $(varT a) |] | a <- dropLast 2 abc])
-        [t| NFData2 $(strictTupleUnsatT 2) |]
-        [funD 'liftRnf2 [clause
-            [varP fName, varP gName, strictTupleP xs]
-            (normalB $ foldr
+        {-
+        instance (Read a, Read b) => Read1 (StrictTuple3 a b) where
+            liftReadPrec = liftReadPrec2 readPrec readListPrec
+            liftReadListPrec = liftReadListPrecDefault
+            liftReadList = liftReadListDefault
+        -}
+        instance $(contextT 1 ''Read) => Read1 $(strictTupleUnsatT 1) where
+            liftReadPrec = liftReadPrec2 readPrec readListPrec
+            liftReadListPrec = liftReadListPrecDefault
+            liftReadList = liftReadListDefault
+
+        {-
+        instance (Read a) => Read2 (StrictTuple3 a) where
+            liftReadPrec2 rp1 _ rp2 _ = readData $ do
+                Read.expectP $ Read.Ident "StrictTuple3"
+                Read.step $ StrictTuple3 <$> readPrec <*> rp1 <*> rp2
+        -}
+        instance $(contextT 2 ''Read) => Read2 $(strictTupleUnsatT 2) where
+            liftReadPrec2 rp1 _ rp2 _ = readData $ do
+                Read.expectP $ Read.Ident $(stringE $ nameBase $ strictDName ?n)
+                Read.step $ $(applicativeE $ replicate (?n - 2) [| readPrec |])
+                    <*> rp1
+                    <*> rp2
+
+        {-
+        instance (NFData a, NFData b, NFData c) =>
+            NFData (StrictTuple3 a b c)
+        -}
+        instance $(contextT 0 ''NFData) => NFData $strictTupleSatT
+
+        {-
+        instance (NFData a) => NFData2 (StrictTuple3 a) where
+            liftRnf2 f g (StrictTuple3 x1 x2 x3) = x1 `seq` f x2 `seq` g x3
+        -}
+        instance $(contextT 2 ''NFData) => NFData2 $(strictTupleUnsatT 2) where
+            liftRnf2 f g $(strictTupleP xs) = $(foldr
                 (\x r -> [| rnf $(varE x) `seq` $r |])
-                [| $(varE fName) $(varE x1) `seq` $(varE gName) $(varE x2) |]
+                [| f $(varE x1) `seq` g $(varE x2) |]
                 (dropLast 2 xs))
-            []]]
 
-    {-
-    instance (Binary a, Binary b, Binary c) => Binary (StrictTuple3 a b c) where
-        put = put . toLazy
-        get = toStrict <$> get
-    -}
-    binaryInst <- instanceD
-        (cxt [[t| Binary $(varT a) |] | a <- abc])
-        [t| Binary $strictTupleSatT |]
-        [
-            funD 'put [clause [] (normalB [| put . toLazy |]) []],
-            funD 'get [clause [] (normalB [| toStrict <$> get |]) []]]
+        {-
+        instance (NFData a, NFData b) => NFData1 (StrictTuple3 a b)
+        -}
+        instance $(contextT 1 ''NFData) => NFData1 $(strictTupleUnsatT 1)
 
-    {-
-    instance (Monoid a, Monoid b) => Applicative (StrictTuple3 a b) where
-        pure = StrictTuple3 mempty mempty
-        StrictTuple3 x0 x1 x2 <*> StrictTuple3 y0 y1 y2 =
-            StrictTuple (x0 <> y0) (x1 <> y1) (x2 y2)
-    -}
-    applicativeInst <- instanceD
-        (cxt [[t| Monoid $(varT a) |] | a <- init abc])
-        [t| Applicative $(strictTupleUnsatT 1) |]
-        [
-            funD 'pure [clause
-                [varP $ last xs]
-                (normalB $ foldl' appE (conE $ strictDName ?n) $
-                    replicate (?n - 1) [| mempty |] ++ [varE $ last xs])
-                []],
-            funD '(<*>) [clause
-                [strictTupleP xs, strictTupleP ys]
-                (normalB $ foldl' appE (conE $ strictDName ?n) $
-                    let mappendE x y = [| $(varE x) <> $(varE y) |]
-                        mappendE's = init $ zipWith mappendE xs ys
-                    in mappendE's ++ [varE (last xs) `appE` varE (last ys)])
-                []]]
+        {-
+        instance (Semigroup a, Semigroup b, Semigroup c) =>
+            Semigroup (StrictTuple3 a b c) where
+            StrictTuple3 x1 x2 x3 <> StrictTuple3 y1 y2 y3 =
+                StrictTuple3 (x1 <> y1) (x2 <> y2) (x3 <> y3)
+        -}
+        instance $(contextT 0 ''Semigroup) => Semigroup $strictTupleSatT where
+            $(strictTupleP xs) <> $(strictTupleP ys) =
+                $(foldl' appE (conE $ strictDName ?n) $ zipWith sappendE xs ys)
 
-    {-
-    instance (Monoid a, Monoid b) => Monad (StrictTuple3 a b) where
-        StrictTuple3 x0 x1 x2 >>= f = StrictTuple3 (x0 <> y0) (x1 <> y1) y2 where
-            StrictTuple3 y0 y1 y2 = f x2
-    -}
-    monadInst <- instanceD
-        (cxt [[t| Monoid $(varT a) |] | a <- init abc])
-        [t| Monad $(strictTupleUnsatT 1) |]
-        [funD '(>>=) [clause
-            [strictTupleP xs, varP fName]
-            (normalB $ foldl' appE (conE $ strictDName ?n) $
-                let mappendE x y = [| $(varE x) <> $(varE y) |]
-                in init (zipWith mappendE xs ys) ++ [varE $ last ys])
-            [valD
-                (strictTupleP ys)
-                (normalB $ varE fName `appE` varE (last xs))
-                []]]]
+        {-
+        instance (Monoid a, Monoid b, Monoid c) => Monoid (StrictTuple3 a b c) where
+            mempty = StrictTuple3 mempty mempty mempty
+        -}
+        instance $(contextT 0 ''Monoid) => Monoid $strictTupleSatT where
+            mempty = $(foldl' appE (conE $ strictDName ?n) $
+                replicate ?n [| mempty |])
 
-    otherInsts <-
-        let bifoldMapP = tildeP $ conP (strictDName ?n) $
-                replicate (?n - 2) wildP ++ [varP x1, varP x2]
-        in [d|
-            {-
-            instance Strict (a, b, c) (StrictTuple3 a b c) where
-                toStrict (x0, x1, x2) = StrictTuple3 x0 x1 x2
-                toLazy (StrictTuple3 x0 x1 x2) = (x0, x1, x2)
-            -}
-            instance Strict $lazyTupleSatT $strictTupleSatT where
-                toStrict $(tupP $ map varP xs) = $strictTupleSatE
-                toLazy $(strictTupleP xs) = $(tupE $ map varE xs)
+        {-
+        instance (Monoid a, Monoid b) => Applicative (StrictTuple3 a b) where
+            pure = StrictTuple3 mempty mempty
+            StrictTuple3 x1 x2 x3 <*> StrictTuple3 y1 y2 y3 =
+                StrictTuple (x1 <> y1) (x2 <> y2) (x3 y3)
+        -}
+        instance $(contextT 1 ''Monoid) => Applicative $(strictTupleUnsatT 1)
+            where
+            pure = $(foldl' appE (conE $ strictDName ?n) $
+                replicate (?n - 1) [| mempty |])
+            $(strictTupleP xs) <*> $(strictTupleP ys) =
+                $(foldl' appE (conE $ strictDName ?n) $
+                    let sappendE's = init $ zipWith sappendE xs ys
+                    in sappendE's ++ [varE (last xs) `appE` varE (last ys)])
 
-            {-
-            instance Swap (StrictTuple3 a) where
-                swap (StrictTuple3 x0 x1 x2) = StrictTuple3 x0 x2 x1
-            -}
-            instance Swap $(strictTupleUnsatT 2) where
-                swap $(strictTupleP xs) =
-                    $(strictTupleUnsatE 2) $(varE x2) $(varE x1)
+        {-
+        instance (Monoid a, Monoid b) => Monad (StrictTuple3 a b) where
+            StrictTuple3 x1 x2 x3 >>= f = StrictTuple3 (x1 <> y1) (x2 <> y2) y3
+                where
+                StrictTuple3 y1 y2 y3 = f x3
+        -}
+        instance $(contextT 1 ''Monoid) => Monad $(strictTupleUnsatT 1) where
+            $(strictTupleP xs) >>= f = $(foldl' appE (conE $ strictDName ?n) $
+                init (zipWith sappendE xs ys) ++ [varE $ last ys])
+                where
+                $(strictTupleP ys) = f $(varE $ last xs)
 
-            {-
-            instance Bifoldable (StrictTuple3 a) where
-                bifoldMap f g ~(StrictTuple3 _ x1 x2) = f x1 <> g x2
-            -}
-            instance Bifoldable $(strictTupleUnsatT 2) where
-                bifoldMap f g $bifoldMapP = f $(varE x1) <> g $(varE x2)
+        {-
+        instance (Binary a, Binary b, Binary c) => Binary (StrictTuple3 a b c) where
+            put = put . toLazy
+            get = toStrict <$> get
+        -}
+        instance $(contextT 0 ''Binary) => Binary $strictTupleSatT where
+            put = put . toLazy
+            get = toStrict <$> get
 
-            {-
-            instance Bifunctor (StrictTuple3 a) where
-                bimap f g ~(StrictTuple3 x0 x1 x2) =
-                    StrictTuple3 x0 (f x1) (g x2)
-            -}
-            instance Bifunctor $(strictTupleUnsatT 2) where
-                bimap f g $(tildeP $ strictTupleP xs) =
-                    $(strictTupleUnsatE 2) (f $(varE x1)) (g $(varE x2))
+        {-
+        instance (Hashable a, Hashable b, Hashable c) =>
+            Hashable (StrictTuple3 a b c)
+        -}
+        instance $(contextT 0 ''Hashable) => Hashable $strictTupleSatT
 
-            {-
-            instance Bitraversable (StrictTuple3 a) where
-                bitraverse f g ~(StrictTuple3 x0 x1 x2) =
-                    liftA2 (StrictTuple3 x0) (f x1) (g x2)
-            -}
-            instance Bitraversable $(strictTupleUnsatT 2) where
-                bitraverse f g $(tildeP $ strictTupleP xs) =
-                    $(strictTupleUnsatE 2) <$> f $(varE x1) <*> g $(varE x2)
-            |]
+        {-
+        instance (Hashable a, Hashable b) => Hashable1 (StrictTuple3 a b)
+        -}
+        instance $(contextT 1 ''Hashable) => Hashable1 $(strictTupleUnsatT 1)
 
-    return $ strictTupleDef
-        : hashableInst
-        : hashable1Inst
-        : eq1Inst
-        : eq2Inst
-        : nfDataInst
-        : nfData1Inst
-        : nfData2Inst
-        : binaryInst
-        : applicativeInst
-        : monadInst
-        : otherInsts
+        {-
+        instance (Hashable a) => Hashable2 (StrictTuple3 a) where
+            liftHashWithSalt2 f g s (StrictTuple3 x1 x2 x3) =
+                (s `hashWithSalt` x1) `f` x2 `g` x3
+        -}
+        instance $(contextT 2 ''Hashable) =>
+            Hashable2 $(strictTupleUnsatT 2) where
+            liftHashWithSalt2 f g s $(strictTupleP xs) = $(
+                let accHashE acc x = [| $(acc) `hashWithSalt` $(varE x) |]
+                in parensE $ foldl' accHashE [| s |] $ dropLast 2 xs)
+                `f` $(varE x1)
+                `g` $(varE x2)
+
+        {-
+        instance Strict (a, b, c) (StrictTuple3 a b c) where
+            toStrict (x1, x2, x3) = StrictTuple3 x1 x2 x3
+            toLazy (StrictTuple3 x1 x2 x3) = (x1, x2, x3)
+        -}
+        instance Strict $lazyTupleSatT $strictTupleSatT where
+            toStrict $(tupP $ map varP xs) = $strictTupleSatE
+            toLazy $(strictTupleP xs) = $(tupE $ map varE xs)
+
+#if !defined(LENS)
+        {-
+        instance Lens.Micro.Strict (a, b, c) (StrictTuple3 a b c) where
+            strict = lens toStrict (const toLazy)
+            lazy = lens toLazy (const toStrict)
+        -}
+        instance Lens.Micro.Strict $lazyTupleSatT $strictTupleSatT where
+            strict f = fmap toLazy . f . toStrict
+            {-# INLINE strict #-}
+            lazy f = fmap toStrict . f . toLazy
+            {-# INLINE lazy #-}
+#endif
+
+        {-
+        instance Swap (StrictTuple3 a) where
+            swap (StrictTuple3 x1 x2 x3) = StrictTuple3 x1 x3 x2
+        -}
+        instance Swap $(strictTupleUnsatT 2) where
+            swap $(strictTupleP xs) =
+                $(strictTupleUnsatE 2) $(varE x2) $(varE x1)
+
+        {-
+        instance Bifoldable (StrictTuple3 a) where
+            bifoldMap f g ~(StrictTuple3 _ x1 x2) = f x1 <> g x2
+        -}
+        instance Bifoldable $(strictTupleUnsatT 2) where
+            bifoldMap f g $(tildeP $ conP (strictDName ?n) $
+                replicate (?n - 2) wildP ++ [varP x1, varP x2])
+                = f $(varE x1) <> g $(varE x2)
+
+        {-
+        instance Bifunctor (StrictTuple3 a) where
+            bimap f g ~(StrictTuple3 x1 x2 x3) = StrictTuple3 x1 (f x2) (g x3)
+        -}
+        instance Bifunctor $(strictTupleUnsatT 2) where
+            bimap f g $(tildeP $ strictTupleP xs) =
+                $(strictTupleUnsatE 2) (f $(varE x1)) (g $(varE x2))
+
+        {-
+        instance Bitraversable (StrictTuple3 a) where
+            bitraverse f g ~(StrictTuple3 x1 x2 x3) =
+                liftA2 (StrictTuple3 x1) (f x2) (g x3)
+        -}
+        instance Bitraversable $(strictTupleUnsatT 2) where
+            bitraverse f g $(tildeP $ strictTupleP xs) =
+                $(strictTupleUnsatE 2) <$> f $(varE x1) <*> g $(varE x2)
+
+        {-
+        instance Each (StrictTuple3 a a a) (StrictTuple3 b b b) a b where
+            each f ~(StrictTuple3 x1 x2 x3) =
+                StrictTuple3 <$> f x1 <*> f x2 <*> f x3
+        -}
+        instance Each
+            $(strictTupleAllSameT aT)
+            $(strictTupleAllSameT bT)
+            $aT
+            $bT
+            where
+            each f $(tildeP $ strictTupleP xs) =
+                $(applicativeE [[| f $(varE x) |] | x <- xs])
+            {-# INLINE each #-}
+        |]
+
+    return $ strictTupleDef : fieldNInsts ++ otherInsts
diff --git a/test/DocTest01.hs b/test/DocTest01.hs
--- a/test/DocTest01.hs
+++ b/test/DocTest01.hs
@@ -1,9 +1,15 @@
+{-# LANGUAGE CPP #-}
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE TypeApplications #-}
 
 module DocTest01 (main) where
 
-import Data.Tuple.Classes (curryN', uncurryN')
+import Data.Tuple.Classes (uncurriedN')
+#if defined(LENS)
+import Control.Lens (over)
+#else
+import Lens.Micro (over)
+#endif
 
 -- Function wrapper that expects a unary function
 printArgAndRun :: (Show a) => (a -> IO b) -> a -> IO b
@@ -17,10 +23,9 @@
     putStrLn title
     print $ i + j
 
--- We can adapt them
+-- We can adapt it
 printArgsTitleAndSum :: String -> Int -> Int -> IO ()
-printArgsTitleAndSum =
-    curryN' @3 $ printArgAndRun $ uncurryN' @3 printTitleAndSum
+printArgsTitleAndSum = over (uncurriedN' @3) printArgAndRun printTitleAndSum
 
 main :: IO ()
 main = printArgsTitleAndSum "Important identity" 26885 15184
diff --git a/test/DocTest02.hs b/test/DocTest02.hs
--- a/test/DocTest02.hs
+++ b/test/DocTest02.hs
@@ -8,6 +8,7 @@
 
 consTuple :: (TupleAt 1 a t u) => a -> t -> u
 consTuple = tupleInsert @1
+infixr `consTuple`
 
 main :: IO ()
-main = print $ consTuple 'x' $ consTuple False ("sequitur", "quodlibet")
+main = print $ 'x' `consTuple` False `consTuple` ("sequitur", "quodlibet")
diff --git a/test/Spec.hs b/test/Spec.hs
--- a/test/Spec.hs
+++ b/test/Spec.hs
@@ -1,10 +1,18 @@
+{-# LANGUAGE CPP #-}
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE GHC2021 #-}
 
 module Main (main) where
 
-import Control.Exception  (SomeException, catch, evaluate)
+import Control.Exception    (SomeException, catch, evaluate)
+import Data.Functor.Classes
 import Data.Tuple.Classes
+import Text.Read            (readPrec_to_S)
+#if defined(LENS)
+import Control.Lens
+#else
+import Lens.Micro
+#endif
 
 import DocTest01 qualified
 import DocTest02 qualified
@@ -16,6 +24,9 @@
 
     testUncurryNLaziness
     testTupleInsertLaziness
+    testFieldN
+    testShow2
+    testRead2
 
 testUncurryNLaziness :: IO ()
 testUncurryNLaziness = do
@@ -37,3 +48,28 @@
     strictConverged <- test $
         ith @2 $ tupleInsert @2 () (undefined :: Identity Int)
     if strictConverged then error "not strict enough" else putStrLn "OK"
+
+testFieldN :: IO ()
+testFieldN = putStrLn $ StrictTuple5 () () () () "OK" ^. _5
+
+nestedS :: String
+nestedS = "StrictTuple3 (StrictTuple3 1 2 3) 2 3"
+
+nested :: StrictTuple3 (StrictTuple3 Int Int Int) Int Int
+nested = StrictTuple3 (StrictTuple3 1 2 3) 2 3
+
+testShow2 :: IO ()
+testShow2 = do
+    let expected = nestedS
+        got = showsPrec2 0 nested ""
+    if expected == got
+        then putStrLn "OK"
+        else error $ "expected " ++ show expected ++ "; got " ++ show got
+
+testRead2 :: IO ()
+testRead2 = do
+    let expected = [(nested, "")]
+        got = readPrec_to_S readPrec2 0 nestedS
+    if expected == got
+        then putStrLn "OK"
+        else error $ "expected " ++ show expected ++ "; got " ++ show got
diff --git a/tuple-classes.cabal b/tuple-classes.cabal
--- a/tuple-classes.cabal
+++ b/tuple-classes.cabal
@@ -5,7 +5,7 @@
 -- see: https://github.com/sol/hpack
 
 name:           tuple-classes
-version:        1.0.1
+version:        1.0.2
 synopsis:       Working with n-ary tuples and functions; strict tuples
 description:    Please see the README on Codeberg at <https://codeberg.org/sjshuck/tuple-classes#readme>
 category:       data
@@ -25,6 +25,11 @@
   type: git
   location: https://codeberg.org/sjshuck/tuple-classes
 
+flag lens
+  description: Use the full lens library instead of microlens.
+  manual: False
+  default: False
+
 library
   exposed-modules:
       Data.Tuple.Classes
@@ -43,6 +48,13 @@
     , strict
     , template-haskell
   default-language: GHC2021
+  if flag(lens)
+    cpp-options: -DLENS
+    build-depends:
+        lens
+  else
+    build-depends:
+        microlens
 
 test-suite tuple-classes-test
   type: exitcode-stdio-1.0
@@ -64,3 +76,10 @@
     , template-haskell
     , tuple-classes
   default-language: Haskell2010
+  if flag(lens)
+    cpp-options: -DLENS
+    build-depends:
+        lens
+  else
+    build-depends:
+        microlens
