diff --git a/changelog b/changelog
--- a/changelog
+++ b/changelog
@@ -1,3 +1,34 @@
+1.2.2
+
+* Implement Either instances for ReviewFailure, AsFailure, ReviewSuccess,
+  and AsSuccess
+* Add hedgehog property tests for Either optic instances
+
+1.2.1
+
+* Updates to documentation.
+
+1.2.0
+
+* Add Validator e p x a profunctor transformer newtype with instances
+  for Functor, Apply, Applicative, Alt, Selective, Profunctor, Strong,
+  Choice, Semigroupoid, Category, Arrow, ArrowChoice, ArrowApply, and
+  Wrapped/Rewrapped
+* Add classy optics for Validator (GetValidator, HasValidator,
+  ReviewValidator, AsValidator)
+* Add profunctor newtypes Iso'' and Prism'' for wrapping monomorphic
+  optics as two-parameter profunctors
+* Add unitValidator, taggedValidator, and swapValidator isomorphisms
+* Add nonEmptyList example validators using Iso'' and Prism''
+* Add tagged package dependency
+* Expand hedgehog property tests from 4 to 35 covering semigroup,
+  monoid, functor, applicative, apply, alt, bifunctor, foldValidation,
+  iso/prism roundtrips, swap, and all Validator instances
+* Replace HUnit tests with doctest suite (187 examples)
+* Add tests: True to cabal.project
+* Update examples for current API (remove Validate class, use either iso)
+* Hide Prelude.either to avoid ambiguity with Data.Validation.either
+
 1.1.5
 
 * Update version bounds
diff --git a/src/Data/Validation.hs b/src/Data/Validation.hs
--- a/src/Data/Validation.hs
+++ b/src/Data/Validation.hs
@@ -1,350 +1,1048 @@
 {-# LANGUAGE CPP #-}
 {-# LANGUAGE DeriveDataTypeable #-}
 {-# LANGUAGE DeriveGeneric #-}
-{-# LANGUAGE LambdaCase #-}
-{-# LANGUAGE NoImplicitPrelude #-}
-{-# LANGUAGE TypeFamilies #-}
-
--- | A data type similar to @Data.Either@ that accumulates failures.
-module Data.Validation
-(
-  -- * Data type
-  Validation(..)
-  -- * Constructing validations
-, validate
-, validationNel
-, fromEither
-, liftError
-  -- * Functions on validations
-, validation
-, toEither
-, orElse
-, valueOr
-, ensure
-, codiagonal
-, validationed
-, bindValidation
-  -- * Prisms
-  -- | These prisms are useful for writing code which is polymorphic in its
-  -- choice of Either or Validation. This choice can then be made later by a
-  -- user, depending on their needs.
-  --
-  -- An example of this style of usage can be found
-  -- <https://github.com/qfpl/validation/blob/master/examples/src/PolymorphicEmail.hs here>
-, _Failure
-, _Success
-  -- * Isomorphisms
-, Validate(..)
-, revalidate
-) where
-
-import Control.Applicative(Applicative((<*>), pure), (<$>))
-import Control.DeepSeq (NFData (rnf))
-import Control.Lens (over, under)
-import Control.Lens.Getter((^.))
-import Control.Lens.Iso(Iso, iso, from
-#if !MIN_VERSION_lens(4,20,0)
-                       , Swapped(..))
-#else
-                       )
-#endif
-import Control.Lens.Prism(Prism, _Left, _Right)
-import Control.Lens.Review(( # ))
-import Control.Selective(Selective(..))
-import Data.Bifoldable(Bifoldable(bifoldr))
-import Data.Bifunctor(Bifunctor(bimap))
-import Data.Bifunctor.Swap(Swap(..))
-import Data.Bitraversable(Bitraversable(bitraverse))
-import Data.Data(Data)
-import Data.Either(Either(Left, Right), either)
-import Data.Eq(Eq)
-import Data.Foldable(Foldable(foldr))
-import Data.Function((.), ($), id)
-import Data.Functor(Functor(fmap))
-import Data.Functor.Alt(Alt((<!>)))
-import Data.Functor.Apply(Apply((<.>)))
-import Data.List.NonEmpty (NonEmpty)
-import Data.Monoid(Monoid(mempty))
-import Data.Ord(Ord)
-import Data.Semigroup(Semigroup((<>)))
-import Data.Traversable(Traversable(traverse))
-import Data.Typeable(Typeable)
-import GHC.Generics (Generic)
-import Prelude(Show, Maybe(..))
-
-
--- | A @Validation@ is either a value of the type @err@ or @a@, similar to 'Either'. However,
--- the 'Applicative' instance for @Validation@ /accumulates/ errors using a 'Semigroup' on @err@.
--- In contrast, the @Applicative@ for @Either@ returns only the first error.
---
--- A consequence of this is that @Validation@ has no 'Data.Functor.Bind.Bind' or 'Control.Monad.Monad' instance. This is because
--- such an instance would violate the law that a Monad's 'Control.Monad.ap' must equal the
--- @Applicative@'s 'Control.Applicative.<*>'
---
--- An example of typical usage can be found <https://github.com/qfpl/validation/blob/master/examples/src/Email.hs here>.
---
-data Validation err a =
-  Failure err
-  | Success a
-  deriving (Data, Eq, Generic, Ord, Show, Typeable)
-
-instance Functor (Validation err) where
-  fmap _ (Failure e) =
-    Failure e
-  fmap f (Success a) =
-    Success (f a)
-  {-# INLINE fmap #-}
-
-instance Semigroup err => Apply (Validation err) where
-  Failure e1 <.> b = Failure $ case b of
-    Failure e2 -> e1 <> e2
-    Success _ -> e1
-  Success _  <.> Failure e2 =
-    Failure e2
-  Success f  <.> Success a  =
-    Success (f a)
-  {-# INLINE (<.>) #-}
-
-instance Semigroup err => Applicative (Validation err) where
-  pure =
-    Success
-  (<*>) =
-    (<.>)
-
--- | For two errors, this instance reports only the last of them.
-instance Alt (Validation err) where
-  Failure _ <!> x =
-    x
-  Success a <!> _ =
-    Success a
-  {-# INLINE (<!>) #-}
-
-instance Semigroup err => Selective (Validation err) where
-  select (Failure e) _ = Failure e
-  select (Success x) f = either (\a -> ($ a) <$> f) Success x
-
-instance Foldable (Validation err) where
-  foldr f x (Success a) =
-    f a x
-  foldr _ x (Failure _) =
-    x
-  {-# INLINE foldr #-}
-
-instance Traversable (Validation err) where
-  traverse f (Success a) =
-    Success <$> f a
-  traverse _ (Failure e) =
-    pure (Failure e)
-  {-# INLINE traverse #-}
-
-instance Bifunctor Validation where
-  bimap f _ (Failure e) =
-    Failure (f e)
-  bimap _ g (Success a) =
-    Success (g a)
-  {-# INLINE bimap #-}
-
-
-instance Bifoldable Validation where
-  bifoldr _ g x (Success a) =
-    g a x
-  bifoldr f _ x (Failure e) =
-    f e x
-  {-# INLINE bifoldr #-}
-
-instance Bitraversable Validation where
-  bitraverse _ g (Success a) =
-    Success <$> g a
-  bitraverse f _ (Failure e) =
-    Failure <$> f e
-  {-# INLINE bitraverse #-}
-
-appValidation ::
-  (err -> err -> err)
-  -> Validation err a
-  -> Validation err a
-  -> Validation err a
-appValidation m (Failure e1) (Failure e2) =
-  Failure (e1 `m` e2)
-appValidation _ (Failure _) (Success a2) =
-  Success a2
-appValidation _ (Success a1) (Failure _) =
-  Success a1
-appValidation _ (Success a1) (Success _) =
-  Success a1
-{-# INLINE appValidation #-}
-
-instance Semigroup e => Semigroup (Validation e a) where
-  (<>) =
-    appValidation (<>)
-  {-# INLINE (<>) #-}
-
-instance Monoid e => Monoid (Validation e a) where
-  mempty =
-    Failure mempty
-  {-# INLINE mempty #-}
-
-#if !MIN_VERSION_lens(4,20,0)
-instance Swapped Validation where
-  swapped = iso swap swap
-  {-# INLINE swapped #-}
-#endif
-
-instance Swap Validation where
-  swap v =
-    case v of
-      Failure e -> Success e
-      Success a -> Failure a
-  {-# INLINE swap #-}
-
-instance (NFData e, NFData a) => NFData (Validation e a) where
-  rnf v =
-    case v of
-      Failure e -> rnf e
-      Success a -> rnf a
-
--- | 'validate's an @a@ producing an updated optional value, returning
--- @e@ in the empty case.
---
--- This can be thought of as having the less general type:
---
--- @
--- validate :: e -> (a -> Maybe b) -> a -> Validation e b
--- @
-validate :: Validate v => e -> (a -> Maybe b) -> a -> v e b
-validate e p a = case p a of
-  Nothing -> _Failure # e
-  Just b  -> _Success # b
-
--- | 'validationNel' is 'liftError' specialised to 'NonEmpty' lists, since
--- they are a common semigroup to use.
-validationNel :: Either e a -> Validation (NonEmpty e) a
-validationNel = liftError pure
-
--- | Converts from 'Either' to 'Validation'.
-fromEither :: Either e a -> Validation e a
-fromEither = liftError id
-
--- | 'liftError' is useful for converting an 'Either' to an 'Validation'
--- when the @Left@ of the 'Either' needs to be lifted into a 'Semigroup'.
-liftError :: (b -> e) -> Either b a -> Validation e a
-liftError f = either (Failure . f) Success
-
--- | 'validation' is the catamorphism for @Validation@.
-validation :: (e -> c) -> (a -> c) -> Validation e a -> c
-validation ec ac = \case
-  Failure e -> ec e
-  Success a -> ac a
-
--- | Converts from 'Validation' to 'Either'.
-toEither :: Validation e a -> Either e a
-toEither = validation Left Right
-
--- | @v 'orElse' a@ returns @a@ when @v@ is Failure, and the @a@ in @Success a@.
---
--- This can be thought of as having the less general type:
---
--- @
--- orElse :: Validation e a -> a -> a
--- @
-orElse :: Validate v => v e a -> a -> a
-orElse v a = case v ^. _Validation of
-  Failure _ -> a
-  Success x -> x
-
--- | Return the @a@ or run the given function over the @e@.
---
--- This can be thought of as having the less general type:
---
--- @
--- valueOr :: (e -> a) -> Validation e a -> a
--- @
-valueOr :: Validate v => (e -> a) -> v e a -> a
-valueOr ea v = case v ^. _Validation of
-  Failure e -> ea e
-  Success a -> a
-
--- | 'codiagonal' gets the value out of either side.
-codiagonal :: Validation a a -> a
-codiagonal = valueOr id
-
--- | 'ensure' ensures that a validation remains unchanged upon failure,
--- updating a successful validation with an optional value that could fail
--- with @e@ otherwise.
---
--- This can be thought of as having the less general type:
---
--- @
--- ensure :: e -> (a -> Maybe b) -> Validation e a -> Validation e b
--- @
-ensure :: Validate v => e -> (a -> Maybe b) -> v e a -> v e b
-ensure e p =
-  over _Validation $ \case
-    Failure x -> Failure x
-    Success a -> validate e p a
-
--- | Run a function on anything with a Validate instance (usually Either)
--- as if it were a function on Validation
---
--- This can be thought of as having the type
---
--- @(Either e a -> Either e' a') -> Validation e a -> Validation e' a'@
-validationed :: Validate v => (v e a -> v e' a') -> Validation e a -> Validation e' a'
-validationed = under _Validation
-
--- | @bindValidation@ binds through a Validation, which is useful for
--- composing Validations sequentially. Note that despite having a bind
--- function of the correct type, Validation is not a monad.
--- The reason is, this bind does not accumulate errors, so it does not
--- agree with the Applicative instance.
---
--- There is nothing wrong with using this function, it just does not make a
--- valid @Monad@ instance.
-bindValidation :: Validation e a -> (a -> Validation e b) -> Validation e b
-bindValidation v f = case v of
-  Failure e -> Failure e
-  Success a -> f a
-
--- | The @Validate@ class carries around witnesses that the type @f@ is isomorphic
--- to Validation, and hence isomorphic to Either.
-class Validate f where
-  _Validation ::
-    Iso (f e a) (f g b) (Validation e a) (Validation g b)
-
-  _Either ::
-    Iso (f e a) (f g b) (Either e a) (Either g b)
-  _Either = _Validation . iso toEither fromEither
-  {-# INLINE _Either #-}
-
-instance Validate Validation where
-  _Validation =
-    id
-  {-# INLINE _Validation #-}
-
-instance Validate Either where
-  _Validation =
-    iso
-      fromEither
-      toEither
-  {-# INLINE _Validation #-}
-  _Either =
-    id
-  {-# INLINE _Either #-}
-
--- | This prism generalises 'Control.Lens.Prism._Left'. It targets the failure case of either 'Either' or 'Validation'.
-_Failure ::
-  Validate f =>
-  Prism (f e1 a) (f e2 a) e1 e2
-_Failure = _Either . _Left
-{-# INLINE _Failure #-}
-
--- | This prism generalises 'Control.Lens.Prism._Right'. It targets the success case of either 'Either' or 'Validation'.
-_Success ::
-  Validate f =>
-  Prism (f e a) (f e b) a b
-_Success = _Either . _Right
-{-# INLINE _Success #-}
-
--- | 'revalidate' converts between any two instances of 'Validate'.
-revalidate :: (Validate f, Validate g) => Iso (f e1 s) (f e2 t) (g e1 s) (g e2 t)
-revalidate = _Validation . from _Validation
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE FunctionalDependencies #-}
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE RankNTypes #-}
+{-# LANGUAGE TupleSections #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE NoImplicitPrelude #-}
+{-# OPTIONS_GHC -Wall #-}
+
+-- \$setup
+-- >>> import Prelude hiding (either, id, (.))
+-- >>> import Data.List.NonEmpty(NonEmpty(..))
+-- >>> import Data.Semigroup(Semigroup((<>)))
+-- >>> import Control.Lens((^?), (#), review, view, from, _Wrapped')
+-- >>> import Data.Functor.Alt(Alt((<!>)))
+-- >>> import Data.Functor.Apply(Apply((<.>)))
+-- >>> import Control.DeepSeq(rnf)
+-- >>> import Control.Category(id, (.))
+-- >>> import Data.Semigroupoid(Semigroupoid(o))
+-- >>> import Data.Profunctor(Profunctor(dimap), Strong(first'), Choice(left'))
+-- >>> import Control.Arrow(Arrow(arr, first), ArrowApply(app), ArrowChoice(left))
+-- >>> import Control.Selective(Selective(select))
+-- >>> import Data.Bifunctor(Bifunctor(bimap))
+-- >>> import Data.Bifoldable(Bifoldable(bifoldr))
+-- >>> import Data.Bitraversable(Bitraversable(bitraverse))
+-- >>> import Data.Bifunctor.Swap(Swap(swap))
+-- >>> import Data.Tagged(Tagged(..))
+-- >>> :set -XNoMonomorphismRestriction -w
+
+-- | A data type similar to @Data.Either@ that accumulates failures.
+module Data.Validation
+  ( -- * Data type
+    Validation (..),
+
+    -- * Catamorphism
+    foldValidation,
+
+    -- * Optics
+
+    -- ** Classy lenses (as generated by @makeClassy@)
+    GetValidation (..),
+    HasValidation (..),
+
+    -- ** Classy prisms (as generated by @makeClassyPrisms@)
+    ReviewValidation (..),
+    AsValidation (..),
+    GetFailure (..),
+    HasFailure (..),
+    ReviewFailure (..),
+    AsFailure (..),
+    GetSuccess (..),
+    HasSuccess (..),
+    ReviewSuccess (..),
+    AsSuccess (..),
+
+    -- ** Prisms
+    __Failure,
+    __Success,
+
+    -- ** Isomorphisms
+    Data.Validation.either,
+    codiagonal,
+
+    -- * Validator
+    Validator (..),
+    Validator',
+
+    -- ** Classy lenses
+    GetValidator (..),
+    HasValidator (..),
+
+    -- ** Classy prisms
+    ReviewValidator (..),
+    AsValidator (..),
+
+    -- ** Isomorphisms
+    unitValidator,
+    taggedValidator,
+    swapValidator,
+
+    -- ** Profunctor newtypes
+    Iso'' (..),
+    Prism'' (..),
+
+    -- ** Example validators
+    nonEmptyListIsoValidator',
+    nonEmptyListIsoValidator,
+    nonEmptyListPrismValidator',
+    nonEmptyListPrismValidator,
+  )
+where
+
+import Control.Arrow (Arrow (arr, first), ArrowApply (app), ArrowChoice (left, right))
+import Control.Category (Category (..), (>>>))
+import Control.DeepSeq (NFData (rnf))
+import Control.Lens (Getter, Lens', Prism, Prism', Review, Rewrapped, Wrapped (_Wrapped', type Unwrapped), prism, prism')
+import Control.Lens.Iso (Iso, iso
+#if !MIN_VERSION_lens(4,20,0)
+                       , Swapped(..))
+#else
+                       )
+#endif
+import Control.Selective (Selective (..))
+import Data.Bifoldable (Bifoldable (bifoldr))
+import Data.Bifunctor (Bifunctor (bimap))
+import Data.Bifunctor.Swap (Swap (..))
+import Data.Bitraversable (Bitraversable (bitraverse))
+import Data.Bool (bool)
+import Data.Data (Data)
+import qualified Data.Either as Either
+import Data.Functor.Alt (Alt ((<!>)))
+import Data.Functor.Apply (Apply ((<.>)))
+import Data.List.NonEmpty (NonEmpty (..), toList)
+import Data.Profunctor (Choice (left', right'), Profunctor (dimap, lmap, rmap), Strong (first', second'))
+import Data.Semigroupoid (Semigroupoid (o))
+import Data.Tagged (Tagged (..))
+import Data.Typeable (Typeable)
+import Data.Void (Void, absurd)
+import GHC.Generics (Generic)
+import Prelude hiding (either, id, (.))
+
+-- | A @Validation@ is either a value of the type @err@ or @a@, similar to 'Either'. However,
+-- the 'Applicative' instance for @Validation@ /accumulates/ errors using a 'Semigroup' on @err@.
+-- In contrast, the @Applicative@ for @Either@ returns only the first error.
+--
+-- A consequence of this is that @Validation@ has no 'Data.Functor.Bind.Bind' or 'Control.Monad.Monad' instance. This is because
+-- such an instance would violate the law that a Monad's 'Control.Monad.ap' must equal the
+-- @Applicative@'s 'Control.Applicative.<*>'
+--
+-- An example of typical usage can be found <https://github.com/qfpl/validation/blob/master/examples/src/Email.hs here>.
+data Validation err a
+  = Failure err
+  | Success a
+  deriving (Data, Eq, Generic, Ord, Show, Typeable)
+
+-- |
+-- >>> fmap (+1) (Success 2 :: Validation String Int)
+-- Success 3
+--
+-- >>> fmap (+1) (Failure "err" :: Validation String Int)
+-- Failure "err"
+instance Functor (Validation err) where
+  fmap _ (Failure e) =
+    Failure e
+  fmap f (Success a) =
+    Success (f a)
+  {-# INLINE fmap #-}
+
+-- | Accumulates errors on the left using 'Semigroup'.
+--
+-- >>> import Data.Functor.Apply(Apply((<.>)))
+-- >>> Success (+1) <.> Success 2 :: Validation [String] Int
+-- Success 3
+--
+-- >>> Failure ["e1"] <.> Success 2 :: Validation [String] Int
+-- Failure ["e1"]
+--
+-- >>> Success (+1) <.> Failure ["e2"] :: Validation [String] Int
+-- Failure ["e2"]
+--
+-- >>> Failure ["e1"] <.> Failure ["e2"] :: Validation [String] Int
+-- Failure ["e1","e2"]
+instance (Semigroup err) => Apply (Validation err) where
+  Failure e1 <.> b = Failure $ case b of
+    Failure e2 -> e1 <> e2
+    Success _ -> e1
+  Success _ <.> Failure e2 =
+    Failure e2
+  Success f <.> Success a =
+    Success (f a)
+  {-# INLINE (<.>) #-}
+
+-- | Delegates to the 'Apply' instance, accumulating errors with '<>'.
+--
+-- >>> pure (+1) <*> pure 2 :: Validation [String] Int
+-- Success 3
+--
+-- >>> Failure ["e1"] <*> Failure ["e2"] :: Validation [String] Int
+-- Failure ["e1","e2"]
+instance (Semigroup err) => Applicative (Validation err) where
+  pure =
+    Success
+  {-# INLINE pure #-}
+  (<*>) =
+    (<.>)
+  {-# INLINE (<*>) #-}
+
+-- | Tries the left, then the right, accumulating errors on two failures.
+--
+-- >>> import Data.Functor.Alt(Alt((<!>)))
+-- >>> Success 1 <!> Success 2 :: Validation [String] Int
+-- Success 1
+--
+-- >>> Failure ["e1"] <!> Success 2 :: Validation [String] Int
+-- Success 2
+--
+-- >>> Success 1 <!> Failure ["e2"] :: Validation [String] Int
+-- Success 1
+--
+-- >>> Failure ["e1"] <!> Failure ["e2"] :: Validation [String] Int
+-- Failure ["e1","e2"]
+instance (Semigroup err) => Alt (Validation err) where
+  Failure e1 <!> Failure e2 =
+    Failure (e1 <> e2)
+  Failure _ <!> Success a =
+    Success a
+  Success a <!> _ =
+    Success a
+  {-# INLINE (<!>) #-}
+
+-- | Skips the second effect on 'Failure'.
+--
+-- >>> import Control.Selective(Selective(select))
+-- >>> select (Success (Right 1)) (Success (+1)) :: Validation [String] Int
+-- Success 1
+--
+-- >>> select (Success (Left 1)) (Success (+1)) :: Validation [String] Int
+-- Success 2
+--
+-- >>> select (Failure ["e1"]) (Success (+1)) :: Validation [String] Int
+-- Failure ["e1"]
+--
+-- >>> select (Failure ["e1"]) (Failure ["e2"]) :: Validation [String] Int
+-- Failure ["e1"]
+instance (Semigroup err) => Selective (Validation err) where
+  select (Failure e) _ = Failure e
+  select (Success x) f = Either.either (\a -> ($ a) <$> f) Success x
+  {-# INLINE select #-}
+
+-- |
+-- >>> foldr (:) [] (Success 1 :: Validation String Int)
+-- [1]
+--
+-- >>> foldr (:) [] (Failure "err" :: Validation String Int)
+-- []
+instance Foldable (Validation err) where
+  foldr f x (Success a) =
+    f a x
+  foldr _ x (Failure _) =
+    x
+  {-# INLINE foldr #-}
+
+-- |
+-- >>> traverse (\x -> [x, x+1]) (Success 1 :: Validation String Int)
+-- [Success 1,Success 2]
+--
+-- >>> traverse (\x -> [x, x+1]) (Failure "err" :: Validation String Int)
+-- [Failure "err"]
+instance Traversable (Validation err) where
+  traverse f (Success a) =
+    Success <$> f a
+  traverse _ (Failure e) =
+    pure (Failure e)
+  {-# INLINE traverse #-}
+
+-- |
+-- >>> import Data.Bifunctor(Bifunctor(bimap))
+-- >>> bimap show (+1) (Failure 1 :: Validation Int Int)
+-- Failure "1"
+--
+-- >>> bimap show (+1) (Success 1 :: Validation Int Int)
+-- Success 2
+instance Bifunctor Validation where
+  bimap f _ (Failure e) =
+    Failure (f e)
+  bimap _ g (Success a) =
+    Success (g a)
+  {-# INLINE bimap #-}
+
+-- |
+-- >>> import Data.Bifoldable(Bifoldable(bifoldr))
+-- >>> bifoldr (\e r -> show e ++ r) (\a r -> show a ++ r) "" (Failure 1 :: Validation Int Int)
+-- "1"
+--
+-- >>> bifoldr (\e r -> show e ++ r) (\a r -> show a ++ r) "" (Success 2 :: Validation Int Int)
+-- "2"
+instance Bifoldable Validation where
+  bifoldr _ g x (Success a) =
+    g a x
+  bifoldr f _ x (Failure e) =
+    f e x
+  {-# INLINE bifoldr #-}
+
+-- |
+-- >>> import Data.Bitraversable(Bitraversable(bitraverse))
+-- >>> bitraverse (\e -> [e, e+1]) (\a -> [a, a*2]) (Failure 1 :: Validation Int Int)
+-- [Failure 1,Failure 2]
+--
+-- >>> bitraverse (\e -> [e, e+1]) (\a -> [a, a*2]) (Success 3 :: Validation Int Int)
+-- [Success 3,Success 6]
+instance Bitraversable Validation where
+  bitraverse _ g (Success a) =
+    Success <$> g a
+  bitraverse f _ (Failure e) =
+    Failure <$> f e
+  {-# INLINE bitraverse #-}
+
+-- | First 'Success' wins; two 'Failure's are combined with '<>'.
+--
+-- >>> Failure ["e1"] <> Failure ["e2"] :: Validation [String] Int
+-- Failure ["e1","e2"]
+--
+-- >>> Failure ["e1"] <> Success 2 :: Validation [String] Int
+-- Success 2
+--
+-- >>> Success 1 <> Failure ["e2"] :: Validation [String] Int
+-- Success 1
+--
+-- >>> Success 1 <> Success 2 :: Validation [String] Int
+-- Success 1
+instance (Semigroup e) => Semigroup (Validation e a) where
+  Failure e1 <> Failure e2 = Failure (e1 <> e2)
+  Failure _ <> Success a = Success a
+  Success a <> _ = Success a
+  {-# INLINE (<>) #-}
+
+-- |
+-- >>> mempty :: Validation [String] Int
+-- Failure []
+instance (Monoid e) => Monoid (Validation e a) where
+  mempty =
+    Failure mempty
+  {-# INLINE mempty #-}
+
+#if !MIN_VERSION_lens(4,20,0)
+instance Swapped Validation where
+  swapped = iso swap swap
+  {-# INLINE swapped #-}
+#endif
+
+-- |
+-- >>> import Data.Bifunctor.Swap(Swap(swap))
+-- >>> swap (Failure "err" :: Validation String Int)
+-- Success "err"
+--
+-- >>> swap (Success 1 :: Validation String Int)
+-- Failure 1
+instance Swap Validation where
+  swap v =
+    case v of
+      Failure e -> Success e
+      Success a -> Failure a
+  {-# INLINE swap #-}
+
+-- |
+-- >>> import Control.DeepSeq(rnf)
+-- >>> rnf (Success 1 :: Validation String Int)
+-- ()
+--
+-- >>> rnf (Failure "err" :: Validation String Int)
+-- ()
+instance (NFData e, NFData a) => NFData (Validation e a) where
+  rnf v =
+    case v of
+      Failure e -> rnf e
+      Success a -> rnf a
+  {-# INLINE rnf #-}
+
+-- | Catamorphism for 'Validation'.
+--
+-- >>> foldValidation show show (Failure 1 :: Validation Int Int)
+-- "1"
+--
+-- >>> foldValidation show show (Success 2 :: Validation Int Int)
+-- "2"
+foldValidation :: (a -> x) -> (b -> x) -> Validation a b -> x
+foldValidation f _ (Failure a) = f a
+foldValidation _ s (Success b) = s b
+{-# INLINE foldValidation #-}
+
+-- | Polymorphic 'Prism' targeting the 'Failure' constructor.
+--
+-- >>> import Control.Lens((^?), review)
+-- >>> review __Failure "err" :: Validation String Int
+-- Failure "err"
+--
+-- >>> (Failure "err" :: Validation String Int) ^? __Failure
+-- Just "err"
+--
+-- >>> (Success 1 :: Validation String Int) ^? __Failure
+-- Nothing
+__Failure :: Prism (Validation a b) (Validation a' b) a a'
+__Failure =
+  prism
+    Failure
+    ( \case
+        Failure a -> Right a
+        Success b -> Left (Success b)
+    )
+{-# INLINE __Failure #-}
+
+-- | Polymorphic 'Prism' targeting the 'Success' constructor.
+--
+-- >>> import Control.Lens((^?), review)
+-- >>> review __Success 1 :: Validation String Int
+-- Success 1
+--
+-- >>> (Success 1 :: Validation String Int) ^? __Success
+-- Just 1
+--
+-- >>> (Failure "err" :: Validation String Int) ^? __Success
+-- Nothing
+__Success :: Prism (Validation a b) (Validation a b') b b'
+__Success =
+  prism
+    Success
+    ( \case
+        Failure a -> Left (Failure a)
+        Success b -> Right b
+    )
+{-# INLINE __Success #-}
+
+-- | Isomorphism between 'Validation' and 'Either'.
+--
+-- >>> import Control.Lens(view)
+-- >>> view either (Failure "err" :: Validation String Int)
+-- Left "err"
+--
+-- >>> view either (Success 1 :: Validation String Int)
+-- Right 1
+either :: Iso (Validation a b) (Validation a' b') (Either a b) (Either a' b')
+either =
+  iso
+    (foldValidation Left Right)
+    (Either.either Failure Success)
+{-# INLINE either #-}
+
+-- | Isomorphism between @Validation a a@ and @(Bool, a)@, where 'False' corresponds to 'Failure'.
+--
+-- >>> import Control.Lens(view)
+-- >>> view codiagonal (Failure "x" :: Validation String String)
+-- (False,"x")
+--
+-- >>> view codiagonal (Success "x" :: Validation String String)
+-- (True,"x")
+codiagonal :: Iso (Validation a a) (Validation a' a') (Bool, a) (Bool, a')
+codiagonal =
+  iso
+    (foldValidation (False,) (True,))
+    (\(p, a) -> bool (Failure a) (Success a) p)
+{-# INLINE codiagonal #-}
+
+-- | Class for types that have a 'Getter' to a 'Validation'.
+class GetValidation s err a | s -> err a where
+  getValidation :: Getter s (Validation err a)
+
+instance GetValidation (Validation err a) err a where
+  getValidation = id
+  {-# INLINE getValidation #-}
+
+-- | Class for types that have a 'Lens'' to a 'Validation' (as generated by @makeClassy@).
+class (GetValidation s err a) => HasValidation s err a | s -> err a where
+  validation :: Lens' s (Validation err a)
+
+instance HasValidation (Validation err a) err a where
+  validation = id
+  {-# INLINE validation #-}
+
+-- | Class for types that have a 'Review' to a 'Validation'.
+class ReviewValidation s err a | s -> err a where
+  reviewValidation :: Review s (Validation err a)
+
+instance ReviewValidation (Validation err a) err a where
+  reviewValidation = id
+  {-# INLINE reviewValidation #-}
+
+-- | Class for types that have a 'Prism'' to a 'Validation' (as generated by @makeClassyPrisms@).
+class (ReviewValidation s err a) => AsValidation s err a | s -> err a where
+  _Validation :: Prism' s (Validation err a)
+
+instance AsValidation (Validation err a) err a where
+  _Validation = id
+  {-# INLINE _Validation #-}
+
+-- | Class for types that have a 'Getter' to an @err@.
+class GetFailure s err a | s -> err a where
+  getFailure :: Getter s err
+
+-- | Class for types that have a 'Lens'' to an @err@.
+class HasFailure s err a | s -> err a where
+  failure :: Lens' s err
+
+-- | Class for types that have a 'Review' to construct from an @err@.
+class ReviewFailure s err a | s -> err a where
+  reviewFailure :: Review s err
+
+-- |
+-- >>> import Control.Lens((#))
+-- >>> reviewFailure # "err" :: Validation String Int
+-- Failure "err"
+instance ReviewFailure (Validation err a) err a where
+  reviewFailure = __Failure
+  {-# INLINE reviewFailure #-}
+
+-- |
+-- >>> import Control.Lens((#))
+-- >>> reviewFailure # "err" :: Either String Int
+-- Left "err"
+instance ReviewFailure (Either a b) a b where
+  reviewFailure =
+    prism'
+      Left
+      ( \case
+          Left a -> Just a
+          Right _ -> Nothing
+      )
+  {-# INLINE reviewFailure #-}
+
+-- | Class for types that have a 'Prism'' to an @err@ (as generated by @makeClassyPrisms@).
+class (ReviewFailure s err a) => AsFailure s err a | s -> err a where
+  _Failure :: Prism' s err
+
+-- |
+-- >>> import Control.Lens((^?), (#))
+-- >>> _Failure # "err" :: Validation String Int
+-- Failure "err"
+--
+-- >>> (Failure "err" :: Validation String Int) ^? _Failure
+-- Just "err"
+--
+-- >>> (Success 1 :: Validation String Int) ^? _Failure
+-- Nothing
+instance AsFailure (Validation err a) err a where
+  _Failure = __Failure
+  {-# INLINE _Failure #-}
+
+-- |
+-- >>> import Control.Lens((^?), (#))
+-- >>> _Failure # "err" :: Either String Int
+-- Left "err"
+--
+-- >>> (Left "err" :: Either String Int) ^? _Failure
+-- Just "err"
+--
+-- >>> (Right 1 :: Either String Int) ^? _Failure
+-- Nothing
+instance AsFailure (Either a b) a b where
+  _Failure =
+    prism'
+      Left
+      ( \case
+          Left a -> Just a
+          Right _ -> Nothing
+      )
+  {-# INLINE _Failure #-}
+
+-- | Class for types that have a 'Getter' to an @a@.
+class GetSuccess s err a | s -> err a where
+  getSuccess :: Getter s a
+
+-- | Class for types that have a 'Lens'' to an @a@.
+class HasSuccess s err a | s -> err a where
+  success :: Lens' s a
+
+-- | Class for types that have a 'Review' to construct from an @a@.
+class ReviewSuccess s err a | s -> err a where
+  reviewSuccess :: Review s a
+
+-- |
+-- >>> import Control.Lens((#))
+-- >>> reviewSuccess # 1 :: Either String Int
+-- Right 1
+instance ReviewSuccess (Either a b) a b where
+  reviewSuccess =
+    prism'
+      Right
+      ( \case
+          Right a -> Just a
+          Left _ -> Nothing
+      )
+  {-# INLINE reviewSuccess #-}
+
+-- |
+-- >>> import Control.Lens((#))
+-- >>> reviewSuccess # 1 :: Validation String Int
+-- Success 1
+instance ReviewSuccess (Validation err a) err a where
+  reviewSuccess = __Success
+  {-# INLINE reviewSuccess #-}
+
+-- | Class for types that have a 'Prism'' to an @a@ (as generated by @makeClassyPrisms@).
+class (ReviewSuccess s err a) => AsSuccess s err a | s -> err a where
+  _Success :: Prism' s a
+
+-- |
+-- >>> import Control.Lens((^?), (#))
+-- >>> _Success # 1 :: Either String Int
+-- Right 1
+--
+-- >>> (Right 1 :: Either String Int) ^? _Success
+-- Just 1
+--
+-- >>> (Left "err" :: Either String Int) ^? _Success
+-- Nothing
+instance AsSuccess (Either a b) a b where
+  _Success =
+    prism'
+      Right
+      ( \case
+          Right a -> Just a
+          Left _ -> Nothing
+      )
+  {-# INLINE _Success #-}
+
+-- |
+-- >>> import Control.Lens((^?), (#))
+-- >>> _Success # 1 :: Validation String Int
+-- Success 1
+--
+-- >>> (Success 1 :: Validation String Int) ^? _Success
+-- Just 1
+--
+-- >>> (Failure "err" :: Validation String Int) ^? _Success
+-- Nothing
+instance AsSuccess (Validation err a) err a where
+  _Success = __Success
+  {-# INLINE _Success #-}
+
+-- | A @Validator@ is a profunctor transformer that wraps the output of @p@ in a 'Validation'.
+-- @Validator e (->) x a@ is isomorphic to @x -> Validation e a@.
+--
+-- The 'Semigroupoid' and 'Category' instances compose by short-circuiting on 'Failure'
+-- (like monadic bind, not accumulating). Use the 'Applicative' instance to accumulate
+-- errors in parallel.
+newtype Validator e p x a = Validator (p x (Validation e a))
+
+-- | @Validator@ specialised to @(->)@.
+type Validator' e x a = Validator e (->) x a
+
+-- |
+-- >>> import Control.Lens(view, _Wrapped')
+-- >>> view _Wrapped' (fmap (+1) (Validator (Success . (*2)) :: Validator' String Int Int)) 3
+-- Success 7
+--
+-- >>> view _Wrapped' (fmap (+1) (Validator (\_ -> Failure "err") :: Validator' String Int Int)) 3
+-- Failure "err"
+instance (Profunctor p) => Functor (Validator e p x) where
+  fmap f (Validator p) = Validator (rmap (fmap f) p)
+  {-# INLINE fmap #-}
+
+-- | Applies the accumulating 'Validation' '<.>' to both outputs, using 'Arrow' to fan out.
+--
+-- >>> import Control.Lens(view, _Wrapped')
+-- >>> import Data.Functor.Apply(Apply((<.>)))
+-- >>> let f = Validator (Success . (+1)) :: Validator' [String] Int Int
+-- >>> let g = Validator (Success . (*2)) :: Validator' [String] Int Int
+-- >>> view _Wrapped' (fmap (*) f <.> g) 3
+-- Success 24
+--
+-- >>> let f = Validator (\_ -> Failure ["e1"]) :: Validator' [String] Int Int
+-- >>> let g = Validator (\_ -> Failure ["e2"]) :: Validator' [String] Int Int
+-- >>> view _Wrapped' (fmap (*) f <.> g) 3
+-- Failure ["e1","e2"]
+instance (Profunctor p, Arrow p, Semigroup e) => Apply (Validator e p x) where
+  Validator f <.> Validator g = Validator (arr (uncurry (<.>)) . (f &&& g))
+    where
+      (&&&) a b = arr (\x -> (x, x)) >>> first a >>> arr (\(a', x) -> (a', x)) >>> swapFirst b
+      swapFirst h = arr (\(c, x) -> (x, c)) >>> first h >>> arr (\(b', c) -> (c, b'))
+  {-# INLINE (<.>) #-}
+
+-- | 'pure' lifts a value into a successful 'Validator'. '<*>' accumulates errors.
+--
+-- >>> import Control.Lens(view, _Wrapped')
+-- >>> view _Wrapped' (pure 1 :: Validator' String Int Int) 99
+-- Success 1
+--
+-- >>> let f = Validator (Success . (+1)) :: Validator' [String] Int Int
+-- >>> let g = Validator (Success . (*2)) :: Validator' [String] Int Int
+-- >>> view _Wrapped' ((*) <$> f <*> g) 3
+-- Success 24
+--
+-- >>> let f = Validator (\_ -> Failure ["e1"]) :: Validator' [String] Int Int
+-- >>> let g = Validator (\_ -> Failure ["e2"]) :: Validator' [String] Int Int
+-- >>> view _Wrapped' ((*) <$> f <*> g) 3
+-- Failure ["e1","e2"]
+instance (Profunctor p, Arrow p, Semigroup e) => Applicative (Validator e p x) where
+  pure a = Validator (arr (\_ -> Success a))
+  {-# INLINE pure #-}
+  (<*>) = (<.>)
+  {-# INLINE (<*>) #-}
+
+-- | Tries the left, then the right, accumulating errors on two failures.
+--
+-- >>> import Control.Lens(view, _Wrapped')
+-- >>> import Data.Functor.Alt(Alt((<!>)))
+-- >>> let f = Validator (\_ -> Failure ["e1"]) :: Validator' [String] Int Int
+-- >>> let g = Validator (Success . (*2)) :: Validator' [String] Int Int
+-- >>> view _Wrapped' (f <!> g) 3
+-- Success 6
+--
+-- >>> let f = Validator (\_ -> Failure ["e1"]) :: Validator' [String] Int Int
+-- >>> let g = Validator (\_ -> Failure ["e2"]) :: Validator' [String] Int Int
+-- >>> view _Wrapped' (f <!> g) 3
+-- Failure ["e1","e2"]
+--
+-- >>> let f = Validator (Success . (+1)) :: Validator' [String] Int Int
+-- >>> let g = Validator (Success . (*2)) :: Validator' [String] Int Int
+-- >>> view _Wrapped' (f <!> g) 3
+-- Success 4
+instance (Profunctor p, Arrow p, Semigroup e) => Alt (Validator e p x) where
+  Validator f <!> Validator g = Validator (arr (uncurry (<!>)) . (f &&& g))
+    where
+      (&&&) a b = arr (\x -> (x, x)) >>> first a >>> arr (\(a', x) -> (a', x)) >>> swapFirst b
+      swapFirst h = arr (\(c, x) -> (x, c)) >>> first h >>> arr (\(b', c) -> (c, b'))
+  {-# INLINE (<!>) #-}
+
+-- | Skips the second effect on 'Failure'.
+--
+-- >>> import Control.Lens(view, _Wrapped')
+-- >>> import Control.Selective(Selective(select))
+-- >>> let v = Validator (\x -> Success (if even x then Right x else Left x)) :: Validator' [String] Int (Either Int Int)
+-- >>> let f = Validator (\_ -> Success (+10)) :: Validator' [String] Int (Int -> Int)
+-- >>> view _Wrapped' (select v f) 4
+-- Success 4
+--
+-- >>> view _Wrapped' (select v f) 3
+-- Success 13
+instance (Profunctor p, Arrow p, Semigroup e) => Selective (Validator e p x) where
+  select (Validator c) (Validator f) = Validator (arr go . (c &&& f))
+    where
+      go (vc, vf) = case vc of
+        Failure e -> Failure e
+        Success x -> Either.either (\a -> fmap ($ a) vf) Success x
+      (&&&) a b = arr (\x -> (x, x)) >>> first a >>> arr (\(a', x) -> (a', x)) >>> swapFirst b
+      swapFirst h = arr (\(c', x) -> (x, c')) >>> first h >>> arr (\(b', c') -> (c', b'))
+  {-# INLINE select #-}
+
+-- |
+-- >>> import Control.Lens(view, _Wrapped')
+-- >>> import Data.Profunctor(Profunctor(dimap))
+-- >>> view _Wrapped' (dimap (+1) (*2) (Validator (Success . (+10)) :: Validator' String Int Int)) 3
+-- Success 28
+instance (Profunctor p) => Profunctor (Validator e p) where
+  dimap f g (Validator p) = Validator (dimap f (fmap g) p)
+  {-# INLINE dimap #-}
+  lmap f (Validator p) = Validator (lmap f p)
+  {-# INLINE lmap #-}
+  rmap f (Validator p) = Validator (rmap (fmap f) p)
+  {-# INLINE rmap #-}
+
+-- |
+-- >>> import Control.Lens(view, _Wrapped')
+-- >>> import Data.Profunctor(Strong(first'))
+-- >>> view _Wrapped' (first' (Validator (Success . (+1)) :: Validator' String Int Int)) (3, "x")
+-- Success (4,"x")
+--
+-- >>> view _Wrapped' (first' (Validator (\_ -> Failure "err") :: Validator' String Int Int)) (3, "x")
+-- Failure "err"
+instance (Strong p) => Strong (Validator e p) where
+  first' (Validator p) = Validator (rmap (\(v, c) -> fmap (,c) v) (first' p))
+  {-# INLINE first' #-}
+  second' (Validator p) = Validator (rmap (\(c, v) -> fmap (c,) v) (second' p))
+  {-# INLINE second' #-}
+
+-- |
+-- >>> import Control.Lens(view, _Wrapped')
+-- >>> import Data.Profunctor(Choice(left'))
+-- >>> view _Wrapped' (left' (Validator (Success . (+1)) :: Validator' String Int Int)) (Left 3)
+-- Success (Left 4)
+--
+-- >>> view _Wrapped' (left' (Validator (Success . (+1)) :: Validator' String Int Int)) (Right "x")
+-- Success (Right "x")
+--
+-- >>> view _Wrapped' (left' (Validator (\_ -> Failure "err") :: Validator' String Int Int)) (Left 3)
+-- Failure "err"
+instance (Choice p) => Choice (Validator e p) where
+  left' (Validator p) = Validator (rmap (Either.either (fmap Left) (Success . Right)) (left' p))
+  {-# INLINE left' #-}
+  right' (Validator p) = Validator (rmap (Either.either (Success . Left) (fmap Right)) (right' p))
+  {-# INLINE right' #-}
+
+-- | Composes by short-circuiting on 'Failure' (does not accumulate errors).
+--
+-- >>> import Control.Lens(view, _Wrapped')
+-- >>> import Data.Semigroupoid(Semigroupoid(o))
+-- >>> let f = Validator (Success . (+1)) :: Validator' String Int Int
+-- >>> let g = Validator (Success . (*2)) :: Validator' String Int Int
+-- >>> view _Wrapped' (g `o` f) 3
+-- Success 8
+--
+-- >>> let f = Validator (\_ -> Failure "e1") :: Validator' String Int Int
+-- >>> let g = Validator (Success . (*2)) :: Validator' String Int Int
+-- >>> view _Wrapped' (g `o` f) 3
+-- Failure "e1"
+instance (Choice p, Semigroupoid p) => Semigroupoid (Validator e p) where
+  Validator g `o` Validator f =
+    Validator (rmap (Either.either Failure id) (right' g `o` rmap (foldValidation Left Right) f))
+  {-# INLINE o #-}
+
+-- |
+-- >>> import Prelude hiding (id)
+-- >>> import Control.Lens(view, _Wrapped')
+-- >>> import Control.Category(id)
+-- >>> view _Wrapped' (id :: Validator' String Int Int) 3
+-- Success 3
+instance (Choice p, Category p) => Category (Validator e p) where
+  id = Validator (rmap Success id)
+  {-# INLINE id #-}
+  Validator g . Validator f =
+    Validator (rmap (Either.either Failure id) (right' g . rmap (foldValidation Left Right) f))
+  {-# INLINE (.) #-}
+
+-- |
+-- >>> import Control.Lens(view, _Wrapped')
+-- >>> import Control.Arrow(Arrow(arr, first))
+-- >>> view _Wrapped' (arr (+1) :: Validator' String Int Int) 3
+-- Success 4
+--
+-- >>> view _Wrapped' (first (Validator (Success . (+1)) :: Validator' String Int Int)) (3, "x")
+-- Success (4,"x")
+instance (ArrowChoice p, Choice p) => Arrow (Validator e p) where
+  arr f = Validator (arr (Success . f))
+  {-# INLINE arr #-}
+  first (Validator f) = Validator (arr (\(v, c) -> fmap (,c) v) . first f)
+  {-# INLINE first #-}
+
+-- |
+-- >>> import Control.Lens(view, _Wrapped')
+-- >>> import Control.Arrow(ArrowApply(app))
+-- >>> view _Wrapped' (app :: Validator' String (Validator' String Int Int, Int) Int) (Validator (Success . (+1)), 3)
+-- Success 4
+instance (ArrowApply p, ArrowChoice p, Choice p) => ArrowApply (Validator e p) where
+  app = Validator (arr (\(Validator f, x) -> (f, x)) >>> app)
+  {-# INLINE app #-}
+
+-- |
+-- >>> import Control.Lens(view, _Wrapped')
+-- >>> import Control.Arrow(ArrowChoice(left))
+-- >>> view _Wrapped' (left (Validator (Success . (+1)) :: Validator' String Int Int)) (Left 3 :: Either Int String)
+-- Success (Left 4)
+--
+-- >>> view _Wrapped' (left (Validator (Success . (+1)) :: Validator' String Int Int)) (Right "x" :: Either Int String)
+-- Success (Right "x")
+--
+-- >>> view _Wrapped' (left (Validator (\_ -> Failure "err") :: Validator' String Int Int)) (Left 3 :: Either Int String)
+-- Failure "err"
+instance (ArrowChoice p, Choice p) => ArrowChoice (Validator e p) where
+  left (Validator f) = Validator (arr (Either.either (fmap Left) (Success . Right)) . left f)
+  {-# INLINE left #-}
+  right (Validator f) = Validator (arr (Either.either (Success . Left) (fmap Right)) . right f)
+  {-# INLINE right #-}
+
+-- |
+-- >>> import Control.Lens(view, from, _Wrapped')
+-- >>> view _Wrapped' (Validator Success :: Validator' String Int Int) 1
+-- Success 1
+--
+-- >>> view _Wrapped' (view (from _Wrapped') (Success . (+1)) :: Validator' String Int Int) 3
+-- Success 4
+instance Wrapped (Validator e p x a) where
+  type Unwrapped (Validator e p x a) = p x (Validation e a)
+  _Wrapped' = iso (\(Validator p) -> p) Validator
+  {-# INLINE _Wrapped' #-}
+
+instance Rewrapped (Validator e p x a) (Validator e' p' x' a')
+
+-- | Class for types that have a 'Getter' to a 'Validator'.
+class GetValidator s e p x a | s -> e p x a where
+  getValidator :: Getter s (Validator e p x a)
+
+instance GetValidator (Validator e p x a) e p x a where
+  getValidator = id
+  {-# INLINE getValidator #-}
+
+-- | Class for types that have a 'Lens'' to a 'Validator' (as generated by @makeClassy@).
+class (GetValidator s e p x a) => HasValidator s e p x a | s -> e p x a where
+  validator :: Lens' s (Validator e p x a)
+
+instance HasValidator (Validator e p x a) e p x a where
+  validator = id
+  {-# INLINE validator #-}
+
+-- | Class for types that have a 'Review' to a 'Validator'.
+class ReviewValidator s e p x a | s -> e p x a where
+  reviewValidator :: Review s (Validator e p x a)
+
+instance ReviewValidator (Validator e p x a) e p x a where
+  reviewValidator = id
+  {-# INLINE reviewValidator #-}
+
+-- | Class for types that have a 'Prism'' to a 'Validator' (as generated by @makeClassyPrisms@).
+class (ReviewValidator s e p x a) => AsValidator s e p x a | s -> e p x a where
+  _Validator :: Prism' s (Validator e p x a)
+
+instance AsValidator (Validator e p x a) e p x a where
+  _Validator = id
+  {-# INLINE _Validator #-}
+
+-- | Isomorphism between @Validator' e () a@ and @Validation e a@, unwrapping by applying to @()@.
+--
+-- >>> import Control.Lens(view, from, _Wrapped')
+-- >>> view unitValidator (Validator (\() -> Success 1) :: Validator' String () Int)
+-- Success 1
+--
+-- >>> view _Wrapped' (view (from unitValidator) (Failure "err" :: Validation String Int)) ()
+-- Failure "err"
+unitValidator :: Iso (Validator' e () a) (Validator' e' () a') (Validation e a) (Validation e' a')
+unitValidator =
+  iso
+    (\(Validator k) -> k ())
+    (Validator . pure)
+{-# INLINE unitValidator #-}
+
+-- | Isomorphism between @Validator e Tagged x a@ and @Validation e a@, unwrapping via 'Tagged'.
+--
+-- >>> import Control.Lens(view)
+-- >>> import Data.Tagged(Tagged(..))
+-- >>> view taggedValidator (Validator (Tagged (Success 1)) :: Validator String Tagged Int Int)
+-- Success 1
+--
+-- >>> view taggedValidator (Validator (Tagged (Failure "err")) :: Validator String Tagged Int Int)
+-- Failure "err"
+taggedValidator :: Iso (Validator e Tagged x a) (Validator e' Tagged x a') (Validation e a) (Validation e' a')
+taggedValidator =
+  iso
+    (\(Validator k) -> unTagged k)
+    (Validator . Tagged)
+{-# INLINE taggedValidator #-}
+
+-- | Isomorphism that swaps the error and success types of a 'Validator' by 'swap'-ping the inner 'Validation'.
+--
+-- >>> import Control.Lens(view, _Wrapped')
+-- >>> let v = Validator (Success . (+1)) :: Validator' String Int Int
+-- >>> view _Wrapped' (view swapValidator v) 3
+-- Failure 4
+--
+-- >>> let w = Validator (\_ -> Failure "err") :: Validator' String Int Int
+-- >>> view _Wrapped' (view swapValidator w) 3
+-- Success "err"
+swapValidator :: (Profunctor p, Profunctor p') => Iso (Validator e p x a) (Validator e' p' x' a') (Validator a p x e) (Validator a' p' x' e')
+swapValidator =
+  iso
+    (\(Validator p) -> Validator (rmap swap p))
+    (\(Validator p) -> Validator (rmap swap p))
+{-# INLINE swapValidator #-}
+
+-- | A newtype wrapping a monomorphic 'Iso' as a two-parameter profunctor.
+-- This allows 'Iso'' to be used as the @p@ parameter in 'Validator'.
+newtype Iso'' a b = Iso'' (Iso a a b b)
+
+-- | An 'Iso' between a list and a @Validation () (NonEmpty a)@.
+-- The empty list maps to @Failure ()@ and a non-empty list maps to @Success@.
+--
+-- >>> import Control.Lens(view, review)
+-- >>> import Data.List.NonEmpty(NonEmpty(..))
+-- >>> view nonEmptyListIsoValidator' [1, 2, 3 :: Int]
+-- Success (1 :| [2,3])
+--
+-- >>> view nonEmptyListIsoValidator' ([] :: [Int])
+-- Failure ()
+--
+-- >>> review nonEmptyListIsoValidator' (Success (1 :| [2, 3]))
+-- [1,2,3]
+--
+-- >>> review nonEmptyListIsoValidator' (Failure ())
+-- []
+nonEmptyListIsoValidator' ::
+  Iso
+    [a]
+    [a']
+    (Validation () (NonEmpty a))
+    (Validation () (NonEmpty a'))
+nonEmptyListIsoValidator' =
+  iso
+    ( \case
+        [] -> Failure ()
+        h : t -> Success (h :| t)
+    )
+    (foldValidation (\() -> []) toList)
+{-# INLINE nonEmptyListIsoValidator' #-}
+
+-- | A 'Validator' using 'Iso''' that validates a list is non-empty.
+-- The empty list maps to @Failure ()@.
+--
+-- >>> import Control.Lens(view, review)
+-- >>> import Data.List.NonEmpty(NonEmpty(..))
+-- >>> let Validator (Iso'' i) = nonEmptyListIsoValidator
+-- >>> view i [1, 2, 3 :: Int]
+-- Success (1 :| [2,3])
+--
+-- >>> let Validator (Iso'' i) = nonEmptyListIsoValidator
+-- >>> view i ([] :: [Int])
+-- Failure ()
+--
+-- >>> let Validator (Iso'' i) = nonEmptyListIsoValidator
+-- >>> review i (Success (1 :| [2, 3]))
+-- [1,2,3]
+--
+-- >>> let Validator (Iso'' i) = nonEmptyListIsoValidator
+-- >>> review i (Failure ())
+-- []
+nonEmptyListIsoValidator :: Validator () Iso'' [a] (NonEmpty a)
+nonEmptyListIsoValidator =
+  Validator
+    ( Iso''
+        nonEmptyListIsoValidator'
+    )
+
+-- | A newtype wrapping a monomorphic 'Prism' as a two-parameter profunctor.
+-- This allows 'Prism''' to be used as the @p@ parameter in 'Validator'.
+newtype Prism'' a b = Prism'' (Prism a a b b)
+
+-- | A 'Prism' from a list to a @Validation Void (NonEmpty a)@.
+-- The empty list does not match (yields 'Nothing'); a non-empty list matches as @Success@.
+--
+-- >>> import Control.Lens((^?), review)
+-- >>> import Data.List.NonEmpty(NonEmpty(..))
+-- >>> [1, 2, 3 :: Int] ^? nonEmptyListPrismValidator'
+-- Just (Success (1 :| [2,3]))
+--
+-- >>> ([] :: [Int]) ^? nonEmptyListPrismValidator'
+-- Nothing
+--
+-- >>> review nonEmptyListPrismValidator' (Success (1 :| [2, 3]))
+-- [1,2,3]
+nonEmptyListPrismValidator' :: Prism [a] [a] (Validation err (NonEmpty a)) (Validation Void (NonEmpty a))
+nonEmptyListPrismValidator' =
+  prism'
+    (foldValidation absurd toList)
+    ( \case
+        [] -> Nothing
+        h : t -> Just (Success (h :| t))
+    )
+{-# INLINE nonEmptyListPrismValidator' #-}
+
+-- | A 'Validator' using 'Prism''' that validates a list is non-empty.
+-- Uses 'Void' as the error type since the 'Prism' encodes partiality via 'Nothing'.
+--
+-- >>> import Control.Lens((^?), review)
+-- >>> import Data.List.NonEmpty(NonEmpty(..))
+-- >>> let Validator (Prism'' p) = nonEmptyListPrismValidator
+-- >>> [1, 2, 3 :: Int] ^? p
+-- Just (Success (1 :| [2,3]))
+--
+-- >>> let Validator (Prism'' p) = nonEmptyListPrismValidator
+-- >>> ([] :: [Int]) ^? p
+-- Nothing
+--
+-- >>> let Validator (Prism'' p) = nonEmptyListPrismValidator
+-- >>> review p (Success (1 :| [2, 3]))
+-- [1,2,3]
+nonEmptyListPrismValidator :: Validator Void Prism'' [a] (NonEmpty a)
+nonEmptyListPrismValidator =
+  Validator
+    (Prism'' nonEmptyListPrismValidator')
diff --git a/test/doctest_tests.hs b/test/doctest_tests.hs
new file mode 100644
--- /dev/null
+++ b/test/doctest_tests.hs
@@ -0,0 +1,17 @@
+import System.Exit (ExitCode (..), exitFailure)
+import System.Process (rawSystem)
+
+main :: IO ()
+main = do
+  exit <-
+    rawSystem
+      "cabal"
+      [ "exec",
+        "--",
+        "doctest",
+        "-isrc",
+        "src/Data/Validation.hs"
+      ]
+  case exit of
+    ExitSuccess -> pure ()
+    ExitFailure _ -> exitFailure
diff --git a/test/hedgehog_tests.hs b/test/hedgehog_tests.hs
--- a/test/hedgehog_tests.hs
+++ b/test/hedgehog_tests.hs
@@ -1,48 +1,125 @@
 {-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE ScopedTypeVariables #-}
 
 import Control.Applicative (liftA3)
+import Control.Category (id, (.))
+import Control.Lens (Wrapped (_Wrapped'), from, review, view, (#), (^.), (^?))
 import Control.Monad (join, unless)
+import Data.Bifunctor (bimap)
+import Data.Bifunctor.Swap (swap)
+import Data.Functor.Alt (Alt ((<!>)))
+import Data.Functor.Apply (Apply ((<.>)))
+import Data.Semigroupoid (Semigroupoid (o))
+import Data.Validation
 import Hedgehog
 import qualified Hedgehog.Gen as Gen
 import qualified Hedgehog.Range as Range
-import System.IO (BufferMode(..), hSetBuffering, stdout, stderr)
 import System.Exit (exitFailure)
-
-import Data.Validation (Validation (Success, Failure))
+import System.IO (BufferMode (..), hSetBuffering, stderr, stdout)
+import Prelude hiding (either, id, (.))
+import qualified Prelude
 
 main :: IO ()
 main = do
   hSetBuffering stdout LineBuffering
   hSetBuffering stderr LineBuffering
 
-  result <- checkParallel $ Group "Validation"
-    [ ("prop_semigroup", prop_semigroup)
-    , ("prop_monoid_assoc", prop_monoid_assoc)
-    , ("prop_monoid_left_id", prop_monoid_left_id)
-    , ("prop_monoid_right_id", prop_monoid_right_id)
-    ]
+  result <-
+    checkParallel $
+      Group
+        "Validation"
+        [ ("prop_semigroup_assoc", prop_semigroup_assoc),
+          ("prop_monoid_assoc", prop_monoid_assoc),
+          ("prop_monoid_left_id", prop_monoid_left_id),
+          ("prop_monoid_right_id", prop_monoid_right_id),
+          ("prop_functor_id", prop_functor_id),
+          ("prop_functor_compose", prop_functor_compose),
+          ("prop_applicative_id", prop_applicative_id),
+          ("prop_applicative_homomorphism", prop_applicative_homomorphism),
+          ("prop_apply_compose", prop_apply_compose),
+          ("prop_alt_assoc", prop_alt_assoc),
+          ("prop_alt_left_catch", prop_alt_left_catch),
+          ("prop_bifunctor_id", prop_bifunctor_id),
+          ("prop_bifunctor_compose", prop_bifunctor_compose),
+          ("prop_foldValidation_failure", prop_foldValidation_failure),
+          ("prop_foldValidation_success", prop_foldValidation_success),
+          ("prop_either_roundtrip", prop_either_roundtrip),
+          ("prop_either_roundtrip_inv", prop_either_roundtrip_inv),
+          ("prop_codiagonal_roundtrip", prop_codiagonal_roundtrip),
+          ("prop_failure_prism_review_preview", prop_failure_prism_review_preview),
+          ("prop_success_prism_review_preview", prop_success_prism_review_preview),
+          ("prop_failure_prism_miss", prop_failure_prism_miss),
+          ("prop_success_prism_miss", prop_success_prism_miss),
+          ("prop_poly_failure_prism", prop_poly_failure_prism),
+          ("prop_poly_success_prism", prop_poly_success_prism),
+          ("prop_swap_failure", prop_swap_failure),
+          ("prop_swap_success", prop_swap_success),
+          ("prop_swap_involution", prop_swap_involution),
+          ("prop_validator_functor_id", prop_validator_functor_id),
+          ("prop_validator_category_left_id", prop_validator_category_left_id),
+          ("prop_validator_category_right_id", prop_validator_category_right_id),
+          ("prop_validator_category_assoc", prop_validator_category_assoc),
+          ("prop_validator_semigroupoid_assoc", prop_validator_semigroupoid_assoc),
+          ("prop_validator_apply_accumulates", prop_validator_apply_accumulates),
+          ("prop_validator_alt_accumulates", prop_validator_alt_accumulates),
+          ("prop_validator_alt_left_success", prop_validator_alt_left_success),
+          ("prop_either_reviewFailure", prop_either_reviewFailure),
+          ("prop_either_asFailure_hit", prop_either_asFailure_hit),
+          ("prop_either_asFailure_miss", prop_either_asFailure_miss),
+          ("prop_either_reviewSuccess", prop_either_reviewSuccess),
+          ("prop_either_asSuccess_hit", prop_either_asSuccess_hit),
+          ("prop_either_asSuccess_miss", prop_either_asSuccess_miss),
+          ("prop_either_failure_roundtrip", prop_either_failure_roundtrip),
+          ("prop_either_success_roundtrip", prop_either_success_roundtrip)
+        ]
 
-  unless result $
-    exitFailure
+  unless result exitFailure
 
+-- Generators
+
 genValidation :: Gen e -> Gen a -> Gen (Validation e a)
 genValidation e a = Gen.choice [fmap Failure e, fmap Success a]
 
+genInt :: Gen Int
+genInt = Gen.int (Range.linear (-100) 100)
+
+genString :: Gen String
+genString = Gen.string (Range.linear 0 50) Gen.unicode
+
+genStrings :: Gen [String]
+genStrings = Gen.list (Range.linear 1 10) genString
+
 testGen :: Gen (Validation [String] Int)
-testGen =
-  let range = Range.linear 1 50
-      string = Gen.string range Gen.unicode
-      strings = Gen.list range string
-  in  genValidation strings Gen.enumBounded
+testGen = genValidation genStrings genInt
 
+runV :: Validator' e x a -> x -> Validation e a
+runV = view _Wrapped'
+
+validators :: [Validator' [String] Int Int]
+validators =
+  [ Validator (Success . (+ 1)),
+    Validator (Success . (* 2)),
+    Validator (Success . negate),
+    Validator (\_ -> Failure ["e1"]),
+    Validator (\_ -> Failure ["e2"])
+  ]
+
+genValidatorIdx :: Gen Int
+genValidatorIdx = Gen.int (Range.constant 0 (length validators - 1))
+
+pickValidator :: Int -> Validator' [String] Int Int
+pickValidator i = validators !! i
+
+-- Semigroup / Monoid
+
 mkAssoc :: (Validation [String] Int -> Validation [String] Int -> Validation [String] Int) -> Property
 mkAssoc f =
   let g = forAll testGen
-      assoc = \x y z -> ((x `f` y) `f` z) === (x `f` (y `f` z))
-  in  property $ join (liftA3 assoc g g g)
+      assoc x y z = ((x `f` y) `f` z) === (x `f` (y `f` z))
+   in property $ join (liftA3 assoc g g g)
 
-prop_semigroup :: Property
-prop_semigroup = mkAssoc (<>)
+prop_semigroup_assoc :: Property
+prop_semigroup_assoc = mkAssoc (<>)
 
 prop_monoid_assoc :: Property
 prop_monoid_assoc = mkAssoc mappend
@@ -59,3 +136,318 @@
     x <- forAll testGen
     (x `mappend` mempty) === x
 
+-- Functor
+
+prop_functor_id :: Property
+prop_functor_id =
+  property $ do
+    x <- forAll testGen
+    fmap Prelude.id x === x
+
+prop_functor_compose :: Property
+prop_functor_compose =
+  property $ do
+    x <- forAll testGen
+    let f = (+ 1)
+        g = (* 2)
+    fmap (f Prelude.. g) x === fmap f (fmap g x)
+
+-- Applicative / Apply
+
+prop_applicative_id :: Property
+prop_applicative_id =
+  property $ do
+    x <- forAll testGen
+    (pure Prelude.id <*> x) === x
+
+prop_applicative_homomorphism :: Property
+prop_applicative_homomorphism =
+  property $ do
+    x <- forAll genInt
+    let f = (+ 1)
+    (pure f <*> pure x :: Validation [String] Int) === pure (f x)
+
+prop_apply_compose :: Property
+prop_apply_compose =
+  property $ do
+    w <- forAll testGen
+    let u = Success (+ 1) :: Validation [String] (Int -> Int)
+        v = Success (* 2) :: Validation [String] (Int -> Int)
+    (fmap (Prelude..) u <.> v <.> w) === (u <.> (v <.> w))
+
+-- Alt
+
+prop_alt_assoc :: Property
+prop_alt_assoc =
+  property $ do
+    x <- forAll testGen
+    y <- forAll testGen
+    z <- forAll testGen
+    ((x <!> y) <!> z) === (x <!> (y <!> z))
+
+prop_alt_left_catch :: Property
+prop_alt_left_catch =
+  property $ do
+    x <- forAll genInt
+    y <- forAll testGen
+    (Success x <!> y) === (Success x :: Validation [String] Int)
+
+-- Bifunctor
+
+prop_bifunctor_id :: Property
+prop_bifunctor_id =
+  property $ do
+    x <- forAll testGen
+    bimap Prelude.id Prelude.id x === x
+
+prop_bifunctor_compose :: Property
+prop_bifunctor_compose =
+  property $ do
+    x <- forAll testGen
+    let f = (++ ["x"])
+        g = (+ 1)
+        h = (++ ["y"])
+        k = (* 2)
+    bimap (f Prelude.. h) (g Prelude.. k) x === bimap f g (bimap h k x)
+
+-- foldValidation
+
+prop_foldValidation_failure :: Property
+prop_foldValidation_failure =
+  property $ do
+    e <- forAll genStrings
+    foldValidation length (const 0) (Failure e :: Validation [String] Int) === length e
+
+prop_foldValidation_success :: Property
+prop_foldValidation_success =
+  property $ do
+    a <- forAll genInt
+    foldValidation (const 0) (+ 1) (Success a :: Validation [String] Int) === (a + 1)
+
+-- Iso: either
+
+prop_either_roundtrip :: Property
+prop_either_roundtrip =
+  property $ do
+    x <- forAll testGen
+    (x ^. either ^. from either) === x
+
+prop_either_roundtrip_inv :: Property
+prop_either_roundtrip_inv =
+  property $ do
+    x <- forAll testGen
+    let e = x ^. either :: Prelude.Either [String] Int
+    (e ^. from either) === x
+
+-- Iso: codiagonal
+
+prop_codiagonal_roundtrip :: Property
+prop_codiagonal_roundtrip =
+  property $ do
+    x <- forAll (genValidation genInt genInt)
+    (x ^. codiagonal ^. from codiagonal) === x
+
+-- Prisms
+
+prop_failure_prism_review_preview :: Property
+prop_failure_prism_review_preview =
+  property $ do
+    e <- forAll genStrings
+    let v = review _Failure e :: Validation [String] Int
+    v ^? _Failure === Just e
+
+prop_success_prism_review_preview :: Property
+prop_success_prism_review_preview =
+  property $ do
+    a <- forAll genInt
+    let v = review _Success a :: Validation [String] Int
+    v ^? _Success === Just a
+
+prop_failure_prism_miss :: Property
+prop_failure_prism_miss =
+  property $ do
+    a <- forAll genInt
+    (Success a :: Validation [String] Int) ^? _Failure === Nothing
+
+prop_success_prism_miss :: Property
+prop_success_prism_miss =
+  property $ do
+    e <- forAll genStrings
+    (Failure e :: Validation [String] Int) ^? _Success === Nothing
+
+-- Polymorphic prisms
+
+prop_poly_failure_prism :: Property
+prop_poly_failure_prism =
+  property $ do
+    e <- forAll genStrings
+    let v = __Failure # e :: Validation [String] Int
+    v ^? __Failure === Just e
+
+prop_poly_success_prism :: Property
+prop_poly_success_prism =
+  property $ do
+    a <- forAll genInt
+    let v = __Success # a :: Validation [String] Int
+    v ^? __Success === Just a
+
+-- Swap
+
+prop_swap_failure :: Property
+prop_swap_failure =
+  property $ do
+    e <- forAll genString
+    let v = Failure e :: Validation String Int
+    swap v === (Success e :: Validation Int String)
+
+prop_swap_success :: Property
+prop_swap_success =
+  property $ do
+    a <- forAll genInt
+    let v = Success a :: Validation String Int
+    swap v === (Failure a :: Validation Int String)
+
+prop_swap_involution :: Property
+prop_swap_involution =
+  property $ do
+    x <- forAll testGen
+    (swap (swap x)) === x
+
+-- Validator: Functor
+
+prop_validator_functor_id :: Property
+prop_validator_functor_id =
+  property $ do
+    x <- forAll genInt
+    i <- forAll genValidatorIdx
+    let v = pickValidator i
+    runV (fmap Prelude.id v) x === runV v x
+
+-- Validator: Category
+
+prop_validator_category_left_id :: Property
+prop_validator_category_left_id =
+  property $ do
+    x <- forAll genInt
+    i <- forAll genValidatorIdx
+    let v = pickValidator i
+    runV (id . v) x === runV v x
+
+prop_validator_category_right_id :: Property
+prop_validator_category_right_id =
+  property $ do
+    x <- forAll genInt
+    i <- forAll genValidatorIdx
+    let v = pickValidator i
+    runV (v . id) x === runV v x
+
+prop_validator_category_assoc :: Property
+prop_validator_category_assoc =
+  property $ do
+    x <- forAll genInt
+    fi <- forAll genValidatorIdx
+    gi <- forAll genValidatorIdx
+    hi <- forAll genValidatorIdx
+    let f = pickValidator fi
+        g = pickValidator gi
+        h = pickValidator hi
+    runV ((f . g) . h) x === runV (f . (g . h)) x
+
+-- Validator: Semigroupoid
+
+prop_validator_semigroupoid_assoc :: Property
+prop_validator_semigroupoid_assoc =
+  property $ do
+    x <- forAll genInt
+    fi <- forAll genValidatorIdx
+    gi <- forAll genValidatorIdx
+    hi <- forAll genValidatorIdx
+    let f = pickValidator fi
+        g = pickValidator gi
+        h = pickValidator hi
+    runV ((f `o` g) `o` h) x === runV (f `o` (g `o` h)) x
+
+-- Validator: Apply / Alt accumulate errors
+
+prop_validator_apply_accumulates :: Property
+prop_validator_apply_accumulates =
+  property $ do
+    x <- forAll genInt
+    let f = Validator (\_ -> Failure ["e1"]) :: Validator' [String] Int Int
+        g = Validator (\_ -> Failure ["e2"]) :: Validator' [String] Int Int
+    runV (fmap const f <.> g) x === Failure ["e1", "e2"]
+
+prop_validator_alt_accumulates :: Property
+prop_validator_alt_accumulates =
+  property $ do
+    x <- forAll genInt
+    let f = Validator (\_ -> Failure ["e1"]) :: Validator' [String] Int Int
+        g = Validator (\_ -> Failure ["e2"]) :: Validator' [String] Int Int
+    runV (f <!> g) x === Failure ["e1", "e2"]
+
+prop_validator_alt_left_success :: Property
+prop_validator_alt_left_success =
+  property $ do
+    x <- forAll genInt
+    let f = Validator (Success . (+ 1)) :: Validator' [String] Int Int
+        g = Validator (\_ -> Failure ["e2"]) :: Validator' [String] Int Int
+    runV (f <!> g) x === Success (x + 1)
+
+-- Either instances: ReviewFailure, AsFailure, ReviewSuccess, AsSuccess
+
+genEither :: Gen a -> Gen b -> Gen (Prelude.Either a b)
+genEither ga gb = Gen.choice [fmap Left ga, fmap Right gb]
+
+prop_either_reviewFailure :: Property
+prop_either_reviewFailure =
+  property $ do
+    e <- forAll genStrings
+    (reviewFailure # e :: Prelude.Either [String] Int) === Left e
+
+prop_either_asFailure_hit :: Property
+prop_either_asFailure_hit =
+  property $ do
+    e <- forAll genStrings
+    (Left e :: Prelude.Either [String] Int) ^? _Failure === Just e
+
+prop_either_asFailure_miss :: Property
+prop_either_asFailure_miss =
+  property $ do
+    a <- forAll genInt
+    (Right a :: Prelude.Either [String] Int) ^? _Failure === Nothing
+
+prop_either_reviewSuccess :: Property
+prop_either_reviewSuccess =
+  property $ do
+    a <- forAll genInt
+    (reviewSuccess # a :: Prelude.Either [String] Int) === Right a
+
+prop_either_asSuccess_hit :: Property
+prop_either_asSuccess_hit =
+  property $ do
+    a <- forAll genInt
+    (Right a :: Prelude.Either [String] Int) ^? _Success === Just a
+
+prop_either_asSuccess_miss :: Property
+prop_either_asSuccess_miss =
+  property $ do
+    e <- forAll genStrings
+    (Left e :: Prelude.Either [String] Int) ^? _Success === Nothing
+
+prop_either_failure_roundtrip :: Property
+prop_either_failure_roundtrip =
+  property $ do
+    x <- forAll (genEither genStrings genInt)
+    let reviewed = x ^? _Failure
+    case x of
+      Left e -> reviewed === Just e
+      Right _ -> reviewed === Nothing
+
+prop_either_success_roundtrip :: Property
+prop_either_success_roundtrip =
+  property $ do
+    x <- forAll (genEither genStrings genInt)
+    let reviewed = x ^? _Success
+    case x of
+      Right a -> reviewed === Just a
+      Left _ -> reviewed === Nothing
diff --git a/test/hunit_tests.hs b/test/hunit_tests.hs
deleted file mode 100644
--- a/test/hunit_tests.hs
+++ /dev/null
@@ -1,147 +0,0 @@
-{-# LANGUAGE ScopedTypeVariables #-}
-
-module Main (main) where
-
-import Test.HUnit
-
-import Prelude hiding (length)
-import Control.Lens ((#))
-import Control.Monad (when)
-import Data.Foldable (length)
-import Data.Proxy (Proxy (Proxy))
-import Data.Validation (Validation (Success, Failure), Validate, _Failure, _Success, ensure,
-                        orElse, validate, validation, validationNel)
-import System.Exit (exitFailure)
-
-seven :: Int
-seven = 7
-
-three :: Int
-three = 3
-
-four :: Int
-four = 4
-
-testYY :: Test
-testYY =
-  let subject  = _Success # (+1) <*> _Success # seven :: Validation String Int
-      expected = Success 8
-  in  TestCase (assertEqual "Success <*> Success" subject expected)
-
-testNY :: Test
-testNY =
-  let subject  = _Failure # ["f1"] <*> _Success # seven :: Validation [String] Int
-      expected = Failure ["f1"]
-  in  TestCase (assertEqual "Failure <*> Success" subject expected)
-
-testYN :: Test
-testYN =
-  let subject  = _Success # (+1) <*> _Failure # ["f2"] :: Validation [String] Int
-      expected = Failure ["f2"]
-  in  TestCase (assertEqual "Success <*> Failure" subject expected)
-
-testNN :: Test
-testNN =
-  let subject  = _Failure # ["f1"] <*> _Failure # ["f2"] :: Validation [String] Int
-      expected = Failure ["f1","f2"]
-  in  TestCase (assertEqual "Failure <*> Failure" subject expected)
-
-testValidationNel :: Test
-testValidationNel =
-  let subject  = validation length (const 0) $ validationNel (Left ())
-  in  TestCase (assertEqual "validationNel makes lists of length 1" subject 1)
-
-testEnsureLeftNothing, testEnsureLeftJust, testEnsureRightNothing,
- testEnsureRightJust, testEnsureRightJust', testOrElseRight, testOrElseLeft
-  :: forall v. (Validate v, Eq (v Int Int), Show (v Int Int)) => Proxy v -> Test
-
-testEnsureLeftNothing _ =
-  let subject :: v Int Int
-      subject = ensure three (const Nothing) (_Failure # seven)
-  in  TestCase (assertEqual "ensure Left False" subject (_Failure # seven))
-
-testEnsureLeftJust _ =
-  let subject :: v Int Int
-      subject = ensure three (Just . id) (_Failure # seven)
-  in  TestCase (assertEqual "ensure Left True" subject (_Failure # seven))
-
-testEnsureRightNothing _ =
-  let subject :: v Int Int
-      subject = ensure three (const Nothing) (_Success # seven)
-  in  TestCase (assertEqual "ensure Right False" subject (_Failure # three))
-
-testEnsureRightJust _ =
-  let subject :: v Int Int
-      subject = ensure three (Just . id) (_Success # seven)
-  in  TestCase (assertEqual "ensure Right True" subject (_Success # seven))
-
-testEnsureRightJust' _ =
-  let subject :: v Int Int
-      subject = ensure three (const $ Just four) (_Success # seven)
-  in  TestCase (assertEqual "ensure Right True" subject (_Success # four))
-
-testOrElseRight _ =
-  let v :: v Int Int
-      v = _Success # seven
-      subject = v `orElse` three
-  in  TestCase (assertEqual "orElseRight" subject seven)
-
-testOrElseLeft _ =
-  let v :: v Int Int
-      v = _Failure # seven
-      subject = v `orElse` three
-  in  TestCase (assertEqual "orElseLeft" subject three)
-
-testValidateJust :: Test
-testValidateJust =
-  let subject = validate three (Just . id) seven
-      expected = Success seven
-  in  TestCase (assertEqual "testValidateTrue" subject expected)
-
-testValidateJust' :: Test
-testValidateJust' =
-  let subject = validate three (const $ Just four) seven
-      expected = Success four
-  in  TestCase (assertEqual "testValidateTrue" subject expected)
-
-testValidateNothing :: Test
-testValidateNothing =
-  let subject = validate three (const option) seven
-      expected = Failure three
-      option = Nothing :: Maybe Int
-  in  TestCase (assertEqual "testValidateFalse" subject expected)
-
-tests :: Test
-tests =
-  let eitherP :: Proxy Either
-      eitherP = Proxy
-      validationP :: Proxy Validation
-      validationP = Proxy
-      generals :: forall v. (Validate v, Eq (v Int Int), Show (v Int Int)) => [Proxy v -> Test]
-      generals =
-        [ testEnsureLeftNothing
-        , testEnsureLeftJust
-        , testEnsureRightNothing
-        , testEnsureRightJust
-        , testEnsureRightJust' 
-        , testOrElseLeft
-        , testOrElseRight
-        ]
-      eithers = fmap ($ eitherP) generals
-      validations = fmap ($ validationP) generals
-  in  TestList $ [
-    testYY
-  , testYN
-  , testNY
-  , testNN
-  , testValidationNel
-  , testValidateNothing
-  , testValidateJust
-  , testValidateJust'
-  ] ++ eithers ++ validations
-  where
-
-main :: IO ()
-main = do
-  c <- runTestTT tests
-  when (errors c > 0 || failures c > 0) exitFailure
diff --git a/validation.cabal b/validation.cabal
--- a/validation.cabal
+++ b/validation.cabal
@@ -1,5 +1,5 @@
 name:               validation
-version:            1.1.5
+version:            1.2.2
 license:            BSD3
 license-file:       LICENCE
 author:             Tony Morris <ʇǝu˙sıɹɹoɯʇ@ןןǝʞsɐɥ> <dibblego>, Nick Partridge <nkpart>
@@ -13,30 +13,53 @@
 description:
   <<https://logo.systemf.com.au/systemf-450x450.png>>
   .
-  A data-type like Either but with differing properties and type-class
-  instances.
-  .
-  Library support is provided for this different representation, include
-  `lens`-related functions for converting between each and abstracting over their
-  similarities.
+  A data type like @Either@ but with an accumulating @Applicative@ instance.
   .
-  * `Validation`
+  == @Validation@
   .
-  The `Validation` data type is isomorphic to `Either`, but has an instance
-  of `Applicative` that accumulates on the error side. That is to say, if two
-  (or more) errors are encountered, they are appended using a `Semigroup`
+  The @Validation@ data type is isomorphic to @Either@, but has an instance
+  of @Applicative@ that accumulates on the error side. That is to say, if two
+  (or more) errors are encountered, they are appended using a @Semigroup@
   operation.
   .
-  As a consequence of this `Applicative` instance, there is no corresponding
-  `Bind` or `Monad` instance. `Validation` is an example of, "An applicative
+  As a consequence of this @Applicative@ instance, there is no corresponding
+  @Bind@ or @Monad@ instance. @Validation@ is an example of, "An applicative
   functor that is not a monad."
+  .
+  The library provides:
+  .
+  * Classy optics (@GetValidation@, @HasValidation@, @ReviewValidation@,
+    @AsValidation@, and corresponding classes for @Failure@ and @Success@)
+    following the conventions of @makeClassy@ and @makeClassyPrisms@ from @lens@.
+  * Polymorphic prisms (@__Failure@, @__Success@) for type-changing operations.
+  * Isomorphisms to @Either@ and @(Bool, a)@.
+  .
+  == @Validator@
+  .
+  The @Validator@ newtype is a profunctor transformer:
+  .
+  @newtype Validator e p x a = Validator (p x (Validation e a))@
+  .
+  @Validator e (->) x a@ is isomorphic to @x -> Validation e a@. The profunctor
+  parameter @p@ generalises this to other optic-like contexts such as @Tagged@,
+  @Iso@, and @Prism@.
+  .
+  Instances include @Functor@, @Apply@, @Applicative@, @Alt@, @Selective@,
+  @Profunctor@, @Strong@, @Choice@, @Semigroupoid@, @Category@, @Arrow@,
+  @ArrowApply@, @ArrowChoice@, and @Wrapped@.
+  .
+  The @Applicative@ instance accumulates errors in parallel (using @Semigroup@),
+  while @Category@ composition short-circuits on @Failure@ (like monadic bind).
+  .
+  The library also provides profunctor newtype wrappers (@Iso''@, @Prism''@) that
+  allow @Validator@ to be parameterised over monomorphic isos and prisms.
 
 homepage:           https://github.com/system-f/validation
 bug-reports:        https://github.com/system-f/validation/issues
 cabal-version:      >= 1.10
 build-type:         Simple
 extra-source-files: changelog
-tested-with:        GHC==9.0.1, GHC==8.10.4, GHC==8.8.4, GHC==8.6.5, GHC==8.4.4
+tested-with:        GHC == 9.10.3, GHC == 9.8.4, GHC == 9.6.7, GHC==9.0.1, GHC==8.10.4, GHC==8.8.4, GHC==8.6.5, GHC==8.4.4
 
 source-repository   head
   type:             git
@@ -55,6 +78,8 @@
                     , semigroupoids >= 5.2.2  && < 7
                     , bifunctors    >= 5.5    && < 6
                     , lens          >= 4.0.5  && < 6
+                    , profunctors   >= 5      && < 6
+                    , tagged        >= 0.8    && < 1
 
   ghc-options:
                     -Wall
@@ -76,9 +101,13 @@
                     Haskell2010
 
   build-depends:
-                      base       >= 4.11   && < 5
-                    , hedgehog   >= 0.5    && < 2
-                    , semigroups >= 0.18.2 && < 1
+                      base         >= 4.11   && < 5
+                    , assoc        >= 1      && < 2
+                    , bifunctors   >= 5.5    && < 6
+                    , hedgehog     >= 0.5    && < 2
+                    , lens         >= 4.0.5  && < 6
+                    , semigroupoids >= 5.2.2 && < 7
+                    , semigroups   >= 0.18.2 && < 1
                     , validation
 
   ghc-options:
@@ -88,21 +117,19 @@
   hs-source-dirs:
                     test
 
-test-suite hunit
+test-suite doctest
   type:
                     exitcode-stdio-1.0
 
   main-is:
-                    hunit_tests.hs
+                    doctest_tests.hs
 
   default-language:
                     Haskell2010
 
   build-depends:
                       base       >= 4.11   && < 5
-                    , HUnit      >= 1.6    && < 1.7
-                    , lens       >= 4.0.5  && < 6
-                    , semigroups >= 0.18.2 && < 1
+                    , process    >= 1.6    && < 2
                     , validation
 
   ghc-options:
