    {-# LANGUAGE CPP #-}
    {-# LANGUAGE TypeFamilies #-}
    {-# LANGUAGE FlexibleContexts #-}
    {-# LANGUAGE FlexibleInstances #-}
    {-# LANGUAGE UndecidableInstances #-}
    {-# LANGUAGE MultiParamTypeClasses #-}
    {-# LANGUAGE FunctionalDependencies #-}
    #ifdef TRUSTWORTHY
    {-# LANGUAGE Trustworthy #-}
    #endif
    -----------------------------------------------------------------------------
    -- |
    -- Module      :  Control.Lens.Internal.Review
    -- Copyright   :  (C) 2012-2013 Edward Kmett
    -- License     :  BSD-style (see the file LICENSE)
    -- Maintainer  :  Edward Kmett <ekmett@gmail.com>
    -- Stability   :  provisional
    -- Portability :  non-portable
    --
    ----------------------------------------------------------------------------
    module Control.Lens.Internal.Review
      (
      -- * Internal Classes
        Reviewable,
      -- * Reviews
        retagged,
        Reviewed(..)
      ) where

    import Control.Applicative
    import Control.Comonad
    import Control.Monad.Fix
    import Data.Bifoldable
    import Data.Bifunctor
    import Data.Bitraversable
    import Data.Distributive
    import Data.Foldable
    import Data.Functor.Bind
    import Data.Profunctor
    import Data.Profunctor.Rep
    import Data.Profunctor.Unsafe
    import Data.Proxy
    import Data.Traversable
    import Data.Void
    #ifndef SAFE
    import Unsafe.Coerce
    #endif

    -- | This class is provided mostly for backwards compatibility with lens 3.8,
    -- but it can also shorten type signatures.
    class (Profunctor p, Bifunctor p) => Reviewable p
    instance (Profunctor p, Bifunctor p) => Reviewable p

    ------------------------------------------------------------------------------
    -- Review: Reviewed
    ------------------------------------------------------------------------------

    -- | This is a profunctor used internally to implement "Review"
    --
    -- It plays a role similar to that of 'Control.Lens.Internal.Getter.Accessor'
    -- or 'Const' do for "Control.Lens.Getter"
    retagged :: (Profunctor p, Bifunctor p) => p a b -> p s b
    retagged = first absurd . lmap absurd

    newtype Reviewed a b = Reviewed { runReviewed :: b }

    instance Functor (Reviewed a) where
      fmap bc (Reviewed b) = Reviewed (bc b)
      {-# INLINE fmap #-}

    instance Apply (Reviewed a) where
      (<.>) a = Reviewed #. runReviewed a .# runReviewed
      {-# INLINE (<.>) #-}
      a <. _ = a
      {-# INLINE (<.) #-}
      _ .> b = b
      {-# INLINE (.>) #-}

    instance Applicative (Reviewed a) where
      pure = Reviewed
      (<*>) a = Reviewed #. runReviewed a .# runReviewed
      {-# INLINE (<*>) #-}
      a <* _ = a
      {-# INLINE (<*) #-}
      _ *> b = b
      {-# INLINE (*>) #-}

    instance Comonad (Reviewed a) where
      extract = runReviewed
      {-# INLINE extract #-}
      duplicate = Reviewed
      {-# INLINE duplicate #-}
      extend = ( #. ) Reviewed
      {-# INLINE extend #-}

    instance ComonadApply (Reviewed a) where
      (<@>) a = Reviewed #. runReviewed a .# runReviewed
      {-# INLINE (<@>) #-}
      a <@ _ = a
      {-# INLINE (<@) #-}
      _ @> b = b
      {-# INLINE (@>) #-}

    instance Bind (Reviewed a) where
      Reviewed a >>- f = f a
      {-# INLINE (>>-) #-}

    instance Monad (Reviewed a) where
      return = Reviewed
      {-# INLINE return #-}
      Reviewed a >>= f = f a
      {-# INLINE (>>=) #-}
      _ >> a = a
      {-# INLINE (>>) #-}

    instance MonadFix (Reviewed a) where
      mfix f = a where a = f (runReviewed a)
      {-# INLINE mfix #-}

    instance Foldable (Reviewed a) where
      foldMap f (Reviewed b) = f b
      {-# INLINE foldMap #-}

    instance Traversable (Reviewed a) where
      traverse f (Reviewed b) = Reviewed <$> f b
      {-# INLINE traverse #-}

    instance Bifunctor Reviewed where
      bimap _ g (Reviewed b) = Reviewed (g b)
      {-# INLINE bimap #-}

    instance Bifoldable Reviewed where
      bifoldMap _ g (Reviewed b) = g b
      {-# INLINE bifoldMap #-}

    instance Bitraversable Reviewed where
      bitraverse _ g (Reviewed b) = Reviewed <$> g b
      {-# INLINE bitraverse #-}

    instance Distributive (Reviewed a) where
      distribute = Reviewed . fmap runReviewed
      {-# INLINE distribute #-}

    instance Profunctor Reviewed where
      dimap _ f (Reviewed c) = Reviewed (f c)
      {-# INLINE dimap #-}
      lmap _ (Reviewed c) = Reviewed c
      {-# INLINE lmap #-}
      rmap = fmap
      {-# INLINE rmap #-}
      Reviewed b .# _ = Reviewed b
      {-# INLINE ( .# ) #-}
    #ifndef SAFE
      ( #. ) _ = unsafeCoerce
      {-# INLINE ( #. ) #-}
    #endif

    instance Choice Reviewed where
      left' (Reviewed b) = Reviewed (Left b)
      {-# INLINE left' #-}
      right' (Reviewed b) = Reviewed (Right b)
      {-# INLINE right' #-}

    instance Corepresentable Reviewed where
      type Corep Reviewed = Proxy
      cotabulate f = Reviewed (f Proxy)
      {-# INLINE cotabulate #-}
      corep (Reviewed b) Proxy = b
      {-# INLINE corep #-}