variadic-function (empty) → 0.1.0.0
raw patch · 8 files changed
+304/−0 lines, 8 filesdep +basedep +hspecdep +variadic-functionsetup-changed
Dependencies added: base, hspec, variadic-function
Files
- ChangeLog.md +3/−0
- LICENSE +30/−0
- README.md +52/−0
- Setup.hs +3/−0
- src/Data/Function/Variadic.hs +91/−0
- src/Data/Function/Variadic/Utils.hs +33/−0
- test/Spec.hs +16/−0
- variadic-function.cabal +76/−0
+ ChangeLog.md view
@@ -0,0 +1,3 @@+# Changelog for variadic-function++## Unreleased changes
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright Boris Lykah (c) 2021++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Boris Lykah nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,52 @@+# variadic-function++Create and transform functions with variable arity.++### How to use++The core of the library is the `Function` class. Use `createFunction` to make a function with a variable number of arguments. Use `transformFunction` to access the arguments and change the result of any function.+++```haskell+constN :: Function f args a EmptyConstraint+ => a -> f+constN a = createFunction+ -- The arguments are not constrained+ (undefined :: p EmptyConstraint)+ -- Combine argument with accumulator. Here we just ignore the argument+ const+ -- Ignore the accumulator and return `a` as a result+ (const a)+ -- Accumulator for combining with the arguments.+ -- We don't take any information from the arguments, so it is just ()+ ()++composeN :: (Function f args b EmptyConstraint, Function g args a EmptyConstraint)+ => (a -> b) -> g -> f+composeN f = transformFunction+ -- The arguments are not constrained+ (undefined :: p EmptyConstraint)+ -- Ignore arguments+ const+ -- Ignore the accumulator and apply f to result of the original function `g`+ (\_ r -> f r)+ -- Composition does not use the accumulator either, so it is ()+ ()++Here is a more complex example that constrains arguments and uses the accumulator:++```haskell+sumN :: forall r f args. (Function f args r ((~) r), Num r)+ => f+sumN = createFunction+ -- The argument must be the same type as the function result. + -- To be able to mention `r` in here, the function signature + -- has `forall` and ScopedTypeVariables is enabled.+ (undefined :: proxy ((~) r))+ -- Add argument to the accumulator+ (+)+ -- Return accumulator as the result+ id+ -- The initial value of accumulator+ 0+```
+ Setup.hs view
@@ -0,0 +1,3 @@+import Distribution.Simple++main = defaultMain
+ src/Data/Function/Variadic.hs view
@@ -0,0 +1,91 @@+module Data.Function.Variadic+ ( -- * Decomposition and creation of functions+ Function (..),+ ConstructFunction,+ DeconstructFunction,++ -- * Helper classes for argument constraints+ EmptyConstraint,+ type (&),+ )+where++import Data.Kind (Type)++-- | Toolkit for creating and transforming functions with a variable number of arguments.+-- Its parameters are function, list of its arguments, its result, and `argC`+-- that constraints all arguments of the function.+class+ (ConstructFunction args r ~ f, DeconstructFunction f ~ '(args, r)) =>+ Function f args r argC+ where+ -- | Create a new function+ --+ -- === __Example usage__+ --+ -- >>> printf :: Function Show f args String => f+ -- >>> printf = createFunction (Proxy :: Proxy Show) (\acc a -> acc <> show a) id ""+ -- >>> printf "hello" () :: String+ -- "hello()"+ createFunction ::+ -- | Required for unambiguous choice of Function instance+ proxy argC ->+ -- | Combine arguments with accumulator+ (forall a. argC a => acc -> a -> acc) ->+ -- | Make result of the function+ (acc -> r) ->+ -- | Accumulator+ acc ->+ f++ -- | Create a function with the same arguments as given one but may have a different result.+ transformFunction ::+ -- | Required for unambiguous choice of the Function instance+ proxy argC ->+ -- | Combine arguments with accumulator+ (forall a. argC a => acc -> a -> acc) ->+ -- | Create result of the `f` function using accumulator and the result of the function to transform+ (acc -> r0 -> r) ->+ -- | Accumulator+ acc ->+ -- | The function to transform+ ConstructFunction args r0 ->+ -- | The new function+ f++-- | Extract list of arguments and the result from the function.+type family DeconstructFunction (f :: Type) :: ([Type], Type) where+ DeconstructFunction (a -> f) = MapFst ((:) a) (DeconstructFunction f)+ DeconstructFunction x = '( '[], x)++type family MapFst (f :: k1 -> k1) (tuple :: (k1, k2)) where+ MapFst f '(a, b) = '(f a, b)++-- | Given the types of function arguments and its result, make a type of a function.+type family ConstructFunction (args :: [Type]) (r :: Type) where+ ConstructFunction '[] r = r+ ConstructFunction (a : args) r = a -> ConstructFunction args r++instance+ ('( '[], r) ~ DeconstructFunction r) =>+ Function r '[] r argC+ where+ createFunction _ _ fr r = fr r+ transformFunction _ _ fr acc r = fr acc r++instance+ (Function f args r argC, argC a) =>+ Function (a -> f) (a : args) r argC+ where+ createFunction pArgC fa fr acc = createFunction pArgC fa fr . fa acc+ transformFunction pArgC fa fr acc f = \a -> transformFunction pArgC fa fr (fa acc a) (f a)++-- | When the arguments are not constrained, use this as the argC parameter of `Function`.+class EmptyConstraint a++instance EmptyConstraint a++-- | Combine constraints. For example, @Function f args x (Show & Num)@.+class (f x, g x) => (&) f g (x :: k)++instance (f x, g x) => (&) f g x
+ src/Data/Function/Variadic/Utils.hs view
@@ -0,0 +1,33 @@+module Data.Function.Variadic.Utils+ ( composeN,+ constN,+ mappendN,+ )+where++import Data.Function.Variadic++-- | Function composition for an arbitrary number of arguments.+--+-- >>> (show `composeN` \a b c -> (a + b + c :: Int)) 1 2 3+-- "6"+composeN :: (Function f args b EmptyConstraint, Function g args a EmptyConstraint) => (a -> b) -> g -> f+composeN f = transformFunction (undefined :: p EmptyConstraint) const (\_ r -> f r) ()++-- | Constant function for an arbitrary number of arguments.+--+-- @+-- let const2 = constN :: x -> a -> b -> x+-- @+--+-- >>> zipWith3 (constN 1) [1..10] [1..5] ["a", "b", "c"] :: [Int]+-- [1,1,1]+constN :: Function f args a EmptyConstraint => a -> f+constN a = createFunction (undefined :: p EmptyConstraint) const (const a) ()++-- | Append multiple monoid values. It is similar to `mconcat` but takes the values as arguments rather than list elements.+--+-- >>> mappendN [1, 2] [3] [4, 5] :: [Int]+-- [1,2,3,4,5]+mappendN :: forall r f args. (Function f args r ((~) r), Monoid r) => f+mappendN = createFunction (undefined :: proxy ((~) r)) mappend id mempty
+ test/Spec.hs view
@@ -0,0 +1,16 @@+{-# LANGUAGE TypeApplications #-}++import Data.Function.Variadic+import Data.Function.Variadic.Utils+import Test.Hspec++main :: IO ()+main = hspec $ do+ describe "Variadic function" $ do+ it "transforms function" $ do+ let f :: Int -> Int -> Int -> String+ f = show `composeN` \a b c -> a + b + c+ f 1 2 3 `shouldBe` "6"+ it "transforms function with inferred type" $ do+ let f = show `composeN` \a b c -> (a + b + c :: Int)+ f 1 2 3 `shouldBe` "6"
+ variadic-function.cabal view
@@ -0,0 +1,76 @@+cabal-version: 1.12++-- This file has been generated from package.yaml by hpack version 0.34.4.+--+-- see: https://github.com/sol/hpack++name: variadic-function+version: 0.1.0.0+synopsis: Create and transform functions with variable arity.+description: Please see the README on GitHub at <https://github.com/lykahb/variadic-function#readme>+category: Data, Functions+homepage: https://github.com/lykahb/variadic-function#readme+bug-reports: https://github.com/lykahb/variadic-function/issues+author: Boris Lykah+maintainer: lykahb@gmail.com+copyright: 2021 Boris Lykah+license: BSD3+license-file: LICENSE+build-type: Simple+extra-source-files:+ README.md+ ChangeLog.md++source-repository head+ type: git+ location: https://github.com/lykahb/variadic-function++library+ exposed-modules:+ Data.Function.Variadic+ Data.Function.Variadic.Utils+ other-modules:+ Paths_variadic_function+ hs-source-dirs:+ src+ default-extensions:+ ConstraintKinds+ DataKinds+ FlexibleContexts+ FlexibleInstances+ MultiParamTypeClasses+ PolyKinds+ RankNTypes+ TypeFamilies+ TypeOperators+ UndecidableInstances+ UndecidableSuperClasses+ build-depends:+ base >=4.7 && <5+ default-language: Haskell2010++test-suite variadic-function-test+ type: exitcode-stdio-1.0+ main-is: Spec.hs+ other-modules:+ Paths_variadic_function+ hs-source-dirs:+ test+ default-extensions:+ ConstraintKinds+ DataKinds+ FlexibleContexts+ FlexibleInstances+ MultiParamTypeClasses+ PolyKinds+ RankNTypes+ TypeFamilies+ TypeOperators+ UndecidableInstances+ UndecidableSuperClasses+ ghc-options: -threaded -rtsopts -with-rtsopts=-N+ build-depends:+ base >=4.7 && <5+ , hspec+ , variadic-function+ default-language: Haskell2010