content: {-# LANGUAGE GADTs #-}
  {-# LANGUAGE TypeFamilies #-}
  {-# LANGUAGE MultiParamTypeClasses #-}
  {-# LANGUAGE Rank2Types #-}
  {-# LANGUAGE UndecidableInstances #-}
  {-# LANGUAGE Trustworthy #-}
  -----------------------------------------------------------------------------
  -- |
  -- Copyright   :  (C) 2013-2016 Edward Kmett
  -- License     :  BSD-style (see the file LICENSE)
  --
  -- Maintainer  :  Edward Kmett <ekmett@gmail.com>
  -- Stability   :  provisional
  -- Portability :  GADTs, TFs, MPTCs
  --
  ----------------------------------------------------------------------------
  module Data.Functor.Contravariant.Yoneda
    ( Yoneda(..)
    , liftYoneda, lowerYoneda
    ) where
  
  import Data.Functor.Contravariant
  import Data.Functor.Contravariant.Adjunction
  import Data.Functor.Contravariant.Rep
  
  -- | Yoneda embedding for a presheaf
  newtype Yoneda f a = Yoneda { runYoneda :: forall r. (r -> a) -> f r }
  
  -- |
  --
  -- @
  -- 'liftYoneda' . 'lowerYoneda' ≡ 'id'
  -- 'lowerYoneda' . 'liftYoneda' ≡ 'id'
  -- @
  
  liftYoneda :: Contravariant f => f a -> Yoneda f a
  liftYoneda fa = Yoneda $ \ra -> contramap ra fa
  {-# INLINE liftYoneda #-}
  
  lowerYoneda :: Yoneda f a -> f a
  lowerYoneda f = runYoneda f id
  {-# INLINE lowerYoneda #-}
  
  instance Contravariant (Yoneda f) where
    contramap ab (Yoneda m) = Yoneda (m . fmap ab)
    {-# INLINE contramap #-}
  
  instance Representable f => Representable (Yoneda f) where
    type Rep (Yoneda f) = Rep f
    tabulate = liftYoneda . tabulate
    {-# INLINE tabulate #-}
    index m a = index (lowerYoneda m) a
    {-# INLINE index #-}
    contramapWithRep beav = liftYoneda . contramapWithRep beav . lowerYoneda
    {-# INLINE contramapWithRep #-}
  
  instance Adjunction f g => Adjunction (Yoneda f) (Yoneda g) where
    leftAdjunct f = liftYoneda . leftAdjunct (lowerYoneda . f)
    {-# INLINE leftAdjunct #-}
    rightAdjunct f = liftYoneda . rightAdjunct (lowerYoneda . f)
    {-# INLINE rightAdjunct #-}
  