{-# LANGUAGE TypeFamilies            #-}

{-# LANGUAGE MultiParamTypeClasses   #-}

{-# LANGUAGE TypeOperators           #-}

{-# LANGUAGE FlexibleInstances       #-}

{-# LANGUAGE FlexibleContexts        #-}

{-# LANGUAGE ScopedTypeVariables     #-}

{-# LANGUAGE UndecidableInstances    #-}

{-# LANGUAGE TypeApplications        #-}

{-# LANGUAGE TypeInType              #-}

{-# LANGUAGE AllowAmbiguousTypes     #-}



-----------------------------------------------------------------------------

-- |

-- Module      :  Data.Function.NAry

-- Copyright   :  (C) 2017 Alexey Vagarenko

-- License     :  BSD-style (see LICENSE)

-- Maintainer  :  Alexey Vagarenko (vagarenko@gmail.com)

-- Stability   :  experimental

-- Portability :  non-portable

--

----------------------------------------------------------------------------



module Data.Function.NAry (

      NAry

    , ApplyNAry(..)

) where



import Data.Kind                    (Type)

import Data.Proxy                   (Proxy(..))

import GHC.TypeLits                 (Nat, type (-), natVal, KnownNat)



---------------------------------------------------------------------------------------------------

-- | N-ary function from @n@ arguments of type @t@ to value of type @r@.

type family NAry (n :: Nat) (t :: Type) (r :: Type) :: Type where

    NAry 0 t r = r

    NAry n t r = t -> (NAry (n - 1) t r)



-- | Apply list of params to N-ary function.

class ApplyNAry (n :: Nat) (t :: Type) (r :: Type) where

    applyNAry :: NAry n t r -> [t] -> r



instance {-# OVERLAPPING #-} ApplyNAry 0 t r where

    applyNAry r _ = r

    {-# INLINE applyNAry #-}



instance {-# OVERLAPPABLE #-}

    (ApplyNAry (n - 1) t r, NAry n t r ~ (t -> NAry (n - 1) t r), KnownNat n)

    => ApplyNAry n t r where

    applyNAry f (x : xs) = applyNAry @(n - 1) @t @r (f x) xs

    applyNAry _ []       = error $ "Not enough params to apply to " ++ show (natVal (Proxy @n)) ++ "-ary function."

    {-# INLINE applyNAry #-}