{-# LANGUAGE NoImplicitPrelude #-}
-- |
-- Module:       $HEADER$
-- Description:  Utilities for Endo data type.
-- Copyright:    (c) 2013-2015, Peter Trško
-- License:      BSD3
--
-- Maintainer:   peter.trsko@gmail.com
-- Stability:    experimental
-- Portability:  NoImplicitPrelude
--
-- Utilities for 'Endo' data type from "Data.Monoid" module.
module Data.Monoid.Endo
    (
    -- * Endo
      E
    , Endo(..)
    , runEndo
    , mapEndo
    , mapEndo2
    , liftEndo

    -- * Lens
    , endo
    )
    where

import Data.Functor (Functor(fmap))
import Data.Monoid (Endo(..))

import Data.Function.Between ((~@~), (<~@~))


-- | Type synonym for endomorphsm; it can be used to simplify type signatures.
type E a = a -> a

-- | Transform function wrapped in 'Endo'.
mapEndo :: (E a -> E b) -> Endo a -> Endo b
mapEndo = Endo ~@~ appEndo
{-# INLINE mapEndo #-}

-- | Variation of 'mapEndo' for functions with arity two.
mapEndo2 :: (E a -> E b -> E c) -> Endo a -> Endo b -> Endo c
mapEndo2 = mapEndo ~@~ appEndo
{-# INLINE mapEndo2 #-}

-- | Apply 'fmap' to function wrapped in 'Endo'. It's a short hand for
-- @'mapEndo' 'fmap'@.
liftEndo :: Functor f => Endo a -> Endo (f a)
liftEndo (Endo f) = Endo (fmap f)
{-# INLINE liftEndo #-}

-- | Flipped version of 'appEndo'.
runEndo :: a -> Endo a -> a
runEndo x (Endo f) = f x
{-# INLINE runEndo #-}

-- | Lens for 'Endo'. In terms of /lens/ package it would have type:
--
-- @
-- 'endo' :: Lens ('Endo' a) ('Endo' b) ('E' a) ('E' b)
-- @
--
-- For details see <http://hackage.haskell.org/package/lens lens package>.
endo :: Functor f => (E a -> f (E b)) -> Endo a -> f (Endo b)
endo = Endo <~@~ appEndo
{-# INLINE endo #-}