diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright Louis Pan (c) 2017
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of Louis Pan nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,6 @@
+[![Hackage](https://img.shields.io/hackage/v/data-diverse-lens.svg)](https://hackage.haskell.org/package/data-diverse-lens)
+[![Build Status](https://secure.travis-ci.org/louispan/data-diverse-lens.png?branch=master)](http://travis-ci.org/louispan/data-diverse-lens)
+
+Provides "Iso"s & 'Len's for "Data.Diverse.Many" and 'Prism's for "Data.Diverse.Which".
+
+Refer to [ManySpec.hs](https://github.com/louispan/data-diverse-lens/blob/master/test/Data/Diverse/Lens/ManySpec.hs) and [WhichSpec.hs](https://github.com/louispan/data-diverse/blob/master/test/Data/Diverse/Lens/WhichSpec.hs) for example usages.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/data-diverse-lens.cabal b/data-diverse-lens.cabal
new file mode 100644
--- /dev/null
+++ b/data-diverse-lens.cabal
@@ -0,0 +1,47 @@
+name:                data-diverse-lens
+version:             0.1.0.0
+synopsis:            Isos & Lens for Data.Diverse.Many and Prisms for Data.Diverse.Which
+description:         Isos & Lens for Data.Diverse.Many and Prisms for Data.Diverse.Which
+                     Refer to [ManySpec.hs](https://github.com/louispan/data-diverse-lens/blob/master/test/Data/Diverse/Lens/ManySpec.hs) and [WhichSpec.hs](https://github.com/louispan/data-diverse-lens/blob/master/test/Data/Diverse/Lens/WhichSpec.hs) for example usages.
+homepage:            https://github.com/louispan/data-diverse-lens#readme
+license:             BSD3
+license-file:        LICENSE
+author:              Louis Pan
+maintainer:          louis@pan.me
+copyright:           2017 Louis Pan
+category:            Data, Records
+build-type:          Simple
+extra-source-files:  README.md
+cabal-version:       >=1.10
+tested-with:         GHC == 8.0.2
+
+library
+  hs-source-dirs:      src
+  exposed-modules:     Data.Diverse.Lens
+                       Data.Diverse.Lens.Many
+                       Data.Diverse.Lens.Which
+  build-depends:       base >= 4.7 && < 5
+                     , data-diverse >= 0.6 && < 1
+                     , lens >= 4 && < 5
+                     , tagged >= 0.8.5 && < 1
+  ghc-options:         -Wall
+  default-language:    Haskell2010
+
+test-suite data-diverse-lens-test
+  type:                exitcode-stdio-1.0
+  hs-source-dirs:      test
+  main-is:             Spec.hs
+  other-modules:       Data.Diverse.Lens.ManySpec
+                       Data.Diverse.Lens.WhichSpec
+  build-depends:       base
+                     , data-diverse >= 0.6 && < 1
+                     , data-diverse-lens
+                     , hspec >= 2 && < 3
+                     , lens >= 4 && < 5
+                     , tagged >= 0.8.5 && < 1
+  ghc-options:         -threaded -rtsopts -with-rtsopts=-N -Wall
+  default-language:    Haskell2010
+
+source-repository head
+  type:     git
+  location: https://github.com/louispan/data-diverse-lens
diff --git a/src/Data/Diverse/Lens.hs b/src/Data/Diverse/Lens.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Diverse/Lens.hs
@@ -0,0 +1,11 @@
+module Data.Diverse.Lens (
+    -- * Re-export "Data.Diverse" for convenience
+      module Data.Diverse
+    -- * Iso's, Lens and Prisms for 'Data.Diverse.Many.Many' and 'Data.Diverse.Which.Which'
+    , module Data.Diverse.Lens.Many
+    , module Data.Diverse.Lens.Which
+    ) where
+
+import Data.Diverse
+import Data.Diverse.Lens.Many
+import Data.Diverse.Lens.Which
diff --git a/src/Data/Diverse/Lens/Many.hs b/src/Data/Diverse/Lens/Many.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Diverse/Lens/Many.hs
@@ -0,0 +1,198 @@
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE PolyKinds #-}
+{-# LANGUAGE RankNTypes #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TypeApplications #-}
+{-# LANGUAGE TypeFamilies #-}
+
+module Data.Diverse.Lens.Many (
+      -- * Isomorphism
+      _Many
+    , _Many'
+
+    -- * Single field
+    -- ** Lens for a single field
+    , item
+    , item'
+    , itemL
+    , itemL'
+    , itemN
+    , itemN'
+
+    -- * Multiple fields
+    -- ** Lens for multiple fields
+    , project
+    , project'
+    , projectL
+    , projectL'
+    , projectN
+    , projectN'
+
+    ) where
+
+import Control.Lens
+import Data.Proxy
+import Data.Tagged
+import Data.Diverse.Many
+import Data.Diverse.TypeLevel
+
+-- | @_Many = iso fromMany toMany@
+_Many :: IsMany t xs a => Iso' (Many xs) (t xs a)
+_Many = iso fromMany toMany
+{-# INLINE _Many #-}
+
+-- | @_Many' = iso fromMany' toMany'@
+_Many' :: IsMany Tagged xs a => Iso' (Many xs) a
+_Many' = iso fromMany' toMany'
+{-# INLINE _Many' #-}
+
+-----------------------------------------------------------------------
+
+-- | 'fetch' ('view' 'item') and 'replace' ('set' 'item') in 'Lens'' form.
+--
+-- @
+-- let x = (5 :: Int) './' False './' \'X' './' Just \'O' './' 'nil'
+-- x '^.' 'item' \@Int \`shouldBe` 5
+-- (x '&' 'item' \@Int .~ 6) \`shouldBe` (6 :: Int) './' False './' \'X' './' Just \'O' './' 'nil'
+-- @
+item :: forall x xs. UniqueMember x xs => Lens' (Many xs) x
+item = lens fetch replace
+{-# INLINE item #-}
+
+-- | Polymorphic version of 'item'
+item' :: forall x y xs. UniqueMember x xs => Lens (Many xs) (Many (Replace x y xs)) x y
+item' = lens fetch (replace' @x @y Proxy)
+{-# INLINE item' #-}
+
+
+-- | 'fetchL' ('view' 'itemL') and 'replaceL' ('set' 'itemL') in 'Lens'' form.
+--
+-- @
+-- let x = (5 :: Int) './' Tagged \@Foo False './' Tagged \@Bar \'X' './' 'nil'
+-- x '^.' 'itemL' \@Foo Proxy \`shouldBe` Tagged \@Foo False
+-- (x '&' 'itemL' \@Foo Proxy '.~' Tagged \@Foo True) \`shouldBe` (5 :: Int) './' Tagged \@Foo True './' Tagged \@Bar \'X' './' 'nil'
+-- @
+itemL :: forall l xs x proxy. (UniqueLabelMember l xs, x ~ KindAtLabel l xs) => proxy l -> Lens' (Many xs) x
+itemL p = lens (fetchL p) (replaceL p)
+{-# INLINE itemL #-}
+
+-- | Polymorphic version of 'itemL'
+--
+-- @
+-- let x = (5 :: Int) './' Tagged @Foo False './' Tagged \@Bar \'X' './' 'nil'
+-- (x '&' itemL' \@Foo Proxy '.~' \"foo") \`shouldBe` (5 :: Int) './' \"foo" './' Tagged \@Bar \'X' './' 'nil'
+-- @
+itemL' :: forall l y xs x proxy. (UniqueLabelMember l xs, x ~ KindAtLabel l xs) => proxy l -> Lens (Many xs) (Many (Replace x y xs)) x y
+itemL' p = lens (fetchL p) (replaceL' p)
+{-# INLINE itemL' #-}
+
+
+-- | 'fetchN' ('view' 'item') and 'replaceN' ('set' 'item') in 'Lens'' form.
+--
+-- @
+-- let x = (5 :: Int) './' False './' \'X' './' Just \'O' './' (6 :: Int) './' Just \'A' ./ nil
+-- x '^.' 'itemN' (Proxy \@0) \`shouldBe` 5
+-- (x '&' 'itemN' (Proxy @0) '.~' 6) \`shouldBe` (6 :: Int) './' False './' \'X' './' Just \'O' './' (6 :: Int) './' Just \'A' './' 'nil'
+-- @
+itemN ::  forall n x xs proxy. MemberAt n x xs => proxy n -> Lens' (Many xs) x
+itemN p = lens (fetchN p) (replaceN p)
+{-# INLINE itemN #-}
+
+
+-- | Polymorphic version of 'itemN'
+itemN' ::  forall n x y xs proxy. MemberAt n x xs => proxy n -> Lens (Many xs) (Many (ReplaceIndex n y xs)) x y
+itemN' p = lens (fetchN p) (replaceN' @n @x @y p)
+{-# INLINE itemN' #-}
+
+-----------------------------------------------------------------------
+
+-- | 'select' ('view' 'project') and 'amend' ('set' 'project') in 'Lens'' form.
+--
+-- @
+-- 'project' = 'lens' 'select' 'amend'
+-- @
+--
+-- @
+-- let x = (5 :: Int) './' False './' \'X' './' Just \'O' './' 'nil'
+-- x '^.' ('project' \@'[Int, Maybe Char]) \`shouldBe` (5 :: Int) './' Just \'O' './' 'nil'
+-- (x '&' ('project' \@'[Int, Maybe Char]) '.~' ((6 :: Int) './' Just 'P' './' 'nil')) \`shouldBe`
+--     (6 :: Int) './' False './' \'X' './' Just \'P' './' 'nil'
+-- @
+project
+    :: forall smaller larger.
+       (Select smaller larger, Amend smaller larger)
+    => Lens' (Many larger) (Many smaller)
+project = lens select amend
+{-# INLINE project #-}
+
+-- | Polymorphic version of project'
+project'
+    :: forall smaller smaller' larger zipped.
+       (Select smaller larger, Amend' smaller smaller' larger zipped)
+    => Lens (Many larger) (Many (Replaces smaller smaller' larger)) (Many smaller) (Many smaller')
+project' = lens select (amend' @smaller @smaller' Proxy)
+{-# INLINE project' #-}
+
+-- | 'selectL' ('view' 'projectL') and 'amendL' ('set' 'projectL') in 'Lens'' form.
+--
+-- @
+-- let x = False './' Tagged \@\"Hi" (5 :: Int) './' Tagged \@Foo False './' Tagged \@Bar \'X' './' Tagged \@\"Bye" \'O' './' 'nil'
+-- x '^.' ('projectL' \@'[Foo, Bar] Proxy) \`shouldBe` Tagged \@Foo False './' Tagged \@Bar \'X' './' nil
+-- (x '&' ('projectL' \@'[\"Hi", \"Bye"] Proxy) '.~' (Tagged \@\"Hi" (6 :: Int) './' Tagged \@\"Bye" \'P' './' nil)) '`shouldBe`
+--     False './' Tagged \@\"Hi" (6 :: Int) './' Tagged \@Foo False './' Tagged \@Bar \'X' './' Tagged \@\"Bye" \'P' './' 'nil'
+-- @
+projectL
+    :: forall ls smaller larger proxy.
+       ( Select smaller larger
+       , Amend smaller larger
+       , smaller ~ KindsAtLabels ls larger
+       , IsDistinct ls
+       , UniqueLabels ls larger)
+    => proxy ls -> Lens' (Many larger) (Many smaller)
+projectL p = lens (selectL p) (amendL p)
+{-# INLINE projectL #-}
+
+-- | Polymorphic version of projectL'
+--
+-- @
+-- let x = False './' Tagged \@\"Hi" (5 :: Int) './' Tagged \@Foo False './' Tagged \@Bar \'X' './' Tagged \@\"Bye" \'O' './' 'nil'
+-- (x '&' ('projectL'' \@'[\"Hi", \"Bye"] Proxy) '.~' (True './' Tagged \@\"Changed" False './' 'nil')) \`shouldBe`
+--     False './' True './' Tagged \@Foo False './' Tagged \@Bar \'X' './' Tagged \@\"Changed" False './' 'nil'
+-- @
+projectL'
+    :: forall ls smaller smaller' larger proxy zipped.
+       ( Select smaller larger
+       , Amend' smaller smaller' larger zipped
+       , smaller ~ KindsAtLabels ls larger
+       , IsDistinct ls
+       , UniqueLabels ls larger)
+    => proxy ls -> Lens (Many larger) (Many (Replaces smaller smaller' larger)) (Many smaller) (Many smaller')
+projectL' p = lens (selectL p) (amendL' p)
+{-# INLINE projectL' #-}
+
+-- | 'selectN' ('view' 'projectN') and 'amendN' ('set' 'projectN') in 'Lens'' form.
+--
+-- @
+-- 'projectN' = 'lens' 'selectN' 'amendN'
+-- @
+--
+-- @
+-- let x = (5 :: Int) './' False './' \'X' './' Just \'O' './' (6 :: Int) './' Just \'A' './' 'nil'
+-- x '^.' ('projectN' \@'[5, 4, 0] Proxy) \`shouldBe` Just \'A' './' (6 :: Int) './' (5 ::Int) './' 'nil'
+-- (x '&' ('projectN' \@'[5, 4, 0] Proxy) '.~' (Just \'B' './' (8 :: Int) './' (4 ::Int) './' nil)) \`shouldBe`
+--     (4 :: Int) './' False './' \'X' './' Just \'O' './' (8 :: Int) './' Just \'B' './' 'nil'
+-- @
+projectN
+    :: forall ns smaller larger proxy.
+       (SelectN ns smaller larger, AmendN ns smaller larger)
+    => proxy ns -> Lens' (Many larger) (Many smaller)
+projectN p = lens (selectN p) (amendN p)
+{-# INLINE projectN #-}
+
+-- | Polymorphic version of 'projectN'
+projectN'
+    :: forall ns smaller smaller' larger proxy zipped.
+       (SelectN ns smaller larger, AmendN' ns smaller smaller' larger zipped)
+    => proxy ns -> Lens (Many larger) (Many (ReplacesIndex ns smaller' larger)) (Many smaller) (Many smaller')
+projectN' p = lens (selectN p) (amendN' p)
+{-# INLINE projectN' #-}
diff --git a/src/Data/Diverse/Lens/Which.hs b/src/Data/Diverse/Lens/Which.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Diverse/Lens/Which.hs
@@ -0,0 +1,133 @@
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE RankNTypes #-}
+
+module Data.Diverse.Lens.Which (
+      -- * Single type
+      -- ** Prism
+      facet
+    , facetL
+    , facetN
+
+      -- * Multiple types
+      -- ** Prism
+    , inject
+    , injectL
+    , injectN
+
+    ) where
+
+import Control.Lens
+import Data.Diverse.Which
+import Data.Diverse.TypeLevel
+
+-----------------------------------------------------------------
+
+-- | Utility to convert Either to Maybe
+hush :: Either a b -> Maybe b
+hush = either (const Nothing) Just
+
+-----------------------------------------------------------------
+
+-- | 'pick' ('review' 'facet') and 'trial' ('preview' 'facet') in 'Prism'' form.
+--
+-- @
+-- 'facet' = 'prism'' 'pick' (either (const Nothing) Just . 'trial')
+-- @
+--
+-- @
+-- let y = 'review' ('facet' \@Int) (5 :: Int) :: 'Which' '[Bool, Int, Char, Bool, Char] -- 'pick'
+--     x = 'preview' ('facet' \@Int) y -- 'trial'
+-- x \`shouldBe` (Just 5)
+-- @
+facet :: forall x xs. (UniqueMember x xs) => Prism' (Which xs) x
+facet = prism' pick (hush . trial)
+{-# INLINE facet #-}
+
+-- | 'pickL' ('review' 'facetL') and 'trialL' ('preview' 'facetL') in 'Prism'' form.
+--
+-- @
+-- let y = 'review' ('facetL' \@Bar Proxy) (Tagged (5 :: Int)) :: Which '[Tagged Foo Bool, Tagged Bar Int, Char, Bool, Char]
+--     x = 'preview' ('facetL' \@Bar Proxy) y
+-- x \`shouldBe` (Just (Tagged 5))
+-- @
+facetL :: forall l xs x proxy. (UniqueLabelMember l xs, x ~ KindAtLabel l xs) => proxy l -> Prism' (Which xs) x
+facetL p = prism' (pickL p) (hush . trialL p)
+{-# INLINE facetL #-}
+
+-- | 'pickN' ('review' 'facetN') and 'trialN' ('preview' 'facetN') in 'Prism'' form.
+--
+-- @
+-- 'facetN' p = 'prism'' ('pickN' p) (either (const Nothing) Just . 'trialN' p)
+-- @
+--
+-- @
+-- let y = 'review' ('facetN' (Proxy \@4)) (5 :: Int) :: 'Which' '[Bool, Int, Char, Bool, Int, Char] -- 'pickN'
+--     x = 'preview' ('facetN' (Proxy \@4)) y -- 'trialN'
+-- x \`shouldBe` (Just 5)
+-- @
+facetN :: forall n xs x proxy. (MemberAt n x xs) => proxy n -> Prism' (Which xs) x
+facetN p = prism' (pickN p) (hush . trialN p)
+{-# INLINE facetN #-}
+
+------------------------------------------------------------------
+
+
+-- | 'diversify' ('review' 'inject') and 'reinterpret' ('preview' 'inject') in 'Prism'' form.
+--
+-- @
+-- let x = 'pick' (5 :: Int) :: 'Which' '[String, Int]
+--     y = 'review' ('inject' \@_ \@[Bool, Int, Char, String]) x -- 'diversify'
+-- y \`shouldBe` pick (5 :: Int) :: 'Which' '[Bool, Int, Char, String]
+-- let y' = 'preview' ('inject' \@[String, Int]) y -- 'reinterpret'
+-- y' \`shouldBe` Just (pick (5 :: Int)) :: Maybe ('Which' '[String, Int])
+-- @
+inject
+    :: forall branch tree.
+       ( Diversify tree branch
+       , Reinterpret branch tree
+       )
+    => Prism' (Which tree) (Which branch)
+inject = prism' diversify (hush . reinterpret)
+{-# INLINE inject #-}
+
+
+-- | 'diversifyL' ('review' 'injectL') and 'reinterpretL' ('preview' 'injectL') in 'Prism'' form.
+--
+-- @
+-- let t = 'pick' \@[Tagged Bar Int, Tagged Foo Bool, Tagged Hi Char, Tagged Bye Bool] (5 :: Tagged Bar Int)
+--     b = 'pick' \@'[Tagged Foo Bool, Tagged Bar Int] (5 :: Tagged Bar Int)
+--     t' = 'review' ('injectL' \@[Foo, Bar] \@_ \@[Tagged Bar Int, Tagged Foo Bool, Tagged Hi Char, Tagged Bye Bool] Proxy) b
+--     b' = 'preview' ('injectL' \@[Foo, Bar] Proxy) t'
+-- t \`shouldBe` t'
+-- b' \`shouldBe` Just b
+-- @
+injectL
+    :: forall ls branch tree proxy.
+       ( Diversify tree branch
+       , Reinterpret branch tree
+       , branch ~ KindsAtLabels ls tree
+       , UniqueLabels ls tree
+       , IsDistinct ls
+       )
+    => proxy ls -> Prism' (Which tree) (Which branch)
+injectL p = prism' (diversifyL p) (hush . reinterpretL p)
+{-# INLINE injectL #-}
+
+-- | 'diversifyN' ('review' 'injectN') and 'reinterpretN' ('preview' 'injectN') in 'Prism'' form.
+--
+-- @
+-- let x = 'pick' (5 :: Int) :: 'Which' '[String, Int]
+--     y = 'review' (injectN \@[3, 1] \@_ \@[Bool, Int, Char, String] Proxy) x -- 'diversifyN'
+-- y \`shouldBe` pick (5 :: Int) :: 'Which' '[Bool, Int, Char, String]
+-- let y' = 'preview' ('injectN' @[3, 1] \@[String, Int] Proxy) y -- 'reinterpertN''
+-- y' \`shouldBe` Just ('pick' (5 :: Int)) :: Maybe ('Which' '[String, Int])
+-- @
+injectN
+    :: forall indices branch tree proxy.
+       ( DiversifyN indices tree branch
+       , ReinterpretN indices branch tree
+       )
+    => proxy indices -> Prism' (Which tree) (Which branch)
+injectN p = prism' (diversifyN p) (reinterpretN p)
+{-# INLINE injectN #-}
diff --git a/test/Data/Diverse/Lens/ManySpec.hs b/test/Data/Diverse/Lens/ManySpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Data/Diverse/Lens/ManySpec.hs
@@ -0,0 +1,117 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE TypeApplications #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE TypeOperators #-}
+
+module Data.Diverse.Lens.ManySpec (main, spec) where
+
+import Control.Lens
+import Data.Diverse
+import Data.Diverse.Lens
+import Data.Tagged
+import Data.Typeable
+import Test.Hspec
+
+-- `main` is here so that this module can be run from GHCi on its own.  It is
+-- not needed for automatic spec discovery.
+main :: IO ()
+main = hspec spec
+
+data Foo
+data Bar
+
+spec :: Spec
+spec = do
+    describe "Many" $ do
+
+        it "can converted to and from a tuple using 'Many'' Iso" $ do
+            let x = (5 :: Int) ./ False ./ 'X' ./ Just 'O' ./ nil
+                t = ((5 :: Int), False, 'X', Just 'O')
+            x `shouldBe` review _Many' t
+            t `shouldBe` view _Many' x
+
+        it "has getter/setter lens using 'item'" $ do
+            let x = (5 :: Int) ./ False ./ 'X' ./ Just 'O' ./ nil
+            x ^. item @Int `shouldBe` 5
+            (x & item @Int .~ 6) `shouldBe` (6 :: Int) ./ False ./ 'X' ./ Just 'O' ./ nil
+            x ^. item @Bool `shouldBe` False
+            (x & item @Bool .~ True) `shouldBe` (5 :: Int) ./ True ./ 'X' ./ Just 'O' ./ nil
+            x ^. item @Char `shouldBe` 'X'
+            x ^. item @(Maybe Char) `shouldBe` Just 'O'
+
+        it "has polymorphic getter/setter lens using 'item''" $ do
+            let x = (5 :: Int) ./ False ./ 'X' ./ Just 'O' ./ nil
+            (x & item' @(Maybe Char) .~ Just 'P') `shouldBe` (5 :: Int) ./ False ./ 'X' ./ Just 'P' ./ nil
+            (x & item' @Int .~ 'Z') `shouldBe` 'Z' ./ False ./ 'X' ./ Just 'O' ./ nil
+            (x & item' @Bool .~ 'Z') `shouldBe` (5 :: Int) ./ 'Z' ./ 'X' ./ Just 'O' ./ nil
+            (x & item' @Char .~ True) `shouldBe` (5 :: Int) ./ False ./ True ./ Just 'O' ./ nil
+            (x & item' @(Maybe Char) .~ 'P') `shouldBe` (5 :: Int) ./ False ./ 'X' ./ 'P' ./ nil
+
+        it "has getter/setter lens using 'item'" $ do
+            let x = (5 :: Int) ./ Tagged @Foo False ./ Tagged @Bar 'X' ./ nil
+            x ^. itemL @Foo Proxy `shouldBe` Tagged @Foo False
+            (x & itemL @Foo Proxy .~ Tagged @Foo True) `shouldBe` (5 :: Int) ./ Tagged @Foo True ./ Tagged @Bar 'X' ./ nil
+
+        it "has polymorphic getter/setter lens using 'itemL''" $ do
+            let x = (5 :: Int) ./ Tagged @Foo False ./ Tagged @Bar 'X' ./ nil
+            (x & itemL' @Foo Proxy .~ "foo") `shouldBe` (5 :: Int) ./ "foo" ./ Tagged @Bar 'X' ./ nil
+
+        it "has getter/setter lens for duplicate fields using 'itemN'" $ do
+            let x = (5 :: Int) ./ False ./ 'X' ./ Just 'O' ./ (6 :: Int) ./ Just 'A' ./ nil
+            x ^. itemN (Proxy @0) `shouldBe` 5
+            (x & itemN (Proxy @0) .~ 6) `shouldBe` (6 :: Int) ./ False ./ 'X' ./ Just 'O' ./ (6 :: Int) ./ Just 'A' ./ nil
+            x ^. itemN (Proxy @1) `shouldBe` False
+            (x & itemN (Proxy @1) .~ True) `shouldBe` (5 :: Int) ./ True ./ 'X' ./ Just 'O' ./ (6 :: Int) ./ Just 'A' ./ nil
+            x ^. itemN (Proxy @2) `shouldBe` 'X'
+            (x & itemN (Proxy @2) .~ 'O') `shouldBe` (5 :: Int) ./ False ./ 'O' ./ Just 'O' ./ (6 :: Int) ./ Just 'A' ./ nil
+            x ^. itemN (Proxy @3) `shouldBe` Just 'O'
+            (x & itemN (Proxy @3) .~ Just 'P') `shouldBe` (5 :: Int) ./ False ./ 'X' ./ Just 'P' ./ (6 :: Int) ./ Just 'A' ./ nil
+            x ^. itemN (Proxy @4) `shouldBe` 6
+            (x & itemN (Proxy @4) .~ 7) `shouldBe` (5 :: Int) ./ False ./ 'X' ./ Just 'O' ./ (7 :: Int) ./ Just 'A' ./ nil
+            x ^. itemN (Proxy @5) `shouldBe` Just 'A'
+            (x & itemN (Proxy @5) .~ Just 'B') `shouldBe` (5 :: Int) ./ False ./ 'X' ./ Just 'O' ./ (6 :: Int) ./ Just 'B' ./ nil
+
+        it "has polymorphic getter/setter lens for duplicate fields using 'itemN''" $ do
+            let x = (5 :: Int) ./ False ./ 'X' ./ Just 'O' ./ (6 :: Int) ./ Just 'A' ./ nil
+            (x & itemN' (Proxy @0) .~ "Foo") `shouldBe` "Foo" ./ False ./ 'X' ./ Just 'O' ./ (6 :: Int) ./ Just 'A' ./ nil
+            (x & itemN' (Proxy @1) .~ "Foo") `shouldBe` (5 :: Int) ./ "Foo" ./ 'X' ./ Just 'O' ./ (6 :: Int) ./ Just 'A' ./ nil
+            (x & itemN' (Proxy @2) .~ "Foo") `shouldBe` (5 :: Int) ./ False ./ "Foo" ./ Just 'O' ./ (6 :: Int) ./ Just 'A' ./ nil
+            (x & itemN' (Proxy @3) .~ "Foo") `shouldBe` (5 :: Int) ./ False ./ 'X' ./ "Foo" ./ (6 :: Int) ./ Just 'A' ./ nil
+            (x & itemN' (Proxy @4) .~ "Foo") `shouldBe` (5 :: Int) ./ False ./ 'X' ./ Just 'O' ./ "Foo" ./ Just 'A' ./ nil
+            (x & itemN' (Proxy @5) .~ "Foo") `shouldBe` (5 :: Int) ./ False ./ 'X' ./ Just 'O' ./ (6 :: Int) ./ "Foo" ./ nil
+
+        it "has getter/setter lens for multiple fields using 'project'" $ do
+            let x = (5 :: Int) ./ False ./ 'X' ./ Just 'O' ./ nil
+            x ^. (project @'[Int, Maybe Char]) `shouldBe` (5 :: Int) ./ Just 'O' ./ nil
+            (x & (project @'[Int, Maybe Char]) .~ ((6 :: Int) ./ Just 'P' ./ nil)) `shouldBe`
+                (6 :: Int) ./ False ./ 'X' ./ Just 'P' ./ nil
+
+        it "has polymorphic getter/setter lens for multiple fields using 'project'" $ do
+            let x = (5 :: Int) ./ False ./ 'X' ./ Just 'O' ./ nil
+            (x & (project' @'[Int, Maybe Char]) .~ ("Foo" ./ Just "Bar" ./ nil)) `shouldBe`
+                "Foo" ./ False ./ 'X' ./ Just "Bar" ./ nil
+
+        it "has getter/setter lens for multiple labelled fields using 'projectL'" $ do
+            let x = False ./ Tagged @"Hi" (5 :: Int) ./ Tagged @Foo False ./ Tagged @Bar 'X' ./ Tagged @"Bye" 'O' ./ nil
+            x ^. (projectL @'[Foo, Bar] Proxy) `shouldBe` Tagged @Foo False ./ Tagged @Bar 'X' ./ nil
+            (x & (projectL @'["Hi", "Bye"] Proxy) .~ (Tagged @"Hi" (6 :: Int) ./ Tagged @"Bye" 'P' ./ nil)) `shouldBe`
+                False ./ Tagged @"Hi" (6 :: Int) ./ Tagged @Foo False ./ Tagged @Bar 'X' ./ Tagged @"Bye" 'P' ./ nil
+
+        it "has polymorphic getter/setter lens for multiple labelled fields using 'projectL''" $ do
+            let x = False ./ Tagged @"Hi" (5 :: Int) ./ Tagged @Foo False ./ Tagged @Bar 'X' ./ Tagged @"Bye" 'O' ./ nil
+            (x & (projectL' @'["Hi", "Bye"] Proxy) .~ (True ./ Tagged @"Changed" False ./ nil)) `shouldBe`
+                False ./ True ./ Tagged @Foo False ./ Tagged @Bar 'X' ./ Tagged @"Changed" False ./ nil
+
+        it "has getter/setter lens for multiple fields with duplicates using 'projectN'" $ do
+            let x = (5 :: Int) ./ False ./ 'X' ./ Just 'O' ./ (6 :: Int) ./ Just 'A' ./ nil
+            x ^. (projectN @'[5, 4, 0] Proxy) `shouldBe` Just 'A' ./ (6 :: Int) ./ (5 ::Int) ./ nil
+            (x & (projectN @'[5, 4, 0] Proxy) .~ (Just 'B' ./ (8 :: Int) ./ (4 ::Int) ./ nil)) `shouldBe`
+                (4 :: Int) ./ False ./ 'X' ./ Just 'O' ./ (8 :: Int) ./ Just 'B' ./ nil
+
+        it "has polymorphic getter/setter lens for multiple fields with duplicates using 'projectN'" $ do
+            let x = (5 :: Int) ./ False ./ 'X' ./ Just 'O' ./ (6 :: Int) ./ Just 'A' ./ nil
+            (x & (projectN' @'[5, 4, 0] Proxy) .~ (Just "Foo" ./ (8 :: Int) ./ "Bar" ./ nil)) `shouldBe`
+                "Bar" ./ False ./ 'X' ./ Just 'O' ./ (8 :: Int) ./ Just "Foo" ./ nil
diff --git a/test/Data/Diverse/Lens/WhichSpec.hs b/test/Data/Diverse/Lens/WhichSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Data/Diverse/Lens/WhichSpec.hs
@@ -0,0 +1,71 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE TypeApplications #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE TypeOperators #-}
+
+module Data.Diverse.Lens.WhichSpec (main, spec) where
+
+import Control.Lens
+import Data.Diverse
+import Data.Diverse.Lens
+import Data.Proxy
+import Data.Tagged
+import Test.Hspec
+
+data Foo
+data Bar
+data Hi
+data Bye
+
+-- `main` is here so that this module can be run from GHCi on its own.  It is
+-- not needed for automatic spec discovery.
+main :: IO ()
+main = hspec spec
+
+-- -- | Utility to convert Either to Maybe
+-- hush :: Either a b -> Maybe b
+-- hush = either (const Nothing) Just
+
+spec :: Spec
+spec = do
+    describe "Which" $ do
+
+        it "can be constructed and destructed by type with 'facet'" $ do
+            let y = review (facet @Int) (5 :: Int) :: Which '[Bool, Int, Char, Bool, Char]
+                x = preview (facet @Int) y
+            x `shouldBe` (Just 5)
+
+        it "can be constructed and destructed by label with 'facetL'" $ do
+            let y = review (facetL @Bar Proxy) (Tagged (5 :: Int)) :: Which '[Tagged Foo Bool, Tagged Bar Int, Char, Bool, Char]
+                x = preview (facetL @Bar Proxy) y
+            x `shouldBe` (Just (Tagged 5))
+
+        it "can be constructed and destructed by index with 'facetN'" $ do
+            let y = review (facetN (Proxy @4)) (5 :: Int) :: Which '[Bool, Int, Char, Bool, Int, Char]
+                x = preview (facetN (Proxy @4)) y
+            x `shouldBe` (Just 5)
+
+        it "can be 'diversify'ed and 'reinterpreted' by type with 'inject'" $ do
+            let x = pick (5 :: Int) :: Which '[String, Int]
+                y = review (inject @_ @[Bool, Int, Char, String]) x
+            y `shouldBe` pick (5 :: Int)
+            let y' = preview (inject @[String, Int]) y
+            y' `shouldBe` Just (pick (5 :: Int))
+
+        it "can be 'diversifyL'ed and 'reinterpretedL' by label with 'injectL'" $ do
+            let t = pick @[Tagged Bar Int, Tagged Foo Bool, Tagged Hi Char, Tagged Bye Bool] (5 :: Tagged Bar Int)
+                b = pick @'[Tagged Foo Bool, Tagged Bar Int] (5 :: Tagged Bar Int)
+                t' = review (injectL @[Foo, Bar] @_ @[Tagged Bar Int, Tagged Foo Bool, Tagged Hi Char, Tagged Bye Bool] Proxy) b
+                b' = preview (injectL @[Foo, Bar] Proxy) t'
+            t `shouldBe` t'
+            b' `shouldBe` Just b
+
+        it "can be 'diversifyN'ed and 'reinterpretedN' by index with 'injectN'" $ do
+            let x = pick (5 :: Int) :: Which '[String, Int]
+                y = review (injectN @[3, 1] @_ @[Bool, Int, Char, String] Proxy) x
+            y `shouldBe` pick (5 :: Int)
+            let y' = preview (injectN @[3, 1] @[String, Int] Proxy) y
+            y' `shouldBe` Just (pick (5 :: Int))
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -0,0 +1,1 @@
+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
