{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeInType #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
module Data.Function.NAry (
NAry
, ApplyNAry(..)
) where
import Data.Kind (Type)
import Data.Proxy (Proxy(..))
import GHC.TypeLits (Nat, type (-), natVal, KnownNat)
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)
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 #-}