registry 0.3.2.0 → 0.3.2.1
raw patch · 17 files changed
+156/−136 lines, 17 files
Files
- README.md +41/−18
- registry.cabal +2/−2
- src/Data/Registry/Dot.hs +5/−5
- src/Data/Registry/Internal/Dot.hs +10/−3
- src/Data/Registry/Internal/Dynamic.hs +1/−2
- src/Data/Registry/Internal/Registry.hs +1/−1
- src/Data/Registry/Internal/Stack.hs +5/−2
- src/Data/Registry/Internal/Statistics.hs +4/−5
- src/Data/Registry/Internal/Types.hs +13/−7
- src/Data/Registry/Lift.hs +11/−4
- src/Data/Registry/Make.hs +3/−3
- src/Data/Registry/RIO.hs +3/−1
- src/Data/Registry/Registry.hs +34/−57
- src/Data/Registry/Solver.hs +1/−0
- src/Data/Registry/State.hs +2/−5
- src/Data/Registry/Statistics.hs +1/−0
- src/Data/Registry/TH.hs +19/−21
README.md view
@@ -3,42 +3,65 @@ [](https://gitter.im/etorreborre/registry?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -##### *It's functions all the way down* <img src="doc/images/unboxed-bottomup.jpg" border="0"/>+##### *It's functions all the way down* <img src="https://raw.githubusercontent.com/etorreborre/registry/main/doc/images/unboxed-bottomup.jpg" border="0"/> #### Presentation -This library provides a data structure, a `Registry`, to control the creation of functions from other functions. You can use this technique to:+This library provides a data structure, a "Registry", to control the creation of functions from other functions. You can use this technique to: - create applications out of software components ("dependency injection")- - fine tune encoders/decoders (see the [`registry-aeson`](http://github.com/etorreborre/registry-aeson) and the [`registry-messagepack`](http://github.com/etorreborre/registry-messagepack) projects)- - create composable data generators for nested datatypes (see the [`registry-hedgehog`](http://github.com/etorreborre/registry-hedgehog) and the [`registry-hedgehog-aeson`](http://github.com/etorreborre/registry-hedgehog-aeson) projects)+ - fine tune encoders/decoders (see the [`registry-aeson`][registry-aeson] and the [`registry-messagepack`][registry-messagepack] projects)+ - create composable data generators for nested datatypes (see the [`registry-hedgehog`][registry-hedgehog] and the [`registry-hedgehog-aeson`][registry-hedgehog-aeson] projects) You can watch a video presenting the main ideas behind the library [here](https://skillsmatter.com/skillscasts/12299-wire-once-rewire-twice). The following sections introduce in more details the problem that this library is addressing, the concepts behind the solution and various use-cases which can arise on real projects: - 1. [what is the problem?](doc/motivation.md)- 1. the concept of a [Registry](doc/registry.md) and the resolution algorithm+ 1. [what is the problem?][motivation]+ 1. the concept of a [Registry][registry] and the resolution algorithm 1. how does this [compare to monad transformers and effects](https://github.com/etorreborre/effects)? #### Tutorials - 1. tutorial: use a `Registry` to create [applications](doc/tutorial.md) and define components- 1. use a `Registry` to compose [Hedgehog generators](https://github.com/etorreborre/registry-hedgehog/doc/generators.md)+ 1. tutorial: use a `Registry` to create [applications][tutorial] and define components+ 1. use a `Registry` to compose [Hedgehog generators][generators]+ 1. [workshop][workshop]: implement your own simplified registry and understand the basic concepts behind it #### How-tos - 1. how to [install this library](doc/install.md)?- 1. how to do [mocking](doc/applications.md#integration)?- 1. how to [specialize some values in some contexts](doc/applications.md#context-dependent-configurations)?- 1. how to [control effects](doc/applications.md#memoization) occurring when creating a component (like a connection pool)?- 1. how to [allocate resources](doc/applications.md#resources) which must be finalized?- 1. how to [extract a dot graph from the registry](doc/dot.md) in an application?+ 1. how to [install this library][install]?+ 1. how to do [mocking][mocking]?+ 1. how to [specialize some values in some contexts][specialize]?+ 1. how to [control effects][memoization] occurring when creating a component (like a connection pool)?+ 1. how to [allocate resources][resources] which must be finalized?+ 1. how to [extract a dot graph from the registry][dot] in an application? 1. how to [interact with a library using monad transformers](https://github.com/etorreborre/registry/blob/master/test/Test/Data/Registry/MonadRandomSpec.hs)?- 1. how to [remove boilerplate](doc/boilerplate.md) due to parameter passing?- 1. how to [create a typeclass from a record of functions](doc/typeclass.md)?+ 1. how to [remove boilerplate][boilerplate] due to parameter passing?+ 1. how to [create a typeclass from a record of functions][typeclass]? #### Reference guides - 1. [main operators and functions](doc/reference.md)- 1. [implementation notes](doc/implementation.md)+ 1. [main operators and functions][reference]+ 1. [implementation notes][implementation]+++[motivation]: http://github.com/etorreborre/registry/blob/main/doc/motivation.md+[registry]: http://github.com/etorreborre/registry/blob/main/doc/registry.md+[tutorial]: http://github.com/etorreborre/registry/blob/main/doc/tutorial.md+[applications]: http://github.com/etorreborre/registry/blob/main/doc/applications.md+[mocking]: http://github.com/etorreborre/registry/blob/main/doc/applications.md#integration+[install]: http://github.com/etorreborre/registry/blob/main/doc/install.md+[specialize]: http://github.com/etorreborre/registry/blob/main/doc/applications.md#context-dependent-configurations+[memoization]: http://github.com/etorreborre/registry/blob/main/doc/applications.md#memoization+[resources]: http://github.com/etorreborre/registry/blob/main/doc/applications.md#resources+[dot]: http://github.com/etorreborre/registry/blob/main/doc/dot.md+[boilerplate]: http://github.com/etorreborre/registry/blob/main/doc/boilerplate.md+[typeclass]: http://github.com/etorreborre/registry/blob/main/doc/typeclass.md+[generators]: http://github.com/etorreborre/registry-hedgehog/blob/main/doc/tutorial.md+[registry-hedgehog]: http://github.com/etorreborre/registry-hedgehog+[registry-messagepack]: http://github.com/etorreborre/registry-messagepack+[registry-aeson]: http://github.com/etorreborre/registry-aeson+[registry-hedgehog-aeson]: http://github.com/etorreborre/registry-hedgehog-aeson+[reference]: http://github.com/etorreborre/registry/blob/main/doc/reference.md+[implementation]: http://github.com/etorreborre/registry/blob/main/doc/implementation.md+[workshop]: https://github.com/etorreborre/registry-workshop
registry.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: 63f09863d931c472e14fddae51fd7cf72418e1f06b45ee3f1e8602188d2e680a+-- hash: 5fda8684fe77f8d86c3731ff82397a6c871ebc5b3cc09d525176e0ae7d6f7e24 name: registry-version: 0.3.2.0+version: 0.3.2.1 synopsis: data structure for assembling components description: This library provides a "Registry" which is a data structure containing a list of functions and values representing dependencies in a directed acyclic graph. A `make` function can then be used to create a value of a specific type out of the registry. You can start with the [README](https://github.com/etorreborre/registry/blob/master/README.md) for a full description of the library.
src/Data/Registry/Dot.hs view
@@ -4,7 +4,7 @@ -- | -- This module provides functions to extract -- a DOT graph (https://en.wikipedia.org/wiki/DOT_(graph_description_language)--- out of a 'Registry'.+-- out of a Registry module Data.Registry.Dot ( module D, makeDot,@@ -17,13 +17,13 @@ import Data.Registry.Statistics import Protolude --- | Make a DOT graph for a specific value `a` built from the 'Registry'--- `a` is at the root of the graph and its children are values--- needed to build `a`+-- | Make a DOT graph for a specific value a built from the Registry+-- a is at the root of the graph and its children are values+-- needed to build a makeDot :: forall a ins out. (Typeable a) => Registry ins out -> Dot makeDot = toDot . operations . makeStatistics @a --- | Similar to `make` but does not check if `a` can be made out of the 'Registry'+-- | Similar to make but does not check if a can be made out of the Registry -- It returns a Left value if that's not the case makeDotEither :: forall a ins out. (Typeable a) => Registry ins out -> Either Text Dot makeDotEither r = toDot . operations <$> makeStatisticsEither @a r
src/Data/Registry/Internal/Dot.hs view
@@ -2,7 +2,7 @@ -- Nested datatype to track the resolution algorithm -- -- From this data type we can draw a graph of the full--- instantation of a value+-- instantiation of a value module Data.Registry.Internal.Dot where import Data.Hashable@@ -27,30 +27,37 @@ } deriving (Eq, Show) --- Use a State type to get the current index of a value+-- | Use a State type to get the current index of a value -- when there are values of the same type and different -- hash values type DotState = State ValuesByType +-- | List of value hashes by value type type ValuesByType = Map SomeTypeRep ValueHashes +-- | Type alias for a Hash type Hash = Int +-- | Type alias for a ValueId type ValueId = Int +-- | Type alias for a list of hashes type ValueHashes = [Hash] +-- | Type alias for a list of an edge in the graph type Edge = (Value, Value) +-- | Type alias for a list of edges type Edges = [Edge] +-- | Type alias for associating a number to a value type ValueCounter = Maybe Int -- | Make a DOT graph out of all the function applications toDot :: Operations -> Dot toDot op = let edges = makeEdges op- allValues = join $ (\(v1, v2) -> [v1, v2]) <$> edges+ allValues = edges >>= (\(v1, v2) -> [v1, v2]) valueTypes = execState (traverse countValueTypes allValues) mempty in Dot $ T.unlines $
src/Data/Registry/Internal/Dynamic.hs view
@@ -1,7 +1,6 @@ {-# LANGUAGE AllowAmbiguousTypes #-} --- |--- Utility functions to work with 'Dynamic' values+-- | Utility functions to work with 'Dynamic' values module Data.Registry.Internal.Dynamic where import Data.Dynamic
src/Data/Registry/Internal/Registry.hs view
@@ -4,7 +4,7 @@ {-# LANGUAGE UndecidableInstances #-} -- |--- Internal structure of a 'Registry' and+-- Internal structure of a Registry and -- associated functions module Data.Registry.Internal.Registry where
src/Data/Registry/Internal/Stack.hs view
@@ -1,5 +1,4 @@--- |--- Internal monad for the resolution algorithm+-- | Internal monad for the resolution algorithm -- -- - we keep some state for the list of created values -- - we collect the applied functions as "Operations"@@ -21,6 +20,7 @@ runStackWithValues :: Values -> Stack a -> Either Text a runStackWithValues vs sa = evalStateT sa (initStatistics vs) +-- | Run the stack to get a list of created values execStack :: Stack a -> Either Text Values execStack = execStackWithValues mempty @@ -33,6 +33,7 @@ evalStack :: Stack a -> Either Text Statistics evalStack = evalStackWithValues mempty +-- | Run the stack to get a the statistics, starting with some initially created values evalStackWithValues :: Values -> Stack a -> Either Text Statistics evalStackWithValues vs sa = execStateT sa (initStatistics vs) @@ -48,9 +49,11 @@ modifyValues :: (Values -> Values) -> Stack () modifyValues f = modifyStatistics (\s -> s {values = f (values s)}) +-- | Modify the current operations modifyOperations :: (Operations -> Operations) -> Stack () modifyOperations f = modifyStatistics (\s -> s {operations = f (operations s)}) +-- | Modify the current statistics modifyStatistics :: (Statistics -> Statistics) -> Stack () modifyStatistics = modify
src/Data/Registry/Internal/Statistics.hs view
@@ -1,10 +1,8 @@ {-# LANGUAGE AllowAmbiguousTypes #-} -{-- This module provides a set of statistics over the execution- of the registry. This allows to get better insights over the execution- or test that the registry is well configured--}+-- | This module provides a set of statistics over the execution+-- of the registry. This allows to get better insights over the execution+-- or test that the registry is well configured module Data.Registry.Internal.Statistics where import Data.Registry.Internal.Types@@ -45,6 +43,7 @@ } deriving (Show) +-- | Create Statistics from a list of values initStatistics :: Values -> Statistics initStatistics vs = mempty {values = vs}
src/Data/Registry/Internal/Types.hs view
@@ -61,7 +61,7 @@ describeTypeableValue :: (Typeable a) => a -> ValueDescription describeTypeableValue a = ValueDescription (showFullValueType a) Nothing --- | Show a Value from the 'Registry'+-- | Show a Value from the Registry showValue :: Value -> Text showValue = valDescriptionToText . valDescription @@ -153,7 +153,7 @@ deriving (Eq, Show) -- | Describe a 'Function' (which doesn't have a 'Show' instance)--- that can be put in the 'Registry'+-- that can be put in the Registry describeFunction :: Typeable a => a -> FunctionDescription describeFunction = uncurry FunctionDescription . showFullFunctionType @@ -181,7 +181,7 @@ hasParameters :: Function -> Bool hasParameters = isFunction . funDynTypeRep --- | A Typed value or function can be added to a 'Registry'+-- | A Typed value or function can be added to a Registry -- It is either a value, having both 'Show' and 'Typeable' information -- or a function having just 'Typeable' information data Typed a@@ -251,6 +251,7 @@ } deriving (Eq, Show, Semigroup, Monoid) +-- | Return the types of all the dependencies dependenciesTypes :: Dependencies -> DependenciesTypes dependenciesTypes (Dependencies ds) = DependenciesTypes (valueDynTypeRep <$> ds) @@ -275,7 +276,7 @@ -- For example: -- specializationPath = [App, PaymentEngine, TransactionRepository] -- specializationValue = DatabaseConfig "localhost" 5432--- This means that need to use this `DatabaseConfig` whenever+-- This means that need to use this DatabaseConfig whenever -- trying to find inputs needed to create a TransactionRepository -- if that repository is necessary to create a PaymentEngine, itself -- involved in the creation of the App@@ -285,8 +286,12 @@ } deriving (Eq, Show) +-- | List of consecutive types used when making a specific values+-- See the comments on 'Specialization' type SpecializationPath = NonEmpty SomeTypeRep +-- | Return the various specialization paths which have possibly led to the+-- creation of that value specializationPaths :: Value -> Maybe [SpecializationPath] specializationPaths v = case catMaybes $ usedSpecialization <$> (v : (unDependencies . valDependencies $ v)) of@@ -309,7 +314,7 @@ -- are part of that context, in the right order isContextApplicable :: Context -> Specialization -> Bool isContextApplicable context (Specialization specializationPath _) =- P.all (`elem` (contextTypes context)) specializationPath+ P.all (`elem` contextTypes context) specializationPath -- | Return the specifications valid in a given context applicableTo :: Specializations -> Context -> Specializations@@ -317,7 +322,7 @@ Specializations (P.filter (isContextApplicable context) ss) -- | The depth of a specialization in a context is the--- the index of the 'deepest' type of that specialization+-- the index of the "deepest" type of that specialization -- in the stack of types of that context -- is the one having its "deepest" type (in the value graph) -- the "deepest" in the current context@@ -375,9 +380,10 @@ -- | List of functions modifying some values right after they have been -- built. This enables "tweaking" the creation process with slightly--- different results. Here SomeTypeRep is the target value type 'a' and+-- different results. Here SomeTypeRep is the target value type a and newtype Modifiers = Modifiers [(SomeTypeRep, ModifierFunction)] deriving (Semigroup, Monoid) +-- | Specify a Function to use to modify a value specify by some types paths type ModifierFunction = Maybe [SpecializationPath] -> Function -- | Create a 'Function' value from a Haskell function
src/Data/Registry/Lift.hs view
@@ -90,30 +90,37 @@ -- It uses an auxiliary typeclass to count the arguments of a function data Nat = Z | S Nat +-- | Number of arguments for a given function type data NumArgs :: Nat -> Type -> Type where NAZ :: NumArgs Z a NAS :: NumArgs n b -> NumArgs (S n) (a -> b) +-- | Count the number of arguments for a function type type family CountArgs (f :: Type) :: Nat where CountArgs (a -> b) = S (CountArgs b) CountArgs result = Z +-- | Typeclass for counting the number of arguments of a function type class CNumArgs (numArgs :: Nat) (arrows :: Type) where getNA :: NumArgs numArgs arrows +-- | Instance for zero arguments instance CNumArgs Z a where getNA = NAZ +-- | Instance for n arguments instance CNumArgs n b => CNumArgs (S n) (a -> b) where getNA = NAS getNA +-- | Type family for applying a function to the last type of a function type type family Apply (f :: Type -> Type) (n :: Nat) (arrows :: Type) :: Type where Apply f (S n) (a -> b) = a -> Apply f n b Apply f Z a = f a +-- | Apply a function to the last return value of a function applyLast :: forall f fun. (Applicative f, CNumArgs (CountArgs fun) fun) => fun -> Apply f (CountArgs fun) fun applyLast = applyLast' @f (getNA :: NumArgs (CountArgs fun) fun)--applyLast' :: forall f n fun. Applicative f => NumArgs n fun -> fun -> Apply f n fun-applyLast' NAZ x = pure x-applyLast' (NAS n) f = applyLast' @f n . f+ where+ applyLast' :: forall f' n fun'. Applicative f' => NumArgs n fun' -> fun' -> Apply f' n fun'+ applyLast' NAZ x = pure x+ applyLast' (NAS n) f = applyLast' @f' n . f
src/Data/Registry/Make.hs view
@@ -36,7 +36,7 @@ import Type.Reflection import qualified Prelude (error) --- | Make an element of type 'a' out of the registry+-- | Make an element of type a out of the registry make :: forall a ins out. (Typeable a) => Registry ins out -> a make registry = -- if the registry is an unchecked one, built with +:@@ -45,12 +45,12 @@ Right a -> a Left e -> Prelude.error (toS e) --- | Make an element of type 'a' out of the registry+-- | Make an element of type a out of the registry -- and check statically that the element can be built makeSafe :: forall a ins out. (Typeable a, Solvable ins out) => Registry ins out -> a makeSafe = make --- | Make an element of type 'a' out of the registry, for a registry+-- | Make an element of type a out of the registry, for a registry -- which was possibly created with +: makeEither :: forall a ins out. (Typeable a) => Registry ins out -> Either Text a makeEither = makeEitherWithContext (Context [(someTypeRep (Proxy :: Proxy a), Nothing)])
src/Data/Registry/RIO.hs view
@@ -1,3 +1,4 @@+-- | Utilities for working with ResourceT IO module Data.Registry.RIO where import Control.Monad.Trans.Resource@@ -6,6 +7,7 @@ import Data.Registry.Solver import Protolude +-- | Type alias for ResourceT IO type RIO = ResourceT IO -- | This function must be used to run services involving a top component@@ -22,7 +24,7 @@ runResourceT (runRegistryT @a registry >>= liftIO . f) -- | This can be used if you want to insert the component creation inside--- another action managed with 'ResourceT'. Or if you want to call 'runResourceT' yourself later+-- another action managed with ResourceT. Or if you want to call runResourceT yourself later runRegistryT :: forall a ins out . (Typeable a, Contains (RIO a) out, Solvable ins out, MemoizedActions out) =>
src/Data/Registry/Registry.hs view
@@ -100,7 +100,7 @@ -- | Store an element in the registry -- Internally elements are stored as 'Dynamic' values--- The signature checks that a constructor of type 'a' can be fully+-- The signature checks that a constructor of type a can be fully -- constructed from elements of the registry before adding it register :: (Typeable a, IsSubset (Inputs a) out a) => Typed a -> Registry ins out -> Registry (Inputs a :++ ins) (Output a ': out) register = registerUnchecked@@ -122,22 +122,24 @@ Registry (Values vs) (Functions (fs <> [f])) specializations modifiers -- | Add 2 typed values together to form an initial registry-addTypedUnchecked :: (Typeable a, Typeable b, ins ~ (Inputs a :++ Inputs b), out ~ ('[Output a, Output b])) => Typed a -> Typed b -> Registry ins out+addTypedUnchecked :: (Typeable a, Typeable b, ins ~ (Inputs a :++ Inputs b), out ~ '[Output a, Output b]) => Typed a -> Typed b -> Registry ins out addTypedUnchecked (TypedValue v1) (TypedValue v2) = Registry (Values [v1, v2]) mempty mempty mempty addTypedUnchecked (TypedValue v1) (TypedFunction f2) = Registry (Values [v1]) (Functions [f2]) mempty mempty addTypedUnchecked (TypedFunction f1) (TypedValue v2) = Registry (Values [v2]) (Functions [f1]) mempty mempty addTypedUnchecked (TypedFunction f1) (TypedFunction f2) = Registry mempty (Functions [f1, f2]) mempty mempty --- | Add an element to the Registry but do not check that the inputs of 'a'+-- | Add an element to the Registry but do not check that the inputs of a -- can already be produced by the registry infixr 5 +: +-- | Prepend an element to the registry with no checks at all (+:) :: (Typeable a) => Typed a -> Registry ins out -> Registry (Inputs a :++ ins) (Output a ': out) (+:) = registerUnchecked -- Unification of +: and <+> infixr 5 <: +-- | Typeclass for appending values and or registries together, with static checks class AddRegistryLike a b c | a b -> c where (<:) :: a -> b -> c @@ -165,6 +167,7 @@ -- Unchecked unification of +: and <+> infixr 5 <+ +-- | Typeclass for appending values and or registries together, without static checks class AddRegistryUncheckedLike a b c | a b -> c where (<+) :: a -> b -> c @@ -200,6 +203,7 @@ eraseTypes :: Registry ins out -> Registry '[ERASED_TYPES] '[ERASED_TYPES] eraseTypes (Registry values functions specializations modifiers) = Registry values functions specializations modifiers +-- | Singleton type representing erased types data ERASED_TYPES -- | In case it is hard to show that the types of 2 registries align@@ -216,11 +220,11 @@ end :: Registry '[] '[] end = Registry mempty mempty mempty mempty --- | Create a value which can be added to the 'Registry'+-- | Create a value which can be added to the Registry val :: (Typeable a, Show a) => a -> Typed a val a = TypedValue (ProvidedValue (toDyn a) (describeValue a)) --- | Create a value which can be added to the 'Registry' and "lift" it to an 'Applicative' context+-- | Create a value which can be added to the Registry and "lift" it to an 'Applicative' context valTo :: forall m a. (Applicative m, Typeable a, Typeable (m a), Show a) => a -> Typed (m a) valTo a = TypedValue (liftProvidedValue @m a) @@ -228,7 +232,7 @@ liftProvidedValue :: forall m a. (Applicative m, Typeable a, Typeable (m a), Show a) => a -> Value liftProvidedValue a = ProvidedValue (toDyn (pure a :: m a)) (describeValue a) --- | Create a function which can be added to the 'Registry'+-- | Create a function which can be added to the Registry fun :: (Typeable a) => a -> Typed a fun a = TypedFunction (createFunction a) @@ -244,15 +248,10 @@ funAs :: forall m a b. (ApplyVariadic1 m a b, Typeable a, Typeable b) => a -> Typed b funAs a = fun (argsTo @m a) --- | For a given type `a` being currently built--- when a value of type `b` is required pass a specific+-- | For a given type a being currently built+-- when a value of type b is required pass a specific -- value-specialize ::- forall a b ins out.- (Typeable a, Typeable b) =>- b ->- Registry ins out ->- Registry ins out+specialize :: forall a b ins out. (Typeable a, Typeable b) => b -> Registry ins out -> Registry ins out specialize b (Registry values functions (Specializations c) modifiers) = Registry values@@ -260,12 +259,8 @@ (Specializations (Specialization (pure $ someTypeRep (Proxy :: Proxy a)) (createTypeableValue b) : c)) modifiers -specializePath ::- forall path b ins out.- (PathToTypeReps path, Typeable b) =>- b ->- Registry ins out ->- Registry ins out+-- | Specialize a function for a specific path of types+specializePath :: forall path b ins out. (PathToTypeReps path, Typeable b) => b -> Registry ins out -> Registry ins out specializePath b (Registry values functions (Specializations c) modifiers) = Registry values@@ -273,12 +268,8 @@ (Specializations (Specialization (someTypeReps (Proxy :: Proxy path)) (createTypeableValue b) : c)) modifiers -specializeVal ::- forall a b ins out.- (Typeable a, Contains a out, Typeable b, Show b) =>- b ->- Registry ins out ->- Registry ins out+-- | Specialize a value of type b when building a value of type a+specializeVal :: forall a b ins out. (Typeable a, Contains a out, Typeable b, Show b) => b -> Registry ins out -> Registry ins out specializeVal b (Registry values functions (Specializations c) modifiers) = Registry values@@ -286,12 +277,8 @@ (Specializations (Specialization (pure $ someTypeRep (Proxy :: Proxy a)) (createValue b) : c)) modifiers -specializePathVal ::- forall path b ins out.- (PathToTypeReps path, Typeable b, Show b) =>- b ->- Registry ins out ->- Registry ins out+-- | Specialize a value of type b when building a value of type a, but only when building a specific list of value types+specializePathVal :: forall path b ins out. (PathToTypeReps path, Typeable b, Show b) => b -> Registry ins out -> Registry ins out specializePathVal b (Registry values functions (Specializations c) modifiers) = Registry values@@ -299,12 +286,8 @@ (Specializations (Specialization (someTypeReps (Proxy :: Proxy path)) (createValue b) : c)) modifiers -specializeValTo ::- forall m a b ins out.- (Applicative m, Typeable a, Typeable (m b), Typeable b, Show b) =>- b ->- Registry ins out ->- Registry ins out+-- | Specialize a value of type b when building a value of type a, in the context m+specializeValTo :: forall m a b ins out. (Applicative m, Typeable a, Typeable (m b), Typeable b, Show b) => b -> Registry ins out -> Registry ins out specializeValTo b (Registry values functions (Specializations c) modifiers) = Registry values@@ -312,12 +295,8 @@ (Specializations (Specialization (pure $ someTypeRep (Proxy :: Proxy a)) (liftProvidedValue @m b) : c)) modifiers -specializePathValTo ::- forall m path b ins out.- (Applicative m, PathToTypeReps path, Typeable (m b), Typeable b, Show b) =>- b ->- Registry ins out ->- Registry ins out+-- | Specialize a value of type b when building a value of type a, in the context m, but only when building a specific list of value types+specializePathValTo :: forall m path b ins out. (Applicative m, PathToTypeReps path, Typeable (m b), Typeable b, Show b) => b -> Registry ins out -> Registry ins out specializePathValTo b (Registry values functions (Specializations c) modifiers) = Registry values@@ -337,12 +316,7 @@ -- | Once a value has been computed allow to modify it before storing it -- This keeps the same registry type-tweak ::- forall a ins out.- (Typeable a) =>- (a -> a) ->- Registry ins out ->- Registry ins out+tweak :: forall a ins out. (Typeable a) => (a -> a) -> Registry ins out -> Registry ins out tweak f (Registry values functions specializations (Modifiers mf)) = Registry values@@ -360,11 +334,7 @@ -- | Return memoized values for a monadic type -- Note that the returned Registry is in 'IO' because we are caching a value -- and this is a side-effect!-memoize ::- forall m a ins out.- (MonadIO m, Typeable a, Typeable (m a)) =>- Registry ins out ->- IO (Registry ins out)+memoize :: forall m a ins out. (MonadIO m, Typeable a, Typeable (m a)) => Registry ins out -> IO (Registry ins out) memoize (Registry values functions specializations (Modifiers mf)) = do cache <- newCache @a let modifiers = Modifiers ((someTypeRep (Proxy :: Proxy (m a)), createFunction . fetch @a @m cache) : mf)@@ -372,32 +342,39 @@ -- | Memoize *all* the output actions of a Registry when they are creating effectful components -- This relies on a helper data structure `MemoizeRegistry` tracking the types already--- memoized and a typeclass MemoizedActions going through the list of `out` types to process them--- one by one. Note that a type of the form `a` will not be memoized (only `m a`)+-- memoized and a typeclass MemoizedActions going through the list of out types to process them+-- one by one. Note that a type of the form a will not be memoized (only `m a`) memoizeAll :: forall m ins out. (MonadIO m, MemoizedActions out) => Registry ins out -> IO (Registry ins out) memoizeAll r = _unMemoizeRegistry <$> memoizeActions (startMemoizeRegistry r) +-- | Registry where all output values are memoized newtype MemoizeRegistry (todo :: [Type]) (ins :: [Type]) (out :: [Type]) = MemoizeRegistry {_unMemoizeRegistry :: Registry ins out} +-- | Prepare a Registry for memoization startMemoizeRegistry :: Registry ins out -> MemoizeRegistry out ins out startMemoizeRegistry = MemoizeRegistry +-- | Prepare a Registry for memoization for a specific list of types makeMemoizeRegistry :: forall todo ins out. Registry ins out -> MemoizeRegistry todo ins out makeMemoizeRegistry = MemoizeRegistry @todo +-- | This typeclass take an existing registry and memoize values created for the ls types class MemoizedActions ls where memoizeActions :: MemoizeRegistry ls ins out -> IO (MemoizeRegistry '[] ins out) +-- | If the list of types is empty there is nothing to memoize instance MemoizedActions '[] where memoizeActions = pure +-- | If the type represents an effectful value, memoize it and recurse with the rest instance {-# OVERLAPPING #-} (MonadIO m, Typeable a, Typeable (m a), MemoizedActions rest) => MemoizedActions (m a : rest) where memoizeActions (MemoizeRegistry r) = do r' <- memoize @m @a r memoizeActions (makeMemoizeRegistry @rest r') +-- | If the type represents a pure value, memoize the rest instance (MemoizedActions rest) => MemoizedActions (a : rest) where memoizeActions (MemoizeRegistry r) = memoizeActions (makeMemoizeRegistry @rest r)
src/Data/Registry/Solver.hs view
@@ -95,6 +95,7 @@ FindUnique a (a ': _rest) = '[] FindUnique a (_b ': rest) = FindUnique a rest +-- | Type family to remove some redundant types in a list of types type family Normalized (as :: [Type]) :: [Type] where Normalized '[] = '[] Normalized '[a] = '[a]
src/Data/Registry/State.hs view
@@ -1,5 +1,7 @@ {-# LANGUAGE AllowAmbiguousTypes #-} +-- | This module is experimental and is not added to the top level module Data.Registry.+-- It is not quite sure if we can / should support a useful state monad for passing a registry around module Data.Registry.State where import Control.Monad.Morph@@ -8,11 +10,6 @@ import Data.Registry.Registry import Data.Registry.Solver import Protolude--{-- This module is experimental and is not added to the top level module Data.Registry.- It is not quite sure if we can / should support a useful state monad for passing a registry around.--} -- | Run some registry modifications in the StateT monad runS :: (MFunctor m, Monad n) => Registry ins out -> m (StateT (Registry ins out) n) a -> m n a
src/Data/Registry/Statistics.hs view
@@ -1,6 +1,7 @@ {-# LANGUAGE AllowAmbiguousTypes #-} {-# LANGUAGE MonoLocalBinds #-} +-- | This module returns creation data about the values created when created a value of a given type module Data.Registry.Statistics ( module S, makeStatistics,
src/Data/Registry/TH.hs view
@@ -1,4 +1,23 @@ {-# LANGUAGE CPP #-}++-- | This module generates a typeclass for a given "record of functions". For this component:+-- @+-- data Logger m = Logger {+-- _info :: Text -> m ()+-- , _error :: Text -> m ()+-- }+--+-- -- makeTypeClass ''Logger generates+--+-- class WithLogger m where+-- info :: Text -> m ()+-- error :: Text -> m ()+--+-- -- This requires the import of `Data.Generics.Product.Typed` from `generic-lens`+-- instance HasType (Logger m) s => WithLogger (ReaderT s m) where+-- info t = ReaderT (\l -> _info (getTyped l) t)+-- error t = ReaderT (\l -> _error (getType l) t)+-- @ module Data.Registry.TH ( TypeclassOptions, makeTypeclass,@@ -10,27 +29,6 @@ import Language.Haskell.TH import Language.Haskell.TH.Syntax import Protolude hiding (Type)--{-- This module generates a typeclass for a given "record of functions". For this component:--data Logger m = Logger {- _info :: Text -> m ()-, _error :: Text -> m ()-}---- `makeTypeClass ''Logger` generates--class WithLogger m where- info :: Text -> m ()- error :: Text -> m ()---- This requires the import of `Data.Generics.Product.Typed` from `generic-lens`-instance HasType (Logger m) s => WithLogger (ReaderT s m) where- info t = ReaderT (\l -> _info (getTyped l) t)- error t = ReaderT (\l -> _error (getType l) t)---} -- | Create the haskell code presented in the module description makeTypeclass :: Name -> DecsQ