function-builder 0.2.0.1 → 0.3.0.0
raw patch · 5 files changed
+210/−133 lines, 5 filesdep +function-builderPVP ok
version bump matches the API change (PVP)
Dependencies added: function-builder
API changes (from Hackage documentation)
- Data.FunctionBuilder: class (HasFunctionBuilder w a, ToFunction w a r ~ (b -> r)) => HasParameter w a b r
- Data.FunctionBuilder: instance Data.FunctionBuilder.DynamicContent m (a -> m) a
- Data.FunctionBuilder: instance Data.FunctionBuilder.StaticContent m m
+ Data.FunctionBuilder: deferred :: (a -> m) -> FunctionBuilder m r (a -> r)
- Data.FunctionBuilder: addParameter :: DynamicContent m a parameter => a -> FunctionBuilder m next (parameter -> next)
+ Data.FunctionBuilder: addParameter :: (DynamicContent m a parameter, a ~ (parameter -> m)) => a -> FunctionBuilder m next (parameter -> next)
- Data.FunctionBuilder: addStaticContent :: StaticContent m a => a -> FunctionBuilder m next next
+ Data.FunctionBuilder: addStaticContent :: (StaticContent m a, a ~ m) => a -> FunctionBuilder m next next
Files
- CHANGELOG.md +8/−0
- README.md +1/−1
- function-builder.cabal +36/−1
- src-test/Tests.hs +57/−0
- src/Data/FunctionBuilder.hs +108/−131
CHANGELOG.md view
@@ -1,3 +1,11 @@+# 0.3.0.0++* Update README with `Cokleisli ((->) w) a b ~ FunctionBuilder w a b`+* Re-introduce `immediate` renamed to `deferred`+* Remove `HasParameter`+* Fix some typos+* Add unit tests+ # 0.2.0.1 * Update README
README.md view
@@ -34,7 +34,7 @@ str = immediate renderInt :: FunctionBuiler String next (Int -> next)- renderInt = addParameter show+ renderInt = deferred show renderFloat :: FunctionBuiler String next (Float -> next) renderFloat = ...
function-builder.cabal view
@@ -1,5 +1,5 @@ name: function-builder-version: 0.2.0.1+version: 0.3.0.0 synopsis: Create poly variadic functions for monoidal results description: Please see README.md homepage: https://github.com/sheyll/function-builder#readme@@ -20,6 +20,41 @@ hs-source-dirs: src exposed-modules: Data.FunctionBuilder build-depends: base >= 4.11 && < 5, tagged+ 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++test-suite function-builder-tests+ type: exitcode-stdio-1.0+ ghc-options: -Wall+ hs-source-dirs: src-test+ main-is: Tests.hs+ build-depends: base >= 4.11 && < 5, tagged, function-builder default-language: Haskell2010 default-extensions: BangPatterns , ConstraintKinds
+ src-test/Tests.hs view
@@ -0,0 +1,57 @@+{-# LANGUAGE UndecidableInstances #-}++module Main where+++import Data.FunctionBuilder+++data Hello = MkHello String++instance HasFunctionBuilder String Hello where+ type ToFunction String Hello next = next+ toFunctionBuilder (MkHello !s) = immediate s++data HelloP = MkHelloP++instance DynamicContent String HelloP String where+ addParameter _ = deferred ("HelloP: " ++)++instance HasFunctionBuilder String HelloP where+ type ToFunction String HelloP next = String -> next+ toFunctionBuilder = addParameter++data SetStringParam a = MkSetter a String++instance DynamicContent String a String => HasFunctionBuilder String (SetStringParam a) where+ type ToFunction String (SetStringParam a) r = r+ toFunctionBuilder (MkSetter f str) = fillParameter (addParameter f) str++++data AssignParameter w a b where+ MkAssignParameter ::DynamicContent w a b => a -> b -> AssignParameter w a b++instance StaticContent w (AssignParameter w a b) where+ addStaticContent (MkAssignParameter !a !b) = fillParameter (addParameter a) b++instance HasFunctionBuilder w (AssignParameter w a b) where+ toFunctionBuilder = addStaticContent+++type FBStr a b = FunctionBuilder String a b++main :: IO ()+main =+ if toFunction (toFunctionBuilder (MkHello "test") :: FBStr String String)+ == ("test" :: String)+ && toFunction (addParameter MkHelloP :: FBStr String (String -> String))+ "test"+ == "HelloP: test"+ && toFunction+ (toFunctionBuilder (MkSetter MkHelloP "test") :: FBStr String String)+ == "HelloP: test"+ then+ return ()+ else+ error "Tests failed."
src/Data/FunctionBuilder.hs view
@@ -7,13 +7,13 @@ -- -- > -- > fb1 :: FunctionBuilder MyMonoid composeMe (Int -> composeMe)--- > fb1 = addParameter ...+-- > fb1 = deferred ... -- > -- > fb2 :: FunctionBuilder MyMonoid composeMe (String -> composeMe)--- > fb2 = addParameter ...+-- > fb2 = deferred ... -- > -- > fb3 :: FunctionBuilder MyMonoid composeMe (Bool -> composeMe)--- > fb3 = addParameter ...+-- > fb3 = deferred ... -- > -- > fb :: FunctionBuilder MyMonoid composeMe (Int -> String -> Bool -> composeMe) -- > fb = fb1 . fb2 . fb3@@ -26,7 +26,7 @@ -- This module provides 'Functor', 'Applicative', 'Monad', 'Semigroup', 'Monoid' and -- 'Category' instances; ----- The smart-constructors 'immediate' and 'addParameter' create 'FunctionBuilder's+-- The smart-constructors 'immediate' and 'deferred' 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. --@@ -66,17 +66,17 @@ -- The immediate return value of the function is usually a function type, -- that takes zero or more arguments: @a_0 -> .. -> a_N -> next@. ----- The @FunctionBuilder@s that 'addParameter' returns are polymorphic in @next@.+-- The @FunctionBuilder@s that 'deferred' returns are polymorphic in @next@. -- And @next@ is the key for composition. -- -- For example: -- -- @ -- fb1 :: FunctionBuilder MyMonoid next (Int -> next)--- fb1 = addParameter undefined+-- fb1 = deferred undefined -- -- fb2 :: FunctionBuilder MyMonoid next (String -> next)--- fb2 = addParameter undefined+-- fb2 = deferred undefined -- -- newtype MyMonoid = MyMonoid () deriving (Semigroup, Monoid) -- @@@ -112,6 +112,14 @@ -- (Note: For clarity I renamed the type local type parameter @next@ to @other_next@ from @fb2@) -- -- Also, there is the 'StaticContent' type class for types that have function builders.+--+-- NOTE: @FunctionBuilder w a b@ is actually @Cokleisli ((->) w) a b@+-- When @w@ is a 'Monoid' then, @((->) w)@ is a @Comonad@ instance.+-- The reason why this library does not use the comonad library under the hood+-- is currently only the missing 'Semigroup' and 'Monoid' instances; and+-- to avoid orphan instances a newtype wrapper would be required, and that+-- does not justify the additional dependency and the pollution of this library+-- with scary @Cokleislies@ and @Comonads@. 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@@ -137,13 +145,14 @@ mempty = id instance Functor (FunctionBuilder m r) where- fmap f (FB !h) = FB $ \k -> f (h k)+ fmap f (FB h) = FB (f . h) 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+ return = pure (FB m) >>= f = FB (\k -> runFunctionBuilder (f (m k)) k) -- | Get the composed __output function__ of a 'FunctionBuilder'.@@ -155,7 +164,7 @@ -- 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+-- The 'FunctionBuilder's generated by 'deferred' and 'immediate' are parametric -- in the second type parameter and match the type signature required by this function. -- -- Example 1:@@ -185,6 +194,34 @@ -- ** Building 'FunctionBuilder's +-- | A smart constructor for a 'FunctionBuilder' with that will build a function with+-- a parameter, and when the generated function is applied at that parameter+-- the function given here will be applied to the argument and the resulting monoidal+-- value will be appended to the result.+--+-- The generated builder can be passed to 'toFunction' since it is parametric+-- in its second type parameter.+--+-- 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 = deferred show+--+-- > example :: (Show a, Show b) => a -> b -> String+-- > example = toFunction (showing . showing)+--+-- >>> example True 0.33214+-- "True0.33214"+--+-- See the example in 'toFunction'.+--+-- @since 0.3.0.0+deferred :: (a -> m) -> FunctionBuilder m r (a -> r)+deferred f = FB (. f)+ -- | Create a 'FunctionBuilder' that /appends/ something to the (monoidal-) output value. -- -- This is a smart constructor for a 'FunctionBuilder'.@@ -214,8 +251,66 @@ -- -- See the example in 'toFunction'. immediate :: m -> FunctionBuilder m r r-immediate m = FB { runFunctionBuilder = ($ m) }+immediate m = FB ($ m) +-- ** Type Classes Creating 'FunctionBuilder'++-- | Types @a@ that can be turned into 'FunctionBuilder's+-- for a base monoid @m@.+--+-- This is the abstract version of 'StaticContent' and 'DynamicContent'+--+-- @since 0.1.2.0+class HasFunctionBuilder m a where+ -- | Get the function type (if any) of the builder.+ type ToFunction m a r+ type ToFunction m a r = r+ -- | Make a 'FunctionBuilder' from some value.+ toFunctionBuilder :: a -> FunctionBuilder m r (ToFunction m a r)++-- *** Specific Classes 'StaticContent' vs. 'DynamicContent'++-- | Types @a@ that can be turned into 'FunctionBuilder's+-- for a base monoid @m@.+--+-- These type can provide a function to work on the internal monoid,+--+-- They can be constructed using 'immediate'.+--+-- Of course they can incorporate information __statically known at compile time__+-- or via type class dictionaries (through singletons for instance).+--+-- For example:+--+-- > instance forall s . (KnownSymbol s) => StaticContent String (Proxy s) where+-- > addStaticContent = immediate (symbolVal (Proxy @s))+--+--+-- @since 0.2.0.0+class StaticContent m a where+ -- | Return a 'FunctionBuilder' that can work on the underlying monoid.+ addStaticContent :: a -> FunctionBuilder m next next+ default addStaticContent :: (a ~ m) => a -> FunctionBuilder m next next+ addStaticContent = immediate++-- | Types that have a 'FunctionBuilder' with a runtime @parameter@+-- for a base monoid @m@.+--+-- For example:+-- If an instance adds an @Int@ parameter, it will define this family instance:+--+-- > instance DynamicContent String (Proxy "%i") Int where+-- > addParameter _ = deferred+--+-- @since 0.2.0.0+class DynamicContent m a parameter | m a -> parameter where+ -- | Create a 'FunctionBuilder' that adds a parameter to the output function,+ -- and converts that argument to a value that can be accumulated in the+ -- resulting monoidal value.+ addParameter :: a -> FunctionBuilder m next (parameter -> next)+ default addParameter :: (a ~ (parameter -> m)) => a -> FunctionBuilder m next (parameter -> next)+ addParameter = deferred+ -- ** Modifying Parameters of 'FunctionBuilder's -- | Take away a function parameter added with 'addParameter' by /pre -/ applying it to some@@ -224,10 +319,10 @@ -- For example: -- -- > intArg :: FunctionBuilder MyMonoid a (Int -> a)--- > intArg = addParameter undefined+-- > intArg = deferred undefined -- > -- > stringArg :: FunctionBuilder MyMonoid a (String -> a)--- > stringArg = addParameter undefined+-- > stringArg = deferred undefined -- > -- > twoInt :: FunctionBuilder MyMonoid a (Int -> String -> a) -- > twoInt = intArg . stringArg@@ -259,7 +354,7 @@ -- 'Monoid' may change too, for example: -- -- > intText :: FunctionBuilder Text next (Int -> next)--- > intText = addParameter undefined+-- > intText = deferred undefined -- > -- > unpackB :: Text -> FunctionBuilder String next next -- > unpackB = immediate . unpack@@ -303,121 +398,3 @@ -- 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))---- | Types @a@ that can be turned into 'FunctionBuilder's--- for a base monoid @m@.------ This is the abstract version of 'StaticContent' and 'DynamicContent'------ @since 0.1.2.0-class HasFunctionBuilder m a where- -- | Get the function type (if any) of the builder.- type ToFunction m a r- type ToFunction m a r = r- -- | Make a 'FunctionBuilder' from some value.- toFunctionBuilder :: a -> FunctionBuilder m r (ToFunction m a r)---- | Types @a@ that one parameter to a 'FunctionBuilder's for a base monoid @m@.------ @since 0.1.2.0-class ( HasFunctionBuilder w a- , ToFunction w a r ~ (b -> r)- )- => HasParameter w a b r---- | Types @a@ that can be turned into 'FunctionBuilder's--- for a base monoid @m@.------ These type can provide a function to work on the internal monoid,------ They can be constructed using 'immediate'.------ Of course they can incorporate information __statically known at compile time__--- or via type class dictionaries (through singletons for instance).------ For example:------ > instance forall s . (KnownSymbol s) => StaticContent String (Proxy s) where--- > addStaticContent = immediate (symbolVal (Proxy @s))--------- @since 0.2.0.0-class StaticContent m a where- -- | Return a 'FunctionBuilder' that can work on the underlying monoid.- addStaticContent :: a -> FunctionBuilder m next next---- | 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)------ The generated builder can be passed to 'toFunction' since it is parametric--- in its second type parameter.------ 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'.-instance StaticContent m m where- addStaticContent m = FB { runFunctionBuilder = ($ m) }---- | Types that have a 'FunctionBuilder' with a runtime @parameter@--- for a base monoid @m@.------ For example:--- If an instance adds an @Int@ parameter, it will define this family instance:------ > instance DynamicContent String (Proxy "%i") Int where--- > addParameter _ = addParameter------ @since 0.2.0.0-class DynamicContent m a parameter | m a -> parameter where- -- | Create a 'FunctionBuilder' that adds a parameter to the output function,- -- and converts that argument to a value that can be accumulated in the- -- resulting monoidal value.- addParameter :: a -> FunctionBuilder m next (parameter -> next)----- | This instance is basically a smart constructor for a 'FunctionBuilder' with--- a parameter.------ This functions is probably equal to:------ > 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'--- 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'.-instance DynamicContent m (a -> m) a where- addParameter f = FB { runFunctionBuilder = (. f) }