{-# LANGUAGE NoImplicitPrelude #-}

module Precursor.Function
 ( const
 , fix
 , flip
 , on
 , ($)
 , (&)
 , applyN
 , converge
 , (.:)
 ) where

import           Precursor.Data.Bool
import           Precursor.Control.Category
import           Data.Function hiding ((.))
import           Precursor.Algebra.Eq
import           Precursor.Numeric.Num
import           Precursor.Algebra.Ord
import           Precursor.Algebra.Ring

-- $setup
-- >>> import Precursor.Algebra.Semiring
-- >>> import Precursor.Data.List
-- >>> import Test.QuickCheck

-- | >>> applyN (2+) 2 0
-- 4
applyN :: (a -> a) -> Int -> a -> a
applyN f = go . max 0 where
  go 0 x = x
  go n x = go (n-1) (f x)

-- | Apply a function until it no longer changes its input.
--
-- prop> converge tail xs === []
converge :: Eq a => (a -> a) -> a -> a
converge f = r where
  r x | x == y = y
      | otherwise = r y
      where y = f x

infixr 8 .:
-- | \"Blackbird\" operator. For example:
--
-- @aggregate f xs = sum (map f xs)@
--
-- @aggregate = sum .: map@
(.:) :: (c -> d) -> (a -> b -> c) -> a -> b -> d
(f .: g) x y = f (g x y)