{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE FlexibleInstances #-} -- | -- Module : Data.Function.Polyvariadic -- Copyright : (c) Francesco Gazzetta 2017 -- License : BSD3 (see the file LICENSE) -- -- Maintainer : francygazz@gmail.org -- Stability : experimental -- Portability : portable -- -- Create and apply functions with an arbitrary number of arguments. module Data.Function.Polyvariadic ( -- * Creation Polyvariadic (..) -- * Application , Apply (apply') , apply ) where import Data.Foldable import Data.Accumulator ---- creation -- | Creation of functions with an arbitrary number of arguments. -- -- The arguments will be accumulated in the given 'Accumulator', -- which will then be passed as an argument to the function. -- -- ==== __Examples__ -- -- Three integers to a list. Note that you have to add type annotations -- for nearly everything to avoid ambiguities -- >>> polyvariadic mempty (id :: [Int] -> [Int]) (1::Int) (2::Int) (3::Int) :: [Int] -- -- The classic @printf@ function, which takes an arbitrary amount of arguments -- and inserts them in a string: -- -- @ -- {-# LANGUAGE MultiParamTypeClasses #-} -- {-# LANGUAGE FlexibleInstances #-} -- {-# LANGUAGE FlexibleContexts #-} -- import Data.Function.Polyvariadic -- import Data.Accumulator -- -- magicChar = \'%\' -- notMagicChar = (\/= magicChar) -- -- data PrintfAccum = PrintfAccum { done :: String, todo :: String } -- -- instance Show x => Accumulator PrintfAccum x where -- accumulate x (PrintfAccum done (_:todo)) = PrintfAccum -- (done ++ show x ++ takeWhile notMagicChar todo) -- (dropWhile notMagicChar todo) -- accumulate _ acc = acc -- -- printf' str = polyvariadic -- (PrintfAccum (takeWhile notMagicChar str) (dropWhile notMagicChar str)) -- done -- @ -- -- >>> printf' "aaa%bbb%ccc%ddd" "TEST" 123 True -- "aaa\"TEST\"bbb123cccTrueddd" class Polyvariadic accumulator result x where -- | Takes an accumulator @acc@, a function @f@, and an arbitrary -- number of additional arguments which will be accumulated in @acc@, -- which is finally passed to @f@. polyvariadic :: accumulator -> (accumulator -> result) -> x -- | Accumulates the next argument instance (Accumulator c i, Polyvariadic c b x) => Polyvariadic c b (i -> x) where polyvariadic a f x = polyvariadic (accumulate x a) f -- | There are no more arguments to accumulate so the function is applied -- to the 'Accumulator' instance Polyvariadic accumulator result result where polyvariadic a f = f a ---- application -- | Application of function with an arbitrary number of arguments to the -- elements of a list. -- -- __Will raise an error if the list doesn't have enough elements__ -- -- ==== __Examples__ -- -- >>> apply ((+) :: Int -> Int -> Int) ([1,2] :: [Int]) :: Int -- 3 class Apply a b x where apply' :: x -> [a] -> b -- | The final type is not reached yet and the application continues instance (Apply a b x) => Apply a b (a -> x) where apply' f (x:xs) = apply' (f x) xs apply' _ _ = error "Not enough arguments in polyvariadic application" -- | The final type is reached and the application terminates instance Apply a b b where apply' f _ = f -- | Like 'apply'' but with an arbitrary 'Foldable' instead if a list apply :: (Apply a b x, Foldable t) => x -> t a -> b apply f = apply' f . toList