packages feed

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 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) }