    -- | This module is provided for Haskell 98 compatibility.
    -- If you are able to use @Rank2Types@, I advise you to instead use the rank 2 aliases
    --
    -- * @Lens@, @Lens'@
    --
    -- * @Traversal@, @Traversal'@
    --
    -- * @Setter@, @Setter'@
    --
    -- * @Fold@, @Fold'@
    --
    -- * @Getter@, @Getter'@
    --
    -- from the @lens-family@ package instead.
    --
    -- 'cloneLens' allows one to circumvent the need for rank 2 types by allowing one to take a universal monomorphic lens instance and rederive a polymorphic instance.
    -- When you require a lens family parameter you use the type @'ALens' a a' b b'@ (or @'ALens'' a b@).
    -- Then, inside a @where@ clause, you use 'cloneLens' to create a 'Lens' type.
    --
    -- For example.
    --
    -- > example :: ALens a a' b b' -> Example
    -- > example l = ... x^.cl ... cl .~ y ...
    -- >  where
    -- >   cl x = cloneLens l x
    --
    -- /Note/: It is important to eta-expand the definition of 'cl' to avoid the dreaded monomorphism restriction.
    --
    -- 'cloneTraversal', 'cloneGetter', 'cloneSetter', and 'cloneFold' provides similar functionality for traversals, getters, setters, and folds respectively.
    --
    -- /Note/: Cloning is only need if you use a functional reference multiple times with different instances.
    module Lens.Family.Clone
      ( cloneLens, cloneTraversal, cloneSetter, cloneGetter, cloneFold
      -- * Types
      , ALens, ALens'
      , ATraversal, ATraversal'
      , AGetter, AGetter'
      , AFold, AFold'
      , IStore, IKleeneStore
      -- * Re-exports
      , LensLike, LensLike', FoldLike, FoldLike', ASetter
      , Applicative, Phantom, Identical
      ) where

    import Control.Applicative (Applicative, pure, (<*>), (<$>))
    import Lens.Family.Unchecked (Identical, setting)
    import Lens.Family ( LensLike, LensLike'
                       , ASetter, over
                       , FoldLike, FoldLike', toListOf, folding
                       , to, view
                       , Phantom
                       )

    data IStore b b' a = IStore (b' -> a) b
    instance Functor (IStore b b') where
      fmap f (IStore g b) = IStore (f . g) b

    -- | ALens a a' b b' is a universal Lens a a' b b' instance
    type ALens a a' b b' = LensLike (IStore b b') a a' b b'

    -- | ALens' a b is a universal Lens' a b instance
    type ALens' a b = LensLike' (IStore b b) a b

    -- | Converts a universal lens instance back into a polymorphic lens.
    cloneLens :: Functor f => ALens a a' b b' -> LensLike f a a' b b'
    cloneLens univ f = experiment f . univ (IStore id)

    experiment :: Functor f => (b -> f b') -> IStore b b' a -> f a
    experiment f (IStore g b) = g <$> f b

    data IKleeneStore b b' a = Unit a
                             | Battery (IKleeneStore b b' (b' -> a)) b

    instance Functor (IKleeneStore b b') where
      fmap f (Unit a) = Unit (f a)
      fmap f (Battery g b) = Battery (fmap (f .) g) b

    instance Applicative (IKleeneStore b b') where
      pure = Unit
      Unit f <*> a = fmap f a
      Battery f b <*> a = Battery (flip <$> f <*> a) b

    -- | ATraversal a a' b b' is a universal Traversal a a' b b' instance
    type ATraversal a a' b b' = LensLike (IKleeneStore b b') a a' b b'

    -- | ATraversal' a b is a universal Traversal' a b instance
    type ATraversal' a b = LensLike' (IKleeneStore b b) a b

    -- | Converts a universal traversal instance back into a polymorphic traversal.
    cloneTraversal :: Applicative f => ATraversal a a' b b' -> LensLike f a a' b b'
    cloneTraversal univ f = research f . univ (Battery (Unit id))

    research :: Applicative f => (b -> f b') -> IKleeneStore b b' a -> f a
    research _ (Unit a) = pure a
    research f (Battery g b) = research f g <*> f b

    -- | Converts a universal setter instance back into a polymorphic setter.
    cloneSetter :: Identical f => ASetter a a' b b' -> LensLike f a a' b b'
    cloneSetter = setting . over

    -- | AFold a a' b b' is a universal Fold' a a' b b' instance
    type AFold a a' b b' = FoldLike [b] a a' b b'

    -- | AFold' a b is a universal Fold' a b instance
    type AFold' a b = FoldLike' [b] a b

    -- | Converts a universal fold instance back into a polymorphic fold.
    cloneFold :: (Phantom f, Applicative f) => AFold a a' b b' -> LensLike f a a' b b'
    cloneFold univ = folding (toListOf univ)

    -- | AGetter a a' b b' is a universal Fold a a' b b' instance
    type AGetter a a' b b' = FoldLike b a a' b b'

    -- | AGetter' a b is a universal Fold' a b instance
    type AGetter' a b = FoldLike' b a b

    -- | Converts a universal getter instance back into a polymorphic getter.
    cloneGetter :: Phantom f => AGetter a a' b b' -> LensLike f a a' b b'
    cloneGetter univ = to (view univ)