-- | Haskell operator @g ... f = \\x1 .. xn -> g (f x1 .. xn)@. Compose
-- functions such that all arguments are applied. Obviates @(.).(.)@ and
-- similar patterns in some cases.
--
-- Examples:
--
-- @
-- > ((+) ... (+) ... (+)) (1 :: Int) 2 3 4
-- 10
-- @
--
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}

module Control.DotDotDot
( DotDotDot((...))
, ()
, IsFun
, Return
, Replace
)
where

import Data.Bool
  ( Bool(True, False)
  )
--

import Prelude ()

-- | @IsFun f@ is @'True@ if @f@ has form @_ -> _@ and @'False@ otherwise.
--
type family IsFun (a :: *) :: Bool where
  IsFun (_ -> _) = 'True
  IsFun _        = 'False
--

-- | Class for defining '...' recursively on function types.
--
class b ~ IsFun f => DotDotDot f (b :: Bool) where
  type Return (f :: *) (b :: Bool) :: *
  type Replace (f :: *) (r :: *) (b :: Bool) :: *
  (...) :: (Return f b -> r) -> f -> Replace f r b
  infixr 9 ...
--

instance DotDotDot b (IsFun b) => DotDotDot (a -> b) 'True where
  type Return (a -> b) 'True = Return b (IsFun b)
  type Replace (a -> b) r 'True = a -> Replace b r (IsFun b)
  (...) g f x = g ... f x
--

instance 'False ~ IsFun a => DotDotDot a 'False where
  type Return a 'False = a
  type Replace a r 'False = r
  (...) f x = f x
--

-- | Alias for '...'.
--
() ::
  (b ~ IsFun f, DotDotDot f b) =>
  (Return f b -> r) -> f -> Replace f r b
() = (...)
infixr 9