function-builder 0.1.0.1 → 0.1.0.2
raw patch · 3 files changed
+146/−65 lines, 3 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
- Data.FunctionBuilder: bind :: FunctionBuilder m b c -> (m -> FunctionBuilder n a b) -> FunctionBuilder n a c
+ Data.FunctionBuilder: bind :: FunctionBuilder m g_next f_g_next -> (m -> FunctionBuilder n next g_next) -> FunctionBuilder n next f_g_next
Files
- CHANGELOG.md +8/−0
- function-builder.cabal +1/−1
- src/Data/FunctionBuilder.hs +137/−64
CHANGELOG.md view
@@ -1,3 +1,11 @@+# 0.1.0.2++* Improve documentation++# 0.1.0.1++* Fix Cabal build issues+ # 0.1.0.0 * Initial Release
function-builder.cabal view
@@ -1,5 +1,5 @@ name: function-builder-version: 0.1.0.1+version: 0.1.0.2 synopsis: Create poly variadic functions for monoidal results description: Please see README.md homepage: https://github.com/sheyll/function-builder#readme
src/Data/FunctionBuilder.hs view
@@ -1,19 +1,36 @@--- | This library allows you to build function builder libraries.+-- | A builder for functions of variable parameters ----- 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' values can be composed, and eventually /rendered/+-- into a __function__ by 'toFunction'. ----- 'FunctionBuilder's can also be composed via standard type classes.+-- For example the composition of: --+-- >+-- > fb1 :: FunctionBuilder MyMonoid composeMe (Int -> composeMe)+-- > fb1 = addParameter ...+-- >+-- > fb2 :: FunctionBuilder MyMonoid composeMe (String -> composeMe)+-- > fb2 = addParameter ...+-- >+-- > fb3 :: FunctionBuilder MyMonoid composeMe (Bool -> composeMe)+-- > fb3 = addParameter ...+-- >+-- > fb :: FunctionBuilder MyMonoid composeMe (Int -> String -> Bool -> composeMe)+-- > fb = fb1 . fb2 . fb3+-- >+-- > f :: Int -> String -> Bool -> MyMonoid+-- > f = toFunction fb123+--+-- 'FunctionBuilder's are composed via '.' from 'Category' and '<>' from 'Semigroup'.+-- -- This module provides 'Functor', 'Applicative', 'Monad', 'Semigroup', 'Monoid' and -- 'Category' instances; ----- The basic building blocks when generating a poly variadic function--- are 'immediate' and 'addParameter'.+-- The smart-constructors 'immediate' and 'addParameter' create 'FunctionBuilder's+-- that either write a hard coded constant to the output 'Monoid' or add a function+-- that will be applied to an additional runtime parameter. ----- The output function is obtained from a 'FunctionBuilder' by __toFunction__.+-- Further /glue-code/ is provided to allow changing the underlying Monoid, see 'bind'. -- module Data.FunctionBuilder where @@ -25,68 +42,75 @@ ) 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 tricky newtype wrapper around a function that carries out a computation+-- resulting in a monoidal output value that is passed to a continuation. ----- A @FunctionBuilder acc next f@ is a function @(acc -> next) -> f@.+-- Type parameters: ----- Type parameter:+-- [@acc@] Type of monoidal value that is build from the parameters of the function+-- returned by 'toFunction'.+-- For example: In a @printf@ style formatting library @acc@ could be 'String'. ----- [@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 /trick-/ parameter that allows composing @FunctionBuilder@s.+-- Also note that 'FunctionBuilder's are contravarient in this parameter;+-- @next@ is the output of the continuation @acc -> next@, hence this is an+-- /input/ from the perspective of the @FunctionBuilder@. ----- [@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 type that returns @next@,+-- this is the type of the output function returned by 'toFunction'. ----- [@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'.+-- A @FunctionBuilder acc next f@ is a newtype wrapper around functions of type+-- @(acc -> next) -> f@. ----- It is totaly valid to apply it to 'id', to get @f@, and behind @f@--- typically lies a function of some parameters to @next@.+-- The immediate return value of the function is usually a function type,+-- that takes zero or more arguments: @a_0 -> .. -> a_N -> 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.+-- The @FunctionBuilder@s that 'addParameter' returns are polymorphic in @next@.+-- And @next@ is the key for composition. ----- See `toFunction`.+-- For example: --+-- @+-- fb1 :: FunctionBuilder MyMonoid next (Int -> next)+-- fb1 = addParameter undefined+--+-- fb2 :: FunctionBuilder MyMonoid next (String -> next)+-- fb2 = addParameter undefined+--+-- newtype MyMonoid = MyMonoid () deriving (Semigroup, Monoid)+-- @+--+-- When we /desugar/ with ghci:+--+-- >>> :t (runFunctionBuilder fb1)+-- (runFunctionBuilder fb1) :: (MyMonoid -> next) -> Int -> next+--+-- >>> :t (runFunctionBuilder fb2)+-- (runFunctionBuilder fb2) :: (MyMonoid -> next) -> String -> next+-- -- 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.+-- (1) By using '.' to add to the accumulator a value passed to an additional argument+-- of the resulting output function (see example below). ----- (2) By using `(<>)` to append a fixed value to the accumulator directly.+-- (2) By using '<>' to append a fixed value to the accumulator directly. ----- For example:+-- When __composing__ @fb1@ and @fb2@ using '.' we get: ----- > import Data.Monoid (Sum(..))--- >--- > add :: FunctionBuilder (Sum Int) next (Int -> next)--- > add = FB $ \k -> \x -> k (Sum x)+-- >>> :t (fb1 . fb2)+-- (fb1 . fb2) :: FunctionBuilder MyMonoid a (Int -> String -> a) ----- 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:+-- And desugared: ----- > addToZero :: FunctionBuilder (Sum Int) (Sum Int) (Int -> Sum Int)--- > addToZero = add+-- >>> :t runFunctionBuilder (fb1 . fb2)+-- runFunctionBuilder (fb1 . fb2) :: (MyMonoid -> next) -> Int -> String -> next+--+-- What happened during composition was that the @next@ in @fb1@ was used to insert+-- into @Int -> next@ the @String -> other_next@ from @fb2@; such that this results in+-- @Int -> (String -> other_next)@.+-- (Note: For clarity I renamed the type local type parameter @next@ to @other_next@ from @fb2@)+-- 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@@ -121,14 +145,29 @@ 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@.+-- | Get the composed __output function__ of a 'FunctionBuilder'. ----- If passed a 'FunctionBuilder' value of type @FunctionBuilder String String (Int -> Double -> Int -> String)@+-- The 'FunctionBuilder' passed to this function must match this signature: ----- For example:+-- > FunctionBuilder m m (arg0 -> .. -> m) --+-- This means that the result of the generated function @arg0 -> .. -> m@ __MUST__ be+-- @m@, the underlying 'Monoid'.+--+-- The 'FunctionBuilder's generated by 'addParameter' and 'immediate' are parametric+-- in the second type parameter and match the type signature required by this function.+--+-- Example 1:+--+-- > fb :: FunctionBuilder String String (Int -> Double -> Int -> String)+-- > fb = undefined+-- > -- > example :: Int -> Double -> Int -> String+-- > example = toFunction fb+--+-- Example 2:+--+-- > example :: Int -> Double -> Int -> String -- > example = toFunction (i . d . i) -- > -- > s :: String -> FunctionBuilder String a a@@ -139,6 +178,7 @@ -- > -- > d :: FunctionBuilder String next (Double -> next) -- > d = FB (\k x -> k $ show x)+-- toFunction :: FunctionBuilder output output make_output -> make_output toFunction = ($ id) . runFunctionBuilder @@ -151,6 +191,9 @@ -- -- > immediate x = FB (\k -> k x) --+-- The generated builder can be passed to 'toFunction' since it is parametric+-- in its second type parameter.+-- -- Example: -- -- When building a 'String' formatting 'FunctionBuilder'@@ -168,7 +211,7 @@ -- >>> example -- "hello world" ----- See the example in `toFunction`.+-- See the example in 'toFunction'. immediate :: m -> FunctionBuilder m r r immediate m = FB { runFunctionBuilder = ($ m) } @@ -181,6 +224,9 @@ -- -- > addParameter f = FB (\k x -> k (f x)) --+-- The generated builder can be passed to 'toFunction' since it is parametric+-- in its second type parameter.+-- -- Example: -- -- When building a 'String' formatting 'FunctionBuilder'@@ -195,7 +241,7 @@ -- >>> example True 0.33214 -- "True0.33214" ----- See the example in `toFunction`.+-- See the example in 'toFunction'. addParameter :: (a -> m) -> FunctionBuilder m r (a -> r) addParameter f = FB { runFunctionBuilder = (. f) } @@ -203,6 +249,22 @@ -- | Take away a function parameter added with 'addParameter' by /pre -/ applying it to some -- value.+--+-- For example:+--+-- > intArg :: FunctionBuilder MyMonoid a (Int -> a)+-- > intArg = addParameter undefined+-- >+-- > stringArg :: FunctionBuilder MyMonoid a (String -> a)+-- > stringArg = addParameter undefined+-- >+-- > twoInt :: FunctionBuilder MyMonoid a (Int -> String -> a)+-- > twoInt = intArg . stringArg+-- >+-- > example :: FunctionBuilder MyMonoid a (String -> a)+-- > example = fillParameter twoInt 42+--+-- -- This is equivalent to: -- -- @@@ -222,11 +284,22 @@ -- ** '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@.+-- of the first. Similar to a monadic bind '>>=' but more flexible sind the underlying+-- 'Monoid' may change too, for example:+--+-- > intText :: FunctionBuilder Text next (Int -> next)+-- > intText = addParameter undefined+-- >+-- > unpackB :: Text -> FunctionBuilder String next next+-- > unpackB = immediate . unpack+-- >+-- > intStr :: FunctionBuilder String next (Int -> next)+-- > intStr = intText `bind` unpackB+-- bind- :: FunctionBuilder m b c- -> (m -> FunctionBuilder n a b)- -> FunctionBuilder n a c+ :: FunctionBuilder m g_next f_g_next+ -> (m -> FunctionBuilder n next g_next)+ -> FunctionBuilder n next f_g_next bind mbc fm = FB $ \kna -> runFunctionBuilder mbc (($ kna) . runFunctionBuilder . fm)