    {-# LANGUAGE CPP #-}

    -- | Utilities for functors.

    module Agda.Utils.Functor
      ( module Agda.Utils.Functor
      , (<$>)  -- from Data.Functor
      , ($>)   -- from Data.Functor
    #if MIN_VERSION_base(4,11,0)
      , (<&>)  -- from Data.Functor
    #endif
      )
      where

    import Control.Applicative ( Const(Const), getConst )

    import Data.Functor
    import Data.Functor.Identity
    import Data.Functor.Compose
    import Data.Functor.Classes

    infixr 9 <.>

    -- | Composition: pure function after functorial (monadic) function.
    (<.>) :: Functor m => (b -> c) -> (a -> m b) -> a -> m c
    (f <.> g) a = f <$> g a

    -- | The true pure @for@ loop.
    --   'Data.Traversable.for' is a misnomer, it should be @forA@.
    for :: Functor m => m a -> (a -> b) -> m b
    for = flip fmap

    #if !MIN_VERSION_base(4,11,0)
    infixl 1 <&>

    -- | Infix version of 'for'.
    (<&>) :: Functor m => m a -> (a -> b) -> m b
    (<&>) = for
    #endif

    -- | A decoration is a functor that is traversable into any functor.
    --
    --   The 'Functor' superclass is given because of the limitations
    --   of the Haskell class system.
    --   @traverseF@ actually implies functoriality.
    --
    --   Minimal complete definition: @traverseF@ or @distributeF@.
    class Functor t => Decoration t where

      -- | @traverseF@ is the defining property.
      traverseF :: Functor m => (a -> m b) -> t a -> m (t b)
      traverseF f = distributeF . fmap f

      -- | Decorations commute into any functor.
      distributeF :: (Functor m) => t (m a) -> m (t a)
      distributeF = traverseF id

    -- | Any decoration is traversable with @traverse = traverseF@.
    --   Just like any 'Traversable' is a functor, so is
    --   any decoration, given by just @traverseF@, a functor.
    dmap :: Decoration t => (a -> b) -> t a -> t b
    dmap f = runIdentity . traverseF (Identity . f)

    -- | Any decoration is a lens.  @set@ is a special case of @dmap@.
    dget :: Decoration t => t a -> a
    dget = getConst . traverseF Const

    -- | The identity functor is a decoration.
    instance Decoration Identity where
      traverseF f (Identity x) = Identity <$> f x

    -- | Decorations compose.  (Thus, they form a category.)
    instance (Decoration d, Decoration t) => Decoration (Compose d t) where
      -- traverseF . traverseF :: Functor m => (a -> m b) -> d (t a) -> m (d (t a))
      traverseF f (Compose x) = Compose <$> traverseF (traverseF f) x

    -- Not a decoration are:
    --
    -- * The constant functor.
    -- * Maybe.  Can only be traversed into pointed functors.
    -- * Other disjoint sum types, like lists etc.
    --   (Can only be traversed into Applicative.)

    -- | A typical decoration is pairing with some stuff.
    instance Decoration ((,) a) where
      traverseF f (a, x) = (a,) <$> f x