function-builder (empty) → 0.1.0.0
raw patch · 8 files changed
+453/−0 lines, 8 filesdep +basesetup-changed
Dependencies added: base
Files
- .travis.yml +30/−0
- CHANGELOG.md +3/−0
- LICENSE +30/−0
- README.md +45/−0
- Setup.hs +2/−0
- function-builder.cabal +54/−0
- src/Data/FunctionBuilder.hs +279/−0
- stack.yaml +10/−0
+ .travis.yml view
@@ -0,0 +1,30 @@+language: c++sudo: false++addons:+ apt:+ sources:+ - hvr-ghc+ packages:+ - ghc-8.6.3++before_install:+ - mkdir -p ~/.local/bin+ - travis_retry curl -L https://www.stackage.org/stack/linux-x86_64 | tar xz --wildcards --strip-components=1 -C ~/.local/bin '*/stack'+ - export PATH=~/.local/bin:/opt/ghc/$GHCVER/bin:$PATH+ - chmod a+x ~/.local/bin/stack++install:+ - stack --no-terminal --skip-ghc-check setup++script:+ - stack --no-terminal --skip-ghc-check build+ - stack --no-terminal --skip-ghc-check haddock++cache:+ directories:+ - ~/.stack+ - ~/.local+ - ~/.stack-work-cache+ apt: true
+ CHANGELOG.md view
@@ -0,0 +1,3 @@+# 0.1.0.0++* Initial Release
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright Sven Heyll (c) 2019++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 Author name here 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, addParameter, 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,45 @@+[](https://travis-ci.org/sheyll/function-builder)++[](http://hackage.haskell.org/package/function-builder)++# A library for making Monoid writing functions with variable number of parameters++**Also known as: Holey monoids.**++This library is made to be useful especially for library authors, who want to provide users+with building blocks to create functions that compose a monoidal structure+from their parameters in a type safe way. Think of `printf`.++Imagine a library that let`s user create nicely formatted strings, with the ability to+compose strings into larger strings and to allow the rendering of+all sorts of values, for example `Double`s, `Bool`s, or lists to strings.++**This library** allows the author of such a library to easily add the+building blocks, allowing users to build **poly variadic functions**, i.e. with parameters+depending on the order and composition of these building blocks.++Several `FunctionBuilder` values sharing a common monoidal output type can be composed+to a big `FunctionBuilder` value, in order to build an **output function** that+has a flexible number and types of parameters depending, on the individual+`FunctionBuilder`s used. This output function can be obtained by `toFunction`.++`FunctionBuilder`s can also be composed via standard type classes.++This module gives you ready-made like `Functor`, `Applicative`, `Semigroup`, `Monoid` or Category` instances;++The basic building blocks are `toFunction`, `immediate` and `addParameter`.++For example, you could use this library to build a string formatting+library, that allows users to compose arbitrary, _printf-style_ render **functions**+from reusable building blocks, such that they can be re-combined in order to make+get functions, that can be applied to parameters that fill place holders, like e.g.:++ renderCpuTemp :: Int -> Float -> String+ renderCpuTemp =+ toFunction (render "CPU " . renderInt . render " Temperature: " . renderFloat)++## Similar Libraries++* [polyToMonoid](http://hackage.haskell.org/package/polyToMonoid)++* [HoleyMonoid](http://hackage.haskell.org/package/HoleyMonoid)
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ function-builder.cabal view
@@ -0,0 +1,54 @@+name: function-builder+version: 0.1.0.0+synopsis: Create poly variadic functions for monoidal results+description: Please see README.md+homepage: https://github.com/sheyll/function-builder#readme+license: BSD3+license-file: LICENSE+author: Sven Heyll+maintainer: sven.heyll@gmail.com+copyright: 2019 Sven Heyll+category: Data Structures+build-type: Simple+extra-source-files: README.md+ , CHANGELOG.md+ , stack.yaml+ , .travis.yml+cabal-version: >=1.10++library+ hs-source-dirs: src+ exposed-modules: Data.FunctionBuilder+ build-depends: base >= 4.11 && < 5+ default-language: Haskell2010+ default-extensions: BangPatterns+ , ConstraintKinds+ , DataKinds+ , DefaultSignatures+ , DeriveDataTypeable+ , DeriveFunctor+ , DeriveGeneric+ , DuplicateRecordFields+ , FlexibleInstances+ , FlexibleContexts+ , FunctionalDependencies+ , GADTs+ , GeneralizedNewtypeDeriving+ , KindSignatures+ , MultiParamTypeClasses+ , OverloadedStrings+ , QuasiQuotes+ , RecordWildCards+ , RankNTypes+ , ScopedTypeVariables+ , StandaloneDeriving+ , TupleSections+ , TypeApplications+ , TypeFamilies+ , TypeInType+ , TypeOperators+ , TypeSynonymInstances++source-repository head+ type: git+ location: https://github.com/sheyll/function-builder
+ src/Data/FunctionBuilder.hs view
@@ -0,0 +1,279 @@+-- | This library allows you to build function builder libraries.+--+-- This library is made to be useful especially for library authors, who want to provide+-- building blocks for users to build functions of varying parameters in a type safe way.+--+-- Imagine a library that let's user create nicely formatted strings, with the ability to+-- compose strings into larger strings and to allow the rendering of+-- all sorts of values, for example 'Double's, 'Bool's, or lists to strings.+--+-- __This library__ allows the author of such a library to easily add the+-- building blocks, allowing users to build __poly variadic functions__, i.e. with parameters+-- depending on the order and composition of these building blocks.+--+-- Several 'FunctionBuilder' values sharing a common monoidal output type can be composed+-- to a big 'FunctionBuilder' value, in order to build an __output function__ that+-- has a flexible number and types of parameters depending, on the individual+-- 'FunctionBuilder's used. This output function can be obtained by 'toFunction'.+--+-- 'FunctionBuilder's can also be composed via standard type classes.+--+-- This module gives you ready-made like 'Functor', 'Applicative', 'Semigroup', 'Monoid' or 'Category' instances;+--+-- The basic building blocks are 'toFunction', 'immediate' and 'addParameter'.+--+-- For example, you could use this library to build a string formatting+-- library, that allows users to compose arbitrary, /printf-style/ render __/functions/__+-- from reusable building blocks, such that they can be re-combined in order to make+-- get functions, that can be applied to parameters that fill place holders, like e.g.:+--+-- @+-- renderCpuTemp :: Int -> Float -> String+-- renderCpuTemp =+-- toFunction (render "CPU " . renderInt . render " Temperature: " . renderFloat)+-- @+--+module Data.FunctionBuilder where++import Control.Category+import Data.Monoid+import Data.Semigroup+import Prelude hiding ( id+ , (.)+ )+import Data.Tagged++-- | A function, that takes an accumulation function as paramater,+-- and returns a function that will have zero or more parameters and returns+-- an accumulated result: @(acc -> next)+--+-- A @FunctionBuilder acc next f@ is a function @(acc -> next) -> f@.+--+-- Type parameter:+--+-- [@acc@] The final output value that gets build up by the+-- applying the resulting function build by the composed @FunctionBuilder@s.+-- If you were building a @printf@ style library, then @acc@ would+-- probably be 'String'.+--+-- [@next@] The @next@ parameter allows composing @FunctionBuilder@s, and the final output+-- will be a function @f@ with zero or more parameters of different type+-- resulting in an @acc@ value. Most 'FunctionBuilder's are parameteric in @next@ and+-- also have @next@ in a in @f_make_next@.+-- Also note that in @(acc -> next) -> f_make_next@ the @next@ is+-- the output of the continuation @acc -> next@ passed to the @FunctionBuilder@ function,+-- hence this /output/ is actually in /input/ from the perspective of the @FunctionBuilder@,+-- which makes a @FunctionBuilder@ 'Contravariant' in @next@.+--+-- [@f_make_next@] This is usually a function that returns @next@ or is+-- directly @next@, this is the resulting - seemingly /poly variadic/ -+-- __outout function__ composed through the composition of @FunctionBuilder@s, and+-- obtained by 'toFunction'.+--+-- It is required for the type-class instances allowing the+-- composition as 'Semigroup's or 'Monoid's or even 'Category'.+--+-- It is totaly valid to apply it to 'id', to get @f@, and behind @f@+-- typically lies a function of some parameters to @next@.+--+-- At the end of /the chain/ @next@ will be @acc@ and before that+-- the function that takes the next parameters and then returns out.+--+-- See `toFunction`.+--+-- Composition comes in two flavours:+--+-- (1) By using `(.)` to add to the accumulator a value passed to an additional argument+-- of the resulting output function.+--+-- (2) By using `(<>)` to append a fixed value to the accumulator directly.+--+-- For example:+--+-- > import Data.Monoid (Sum(..))+-- >+-- > add :: FunctionBuilder (Sum Int) next (Int -> next)+-- > add = FB $ \k -> \x -> k (Sum x)+--+-- Here the @next@ parameter in @add@ is just passed through and+-- is the __key__ to be able to __compose__ @FunctionBuilder@s. @add@ is+-- parametric in @next@.+-- .+-- And when we are done composing, we pass `id` to the @FunctionBuilder@, which+-- forces the the @next@ parameter to match the @acc@ type, and which+-- would the make @add@ function look like this:+--+-- > addToZero :: FunctionBuilder (Sum Int) (Sum Int) (Int -> Sum Int)+-- > addToZero = add+newtype FunctionBuilder acc next f_make_next = FB {runFunctionBuilder :: (acc -> next) -> f_make_next }++-- | Compose 'FunctionBuilder's such that the output function first takes all parameters+-- from the first 'FunctionBuilder' and then all parameters from the second 'FunctionBuilder' and then+-- appends the results of both functions, which is why we need the 'Monoid' constraint.+instance Monoid m => Category (FunctionBuilder m) where+ (.) (FB f) (FB g) = FB (\k -> (f (\m1 -> g (\m2 -> k (m1 <> m2)))))+ id = FB ($ mempty)++-- | Allow appending a 'FunctionBuilder' to another without changing the resulting output function.+-- For example, 'FunctionBuilder's that have @FunctionBuilder m r r@ can append something to @m@.+-- It is not possible to add new parameters to the output function, this can only be done by+-- the 'Category' instance.+instance Semigroup m => Semigroup (FunctionBuilder m r r) where+ (<>) (FB f) (FB g) = FB (\k -> (f (\m1 -> g (\m2 -> k (m1 <> m2)))))++-- | Allow appending a 'FunctionBuilder' to another without changing the resulting output function.+-- For example, 'FunctionBuilder's that have @FunctionBuilder m r r@ can append something to @m@.+-- It is not possible to add new parameters to the output function, this can only be done by+-- the 'Category' instance.+instance Monoid m => Monoid (FunctionBuilder m r r) where+ mappend = (<>)+ mempty = id++instance Functor (FunctionBuilder m r) where+ fmap f (FB !h) = FB $ \k -> f (h k)++instance Applicative (FunctionBuilder m r) where+ pure = FB . const+ (FB f) <*> (FB x) = FB (\k -> f k (x k))++instance Monad (FunctionBuilder m r) where+ (FB m) >>= f = FB (\k -> runFunctionBuilder (f (m k)) k)++-- | Turn a 'FunctionBuilder' into the __output function__ that consumes+-- zero or more of parameter and then always return @outout@.+--+-- If passed a 'FunctionBuilder' value of type @FunctionBuilder String String (Int -> Double -> Int -> String)@+--+-- For example:+--+-- > example :: Int -> Double -> Int -> String+-- > example = toFunction (i . d . i)+-- >+-- > s :: String -> FunctionBuilder String a a+-- > s x = FB (\k -> k x)+-- >+-- > i :: FunctionBuilder String next (Int -> next)+-- > i = FB (\k x -> k $ show x)+-- >+-- > d :: FunctionBuilder String next (Double -> next)+-- > d = FB (\k x -> k $ show x)+toFunction :: FunctionBuilder output output make_output -> make_output+toFunction = ($ id) . runFunctionBuilder++-- ** Building 'FunctionBuilder's++-- | Create a 'FunctionBuilder' that /appends/ something to the (monoidal-) output value.+--+-- This is a smart constructor for a 'FunctionBuilder'.+-- This functions is probably equal to:+--+-- > immediate x = FB (\k -> k x)+--+-- Example:+--+-- When building a 'String' formatting 'FunctionBuilder'+-- the function to append a literal string could be:+--+-- > s :: String -> FunctionBuilder String a a+-- > s = immediate+--+-- > c :: Char -> FunctionBuilder String a a+-- > c = immediate . (:[])+--+-- > example :: String+-- > example = toFunction (s "hello" . c ' ' . s "world")+--+-- >>> example+-- "hello world"+--+-- See the example in `toFunction`.+immediate :: m -> FunctionBuilder m r r+immediate m = FB { runFunctionBuilder = ($ m) }++-- | Create a 'FunctionBuilder' that adds an argument to the output function,+-- and converts that argument to a value that can be accumulated in the+-- resulting monoidal value.+--+-- This is a smart constructor for a 'FunctionBuilder'.+-- This functions is probably equal to:+--+-- > addParameter f = FB (\k x -> k (f x))+--+-- Example:+--+-- When building a 'String' formatting 'FunctionBuilder'+-- the function to append a parameter that has a show instance could be:+--+-- > showing :: Show a => FunctionBuilder String r (a -> r)+-- > showing = addParameter show+--+-- > example :: (Show a, Show b) => a -> b -> String+-- > example = toFunction (showing . showing)+--+-- >>> example True 0.33214+-- "True0.33214"+--+-- See the example in `toFunction`.+addParameter :: (a -> m) -> FunctionBuilder m r (a -> r)+addParameter f = FB { runFunctionBuilder = (. f) }++-- ** Modifying Parameters of 'FunctionBuilder's++-- | Take away a function parameter added with 'addParameter' by /pre -/ applying it to some+-- value.+-- This is equivalent to:+--+-- @+-- fillParameter f x = f <*> pure x+-- @+--+fillParameter :: FunctionBuilder m r (a -> b) -> a -> FunctionBuilder m r b+fillParameter (FB !f) x = FB $ \k -> f k x++-- | Convert a 'FunctionBuilder' for a function @(a -> b)@ to @(Tagged tag a -> b)@.+tagParameter+ :: forall tag m r a b+ . FunctionBuilder m r (a -> b)+ -> FunctionBuilder m r (Tagged tag a -> b)+tagParameter = fmap (\f -> f . untag)++-- ** 'FunctionBuilder' Transformations++-- | Compose to 'FunctionBuilder's such that the second 'FunctionBuilder' may depend on the intermediate result+-- of the first. If you skwirm hard enough you __almost__ see '(>>=)' with @m ~ n@.+bind+ :: FunctionBuilder m b c+ -> (m -> FunctionBuilder n a b)+ -> FunctionBuilder n a c+bind mbc fm =+ FB $ \kna -> runFunctionBuilder mbc (($ kna) . runFunctionBuilder . fm)++-- | Convert the accumulated (usually monoidal-) value,+-- this allows to change the underlying accumlator type.+mapAccumulator :: (m -> n) -> FunctionBuilder m a b -> FunctionBuilder n a b+mapAccumulator into (FB f) = FB (\k -> f (k . into))++-- | Convert the output of a 'FunctionBuilder' value; since most+-- 'FunctionBuilder's are parameteric in @r@ they also have @r@ in a+-- in @a@, such that @a@ always either is @r@ or is a+-- function returning @r@ eventually.+--+-- In order to get from a 'FunctionBuilder' that can accept a continuation returning it an @r@+-- to a 'FunctionBuilder' that accepts continuations returning an @s@ instead, we need to+-- apply a function @s -> r@ to the return value of the continuation.+--+-- Note that a 'mapNext' will not only change the @r@ to an @s@ but+-- probably also the the @a@, when it is parametric, as in this contrived example:+--+-- > example :: Int -> x -> Sum Int+-- > example = toFunction (ign add)+-- >+-- > add :: FunctionBuilder (Sum Int) next (Int -> next)+-- > add = FB (\k x -> k $ Sum x)+-- >+-- > ign :: FunctionBuilder m (x -> r) a -> FunctionBuilder m r a+-- > ign = mapNext const+--+-- Here the extra parameter @x@ is /pushed down/ into the @a@ of the @add@ 'FunctionBuilder'.+mapNext :: (s -> r) -> FunctionBuilder m r a -> FunctionBuilder m s a+mapNext outof (FB f) = FB (\k -> f (outof . k))
+ stack.yaml view
@@ -0,0 +1,10 @@+resolver: lts-13.3++packages:+- '.'++extra-deps: []++flags: {}++extra-package-dbs: []