packages feed

dep-t 0.6.7.0 → 0.6.8.0

raw patch · 9 files changed

+376/−290 lines, 9 filesPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

- Dep.Env: instance Dep.Env.Phased (Dep.Env.InductiveEnv rs)
- Dep.Env: instance forall k1 k2 (a :: k1 -> *) (f :: k1 -> *) (f' :: k1 -> *) (fieldsa :: k2 -> *) (fields :: k2 -> *) (fields' :: k2 -> *) (metaData :: GHC.Generics.Meta) (metaCons :: GHC.Generics.Meta). Dep.Env.GLiftA2Phase a f f' fieldsa fields fields' => Dep.Env.GLiftA2Phase a f f' (GHC.Generics.D1 metaData (GHC.Generics.C1 metaCons fieldsa)) (GHC.Generics.D1 metaData (GHC.Generics.C1 metaCons fields)) (GHC.Generics.D1 metaData (GHC.Generics.C1 metaCons fields'))
- Dep.Env: instance forall k1 k2 (a :: k1 -> *) (f :: k1 -> *) (f' :: k1 -> *) (lefta :: k2 -> *) (left :: k2 -> *) (left' :: k2 -> *) (righta :: k2 -> *) (right :: k2 -> *) (right' :: k2 -> *). (Dep.Env.GLiftA2Phase a f f' lefta left left', Dep.Env.GLiftA2Phase a f f' righta right right') => Dep.Env.GLiftA2Phase a f f' (lefta GHC.Generics.:*: righta) (left GHC.Generics.:*: right) (left' GHC.Generics.:*: right')
- Dep.Env: instance forall k1 k2 (bean :: k1) (a :: k1 -> *) (f :: k1 -> *) (f' :: k1 -> *) (metaSel :: GHC.Generics.Meta). Data.Typeable.Internal.Typeable bean => Dep.Env.GLiftA2Phase a f f' (GHC.Generics.S1 metaSel (GHC.Generics.Rec0 (a bean))) (GHC.Generics.S1 metaSel (GHC.Generics.Rec0 (f bean))) (GHC.Generics.S1 metaSel (GHC.Generics.Rec0 (f' bean)))
- Dep.Env: instance forall k1 k2 (bean :: k1) (h :: k1 -> *) (g :: k1 -> *) (metaSel :: GHC.Generics.Meta). Data.Typeable.Internal.Typeable bean => Dep.Env.GTraverseH h g (GHC.Generics.S1 metaSel (GHC.Generics.Rec0 (h bean))) (GHC.Generics.S1 metaSel (GHC.Generics.Rec0 (g bean)))
- Dep.Env: instance forall k1 k2 (h :: k1 -> *) (g :: k1 -> *) (fields :: k2 -> *) (fields' :: k2 -> *) (metaData :: GHC.Generics.Meta) (metaCons :: GHC.Generics.Meta). Dep.Env.GTraverseH h g fields fields' => Dep.Env.GTraverseH h g (GHC.Generics.D1 metaData (GHC.Generics.C1 metaCons fields)) (GHC.Generics.D1 metaData (GHC.Generics.C1 metaCons fields'))
- Dep.Env: instance forall k1 k2 (h :: k1 -> *) (g :: k1 -> *) (left :: k2 -> *) (left' :: k2 -> *) (right :: k2 -> *) (right' :: k2 -> *). (Dep.Env.GTraverseH h g left left', Dep.Env.GTraverseH h g right right') => Dep.Env.GTraverseH h g (left GHC.Generics.:*: right) (left' GHC.Generics.:*: right')
+ Dep.Env: instance Dep.Phases.Phased (Dep.Env.InductiveEnv rs)
+ Dep.Injects: type family InjectsAll rs_ m e
+ Dep.Phases: class Phased (env_ :: (Type -> Type) -> (Type -> Type) -> Type)
+ Dep.Phases: instance forall k1 k2 (a :: k1 -> *) (f :: k1 -> *) (f' :: k1 -> *) (fieldsa :: k2 -> *) (fields :: k2 -> *) (fields' :: k2 -> *) (metaData :: GHC.Generics.Meta) (metaCons :: GHC.Generics.Meta). Dep.Phases.GLiftA2Phase a f f' fieldsa fields fields' => Dep.Phases.GLiftA2Phase a f f' (GHC.Generics.D1 metaData (GHC.Generics.C1 metaCons fieldsa)) (GHC.Generics.D1 metaData (GHC.Generics.C1 metaCons fields)) (GHC.Generics.D1 metaData (GHC.Generics.C1 metaCons fields'))
+ Dep.Phases: instance forall k1 k2 (a :: k1 -> *) (f :: k1 -> *) (f' :: k1 -> *) (lefta :: k2 -> *) (left :: k2 -> *) (left' :: k2 -> *) (righta :: k2 -> *) (right :: k2 -> *) (right' :: k2 -> *). (Dep.Phases.GLiftA2Phase a f f' lefta left left', Dep.Phases.GLiftA2Phase a f f' righta right right') => Dep.Phases.GLiftA2Phase a f f' (lefta GHC.Generics.:*: righta) (left GHC.Generics.:*: right) (left' GHC.Generics.:*: right')
+ Dep.Phases: instance forall k1 k2 (bean :: k1) (a :: k1 -> *) (f :: k1 -> *) (f' :: k1 -> *) (metaSel :: GHC.Generics.Meta). Data.Typeable.Internal.Typeable bean => Dep.Phases.GLiftA2Phase a f f' (GHC.Generics.S1 metaSel (GHC.Generics.Rec0 (a bean))) (GHC.Generics.S1 metaSel (GHC.Generics.Rec0 (f bean))) (GHC.Generics.S1 metaSel (GHC.Generics.Rec0 (f' bean)))
+ Dep.Phases: instance forall k1 k2 (bean :: k1) (h :: k1 -> *) (g :: k1 -> *) (metaSel :: GHC.Generics.Meta). Data.Typeable.Internal.Typeable bean => Dep.Phases.GTraverseH h g (GHC.Generics.S1 metaSel (GHC.Generics.Rec0 (h bean))) (GHC.Generics.S1 metaSel (GHC.Generics.Rec0 (g bean)))
+ Dep.Phases: instance forall k1 k2 (h :: k1 -> *) (g :: k1 -> *) (fields :: k2 -> *) (fields' :: k2 -> *) (metaData :: GHC.Generics.Meta) (metaCons :: GHC.Generics.Meta). Dep.Phases.GTraverseH h g fields fields' => Dep.Phases.GTraverseH h g (GHC.Generics.D1 metaData (GHC.Generics.C1 metaCons fields)) (GHC.Generics.D1 metaData (GHC.Generics.C1 metaCons fields'))
+ Dep.Phases: instance forall k1 k2 (h :: k1 -> *) (g :: k1 -> *) (left :: k2 -> *) (left' :: k2 -> *) (right :: k2 -> *) (right' :: k2 -> *). (Dep.Phases.GTraverseH h g left left', Dep.Phases.GTraverseH h g right right') => Dep.Phases.GTraverseH h g (left GHC.Generics.:*: right) (left' GHC.Generics.:*: right')
+ Dep.Phases: liftA2H :: forall (a :: Type -> Type) (f :: Type -> Type) (f' :: Type -> Type) m. (Phased env_, Typeable a, Typeable f, Typeable f', Typeable m, Generic (env_ a m), Generic (env_ f m), Generic (env_ f' m), GLiftA2Phase a f f' (Rep (env_ a m)) (Rep (env_ f m)) (Rep (env_ f' m))) => (forall x. Typeable x => a x -> f x -> f' x) -> env_ a m -> env_ f m -> env_ f' m
+ Dep.Phases: liftA2Phase :: forall (a :: Type -> Type) (f' :: Type -> Type) (f :: Type -> Type) (g :: Type -> Type) (m :: Type -> Type) env_. (Phased env_, Typeable a, Typeable f, Typeable f', Typeable g, Typeable m) => (forall x. Typeable x => a x -> f x -> f' x) -> env_ (Compose a g) m -> env_ (Compose f g) m -> env_ (Compose f' g) m
+ Dep.Phases: liftAH :: forall deps_ phases phases' m. (Phased deps_, Typeable phases, Typeable phases', Typeable m) => (forall x. Typeable x => phases x -> phases' x) -> deps_ phases m -> deps_ phases' m
+ Dep.Phases: mapPhase :: forall (f :: Type -> Type) (f' :: Type -> Type) (g :: Type -> Type) (m :: Type -> Type) env_. (Phased env_, Typeable f, Typeable f', Typeable g, Typeable m) => (forall x. Typeable x => f x -> f' x) -> env_ (Compose f g) m -> env_ (Compose f' g) m
+ Dep.Phases: pullPhase :: forall (f :: Type -> Type) (g :: Type -> Type) (m :: Type -> Type) env_. (Phased env_, Applicative f, Typeable f, Typeable g, Typeable m) => env_ (Compose f g) m -> f (env_ g m)
+ Dep.Phases: traverseH :: forall (h :: Type -> Type) (f :: Type -> Type) (g :: Type -> Type) (m :: Type -> Type). (Phased env_, Applicative f, Typeable f, Typeable g, Typeable h, Typeable m, Generic (env_ h m), Generic (env_ g m), GTraverseH h g (Rep (env_ h m)) (Rep (env_ g m))) => (forall x. Typeable x => h x -> f (g x)) -> env_ h m -> f (env_ g m)

Files

CHANGELOG.md view
@@ -1,20 +1,33 @@ # Revision history for dep-t -## 0.6.7+## 0.6.8.0 +* Un-deprecated `HasAll`. Turns out that it's actually useful :facepalm:++* Added `InjectsAll`.++* Deprecated `DemotableFieldNames` `demoteFieldNames` `mapPhaseWithFieldNames`++* Deprecated `skipPhase`. Instead of using both  `skipPhase` and `bindPhase`, it's +  better to use only the latter, for consistency.++* Moved main `Phased` definitions to `Dep.Phased`, re-exporting them from `Dep.Env`.++## 0.6.7.0+ * Deprecated `HasAll`.  * Added `Call` pattern synonym to `Dep.Has`.  * Added tuple instances for `Has` and `Injects`. -## 0.6.6+## 0.6.6.0  * Added `Dep.Phases`.   * Added `Dep.Injects`. -## 0.6.5+## 0.6.5.0  * Deprecated `Constructor`, `fixEnv` and the `Accum-` counterparts from `Dep.Env`.   @@ -30,7 +43,7 @@  * Shifted `DepT`-specific parts of the readme to `Control.Monad.Dep`. -## 0.6.4+## 0.6.4.0  * Added AccumConstructor, a generalization of Constructor that threads a   monoidal summary along with the environment record.@@ -45,7 +58,7 @@   TraversableT and ApplicativeT from the "barbies" library to implement Phased.   But most of the time you would use Phased's own generic deriving anyway. -## 0.6.3+## 0.6.3.0  * Now the Bare typeclass doesn't recurse inside Identity or Const. @@ -60,7 +73,7 @@   functions did not compile. This led to unnecessary code duplication. Stopping   at Identity means Bare never touches the type variable and doesn't get stuck. -## 0.6.2+## 0.6.2.0  * Moved `fromBare` and `toBare` from dep-t-dynamic. 
README.md view
@@ -42,8 +42,8 @@ ```mermaid   graph TD;       Dep.Env-->Dep.Has;-      Dep.Constructor-->Dep.Env;-      Dep.Tagged;+      Dep.Env-->Dep.Phases;+      Dep.Constructor-->Dep.Phases;       Control.Monad.Dep.Class-->Control.Monad.Reader;       Control.Monad.Dep-->Control.Monad.Reader;       Control.Monad.Dep-->Control.Monad.Dep.Class;@@ -52,8 +52,8 @@ - __Dep.Has__ provides a generic `Has` typeclass for locating dependencies in an environment. Usually, component implementations import this module. - __Dep.Env__ complements __Dep.Has__ with helpers for building dependency injection environments. Usually, only the [composition root](https://stackoverflow.com/questions/6277771/what-is-a-composition-root-in-the-context-of-dependency-injection) of the application imports this module.-- __Dep.Tagged__ is a helper for disambiguating dependencies in __Dep.Env__ environments.-- __Dep.Constructor__ enables fixpoint-based dependency injection in __Dep.Env__ environments. See [this thread in the Haskell Discourse](https://discourse.haskell.org/t/dependency-injection-fixed-points-and-monoidal-accumulators/5557) for an example.+- __Dep.Phases__ provides a `Phased` typeclass for DI environments which go through a sequence of `Applicative` phases during construction. Also a special [`QualifiedDo`](https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/qualified_do.html#extension-QualifiedDo) notation for phases.+- __Dep.Constructor__ enables fixpoint-based dependency injection in `Phased` environments. See [this thread in the Haskell Discourse](https://discourse.haskell.org/t/dependency-injection-fixed-points-and-monoidal-accumulators/5557) for an example. - __Control.Monad.Dep__ provides the `DepT` monad transformer, a variant of `ReaderT`. You either want to use this or __Dep.Constructor__ in your composition root, but not both. - __Control.Monad.Dep.Class__ is an extension of `MonadReader`, useful to program against both `ReaderT` and `DepT`. 
dep-t.cabal view
@@ -1,7 +1,7 @@ cabal-version:       3.0  name:                dep-t-version:             0.6.7.0+version:             0.6.8.0 synopsis:            Dependency injection for records-of-functions. description:         Put all your functions in the environment record! Let all                      your functions read from the environment record! No favorites!
lib/Dep/Constructor.hs view
@@ -79,10 +79,11 @@ import Data.Function (fix) import Data.Kind import Data.Typeable-import Dep.Env hiding (AccumConstructor, Constructor, accumConstructor, constructor, fixEnv, fixEnvAccum)+import Dep.Phases import Control.Category (Category) import Control.Category qualified import Control.Arrow+import Data.Functor.Identity  -- | A phase with the effect of \"constructing each component by reading its -- dependencies from a completed environment\". It should be the final phase.
lib/Dep/Env.hs view
@@ -128,6 +128,7 @@ import GHC.Generics qualified as G import GHC.Records import GHC.TypeLits+import Dep.Phases hiding ((>>=), (>>))  -- $setup --@@ -261,243 +262,10 @@   WithLeftResult_ ('Just ls) right r = 'Just ls   WithLeftResult_ Nothing right r = GFindFieldByType right r --------- Managing Phases --- see also https://github.com/haskell/cabal/issues/7394#issuecomment-861767980---- | Class of 2-parameter environments for which the first parameter @h@ wraps--- each field and corresponds to phases in the construction of the environment,--- and the second parameter @m@ is the effect monad used by each component.------ @h@ will typically be a composition of applicative functors, each one--- representing a phase. We advance through the phases by \"pulling out\" the--- outermost phase and running it in some way, until we are are left with a--- 'Constructor' phase, which we can remove using 'fixEnv'.------ 'Phased' resembles [FunctorT, TraversableT and--- ApplicativeT](https://hackage.haskell.org/package/barbies-2.0.3.0/docs/Data-Functor-Transformer.html)--- from the [barbies](https://hackage.haskell.org/package/barbies) library,--- although 'Phased' instances /can't/ be written in terms of them because of the extra 'Typeable' constraints.-type Phased :: ((Type -> Type) -> (Type -> Type) -> Type) -> Constraint-class Phased (env_ :: (Type -> Type) -> (Type -> Type) -> Type) where-  -- | Used to implement 'pullPhase' and 'mapPhase',  typically you should use those functions instead.-  traverseH ::-    forall-      (h :: Type -> Type)-      (f :: Type -> Type)-      (g :: Type -> Type)-      (m :: Type -> Type).-    ( -      Applicative f,-      Typeable f,-      Typeable g,-      Typeable h,-      Typeable m-    ) =>-    -- | Transform to be applied to each field.-    (forall x. Typeable x => h x -> f (g x)) ->-    env_ h m ->-    f (env_ g m)-  default traverseH ::-    forall-      (h :: Type -> Type)-      (f :: Type -> Type)-      (g :: Type -> Type)-      (m :: Type -> Type).-    ( Applicative f,-      Typeable f,-      Typeable g,-      Typeable h,-      Typeable m,-      G.Generic (env_ h m),-      G.Generic (env_ g m),-      GTraverseH h g (G.Rep (env_ h m)) (G.Rep (env_ g m))-    ) =>-    -- | Transform to be applied to each field.-    (forall x. Typeable x => h x -> f (g x)) ->-    env_ h m ->-    f (env_ g m)-  traverseH t env = G.to <$> gTraverseH t (G.from env)--  -- | Used to implement 'liftA2Phase', typically you should use that function instead.-  liftA2H ::-    forall (a :: Type -> Type) (f :: Type -> Type) (f' :: Type -> Type) (m :: Type -> Type).-    ( Typeable a,-      Typeable f,-      Typeable f',-      Typeable m-    ) =>-    -- | Binary operation to combine corresponding fields.-    (forall x. Typeable x => a x -> f x -> f' x) ->-    env_ a m ->-    env_ f m ->-    env_ f' m-  default liftA2H ::-    forall (a :: Type -> Type) (f :: Type -> Type) (f' :: Type -> Type) m.-    ( Typeable a,-      Typeable f,-      Typeable f',-      Typeable m,-      G.Generic (env_ a m),-      G.Generic (env_ f m),-      G.Generic (env_ f' m),-      GLiftA2Phase a f f' (G.Rep (env_ a m)) (G.Rep (env_ f m)) (G.Rep (env_ f' m))-    ) =>-    -- | Transform to be applied to each field.-    (forall x. Typeable x => a x -> f x -> f' x) ->-    env_ a m ->-    env_ f m ->-    env_ f' m-  liftA2H f enva env = G.to (gLiftA2Phase f (G.from enva) (G.from env))---- | Slightly less powerful version of 'traverseH'.-liftAH ::-  forall deps_ phases phases' m.-  (Phased deps_, Typeable phases, Typeable phases', Typeable m) =>-  (forall x. Typeable x => phases x -> phases' x) ->-  deps_ phases m ->-  deps_ phases' m-liftAH tweak =-  runIdentity . traverseH (Identity . tweak)---- | Take the outermost phase wrapping each component and \"pull it outwards\",--- aggregating the phase's applicative effects.------ >>> :{---  newtype Foo d = Foo {foo :: String -> d ()} deriving Generic---  makeIOFoo :: MonadIO m => Foo m---  makeIOFoo = Foo (liftIO . putStrLn)---  env :: InductiveEnv '[Foo] (IO `Compose` Constructor (InductiveEnv '[Foo] Identity IO)) IO---  env = EmptyEnv---      & AddDep @Foo (putStrLn "io phase" `bindPhase` \() -> constructor (\_ -> makeIOFoo))---  ioOutside :: IO (InductiveEnv '[Foo] (Constructor (InductiveEnv '[Foo] Identity IO)) IO)---  ioOutside = pullPhase env--- :}-pullPhase ::-  forall (f :: Type -> Type) (g :: Type -> Type) (m :: Type -> Type) env_.-  (Phased env_, Applicative f, Typeable f, Typeable g, Typeable m) =>-  env_ (Compose f g) m ->-  -- Environment with the outer 'Applicative' layer pulled outward.-  f (env_ g m)--- f first to help annotate the phase-pullPhase = traverseH @env_ getCompose---- | Modify the outermost phase wrapping each component.------ >>> :{---  newtype Foo d = Foo {foo :: String -> d ()} deriving Generic---  makeIOFoo :: MonadIO m => Foo m---  makeIOFoo = Foo (liftIO . putStrLn)---  env :: InductiveEnv '[Foo] ((,) Int `Compose` Constructor String) IO---  env = EmptyEnv---      & AddDep @Foo ((2,()) `bindPhase` \() -> constructor (\_ -> makeIOFoo))---  env' :: InductiveEnv '[Foo] ((,) String `Compose` Constructor String) IO---  env' = mapPhase (\(n,x) -> (show n,x)) env--- :}-mapPhase ::-  forall (f :: Type -> Type) (f' :: Type -> Type) (g :: Type -> Type) (m :: Type -> Type) env_.-  (Phased env_, Typeable f, Typeable f', Typeable g, Typeable m) =>-  -- | Transform to be applied to each field.-  (forall x. Typeable x => f x -> f' x) ->-  env_ (Compose f g) m ->-  env_ (Compose f' g) m--- f' first to help annotate the *target* of the transform?-mapPhase f env = runIdentity $ traverseH @env_ (\(Compose fg) -> Identity (Compose (f fg))) env---- | Combine two environments with a function that works on their outermost phases.-liftA2Phase ::-  forall (a :: Type -> Type) (f' :: Type -> Type) (f :: Type -> Type) (g :: Type -> Type) (m :: Type -> Type) env_.-  (Phased env_, Typeable a, Typeable f, Typeable f', Typeable g, Typeable m) =>-  -- | Binary operation to combine corresponding fields.-  (forall x. Typeable x => a x -> f x -> f' x) ->-  env_ (Compose a g) m ->-  env_ (Compose f g) m ->-  env_ (Compose f' g) m--- f' first to help annotate the *target* of the transform?-liftA2Phase f = liftA2H @env_ (\(Compose fa) (Compose fg) -> Compose (f fa fg))--class GTraverseH h g env env' | env -> h, env' -> g where-  gTraverseH :: Applicative f => (forall x. Typeable x => h x -> f (g x)) -> env x -> f (env' x)--instance-  (GTraverseH h g fields fields') =>-  GTraverseH-    h-    g-    (G.D1 metaData (G.C1 metaCons fields))-    (G.D1 metaData (G.C1 metaCons fields'))-  where-  gTraverseH t (G.M1 (G.M1 fields)) =-    G.M1 . G.M1 <$> gTraverseH @h @g t fields--instance-  ( GTraverseH h g left left',-    GTraverseH h g right right'-  ) =>-  GTraverseH h g (left G.:*: right) (left' G.:*: right')-  where-  gTraverseH t (left G.:*: right) =-    let left' = gTraverseH @h @g t left-        right' = gTraverseH @h @g t right-     in liftA2 (G.:*:) left' right'--instance-  Typeable bean =>-  GTraverseH-    h-    g-    (G.S1 metaSel (G.Rec0 (h bean)))-    (G.S1 metaSel (G.Rec0 (g bean)))-  where-  gTraverseH t (G.M1 (G.K1 hbean)) =-    G.M1 . G.K1 <$> t hbean--------class GLiftA2Phase a f f' enva env env' | enva -> a, env -> f, env' -> f' where-  gLiftA2Phase :: (forall r. Typeable r => a r -> f r -> f' r) -> enva x -> env x -> env' x--instance-  GLiftA2Phase a f f' fieldsa fields fields' =>-  GLiftA2Phase-    a-    f-    f'-    (G.D1 metaData (G.C1 metaCons fieldsa))-    (G.D1 metaData (G.C1 metaCons fields))-    (G.D1 metaData (G.C1 metaCons fields'))-  where-  gLiftA2Phase f (G.M1 (G.M1 fieldsa)) (G.M1 (G.M1 fields)) =-    G.M1 (G.M1 (gLiftA2Phase @a @f @f' f fieldsa fields))--instance-  ( GLiftA2Phase a f f' lefta left left',-    GLiftA2Phase a f f' righta right right'-  ) =>-  GLiftA2Phase a f f' (lefta G.:*: righta) (left G.:*: right) (left' G.:*: right')-  where-  gLiftA2Phase f (lefta G.:*: righta) (left G.:*: right) =-    let left' = gLiftA2Phase @a @f @f' f lefta left-        right' = gLiftA2Phase @a @f @f' f righta right-     in (G.:*:) left' right'--instance-  Typeable bean =>-  GLiftA2Phase-    a-    f-    f'-    (G.S1 metaSel (G.Rec0 (a bean)))-    (G.S1 metaSel (G.Rec0 (f bean)))-    (G.S1 metaSel (G.Rec0 (f' bean)))-  where-  gLiftA2Phase f (G.M1 (G.K1 abean)) (G.M1 (G.K1 fgbean)) =-    G.M1 (G.K1 (f abean fgbean))- -- | Class of 2-parameter environments for which it's possible to obtain the -- names of each field as values.+{-# DEPRECATED DemotableFieldNames "using the field names directly is usually a bad idea" #-} type DemotableFieldNames :: ((Type -> Type) -> (Type -> Type) -> Type) -> Constraint class DemotableFieldNames env_ where   demoteFieldNamesH :: (forall x. String -> h String x) -> env_ (h String) m@@ -511,6 +279,7 @@  -- | Bring down the field names of the environment to the term level and store -- them in the accumulator of "Data.Functor.Constant".+{-# DEPRECATED demoteFieldNames "using the field names directly is usually a bad idea" #-} demoteFieldNames :: forall env_ m. DemotableFieldNames env_ => env_ (Constant String) m demoteFieldNames = demoteFieldNamesH Constant @@ -542,6 +311,7 @@ -- A typical usage is modifying a \"parsing the configuration\" phase so that -- each component looks into a different section of the global configuration -- field.+{-# DEPRECATED mapPhaseWithFieldNames "using the field names directly is usually a bad idea" #-} mapPhaseWithFieldNames ::   forall (f :: Type -> Type) (f' :: Type -> Type) (g :: Type -> Type) (m :: Type -> Type) env_.   ( Phased env_,@@ -581,6 +351,10 @@ --      pure 2 `bindPhase` \i2 -> --      Identity (i1 + i2) -- :}+--+--+-- A possible alternative is 'Dep.Phases.>>=' from "Dep.Phases" used in+-- combination with @-XQualifiedDo@. bindPhase :: forall f g a b. Functor f => f a -> (a -> g b) -> Compose f g b -- f as first type parameter to help annotate the current phase bindPhase f k = Compose (f <&> k)@@ -595,6 +369,7 @@ --      skipPhase $ --      Identity 1 -- :}+{-# DEPRECATED skipPhase "use pure () in combination with bindPhase" #-} skipPhase :: forall f g a. Applicative f => g a -> Compose f g a -- f as first type parameter to help annotate the current phase skipPhase g = Compose (pure g)
lib/Dep/Has.hs view
@@ -18,66 +18,51 @@ {-# LANGUAGE ViewPatterns #-}  -- | This module provides a general-purpose 'Has' class favoring a style in--- which the components of the environment, instead of being bare functions,--- are themselves records or newtypes containing functions.------ In this style, the functions that are \"invoked\" from the environment are--- actually record field selectors. These selectors guide the 'Has' class to--- find the correct records in the environment.+-- which the components of the dependency injection environment, instead of+-- being bare functions, are themselves records or newtypes containing+-- functions: -- -- >>> :{ --  newtype Logger d = Logger {log :: String -> d ()}---  instance Dep Logger where---    type DefaultFieldName Logger = "logger"---  -----  data Repository d = Repository---    { select :: String -> d [Int],---      insert :: [Int] -> d ()---    }---  instance Dep Repository where---    type DefaultFieldName Repository = "repository"---  --+--  data Repository d = Repository { select :: String -> d [Int], insert :: [Int] -> d () } --  newtype Controller d = Controller {serve :: Int -> d String}---  instance Dep Controller where---    type DefaultFieldName Controller = "controller"---  --+--  -- A dependency injection environment that contains the components. --  type Deps :: (Type -> Type) -> Type --  data Deps m = Deps --    { logger :: Logger m, --      repository :: Repository m, --      controller :: Controller m --    }---  instance Has Logger m (Deps m)---  instance Has Repository m (Deps m)---  instance Has Controller m (Deps m)+--  instance Has Logger m (Deps m) where dep Deps {logger} = logger+--  instance Has Repository m (Deps m) where dep Deps {repository} = repository+--  instance Has Controller m (Deps m) where dep Deps {controller} = controller --  :} ----- 'Has' can be used in combination with 'MonadDep', like this: ----- >>> :{---  mkController :: MonadDep [Has Logger, Has Repository] d env m => Controller m---  mkController =---    Controller \url ->---      useEnv \(asCall -> call) -> do---        call log "I'm going to insert in the db!"---        call select "select * from ..."---        call insert [1, 2, 3, 4]---        return "view"--- :}+-- In this style, the functions that are \"invoked\" from the environment are+-- actually record field selectors. These selectors guide the 'Has' class to+-- find the correct components in the environment: ----- 'Has' can also be used independently of 'MonadReader' or 'MonadDep'. Here--- for example the environment is passed as a plain function argument, and @m@--- doesn't have any constraint other than 'Monad': -- -- >>> :{---  mkController' :: (Monad m, Has Logger m deps, Has Repository m deps) => deps -> Controller m---  mkController' (asCall -> call) =+--  makeController :: (Has Logger m deps, Has Repository m deps, Monad m) => deps -> Controller m+--  makeController (asCall -> call) = --    Controller \url -> do --      call log "I'm going to insert in the db!" --      call select "select * from ..." --      call insert [1, 2, 3, 4] --      return "view" -- :}+--+--+-- By convention, the DI environment parameter is usually called @deps@. Notice+-- also the use of the (optional) 'asCall' helper.+--+-- If we regard @makeController@ above as a component constructor, 'Has' lets us+-- avoid having to define separate positional parameters for each dependency of the +-- constructor. Not only that: it also avoids us having to give names to those +-- parameters, and even having to mention their types (because they are+-- implicit in the record field selectors). module Dep.Has   ( -- * A general-purpose Has     Has (..),@@ -121,8 +106,6 @@ -- | When partially applied to a type-level list @rs_@ of parametrizable records-of-functions, -- produces a 2-place constraint saying that the environment @e@ has all the -- records @rs_@ with effect monad @m@.-{-# DEPRECATED HasAll "Use All from sop-core along with separate Has applications" #-}- type HasAll :: [(Type -> Type) -> Type] -> (Type -> Type) -> Type -> Constraint type family HasAll rs_ m e where   HasAll '[] m e = ()@@ -161,9 +144,9 @@ -- Each function field is then a \"method\". And the record field selectors are functions -- which take the component and return the method corresponding to that field. ----- Given a dependency injection context, 'asCall' produces a reusable helper+-- Given a dependency injection environment, 'asCall' produces a reusable helper -- that returns the the method corresponding to a field selector, on the condition that--- the required 'Has' instance exists for the selectors' record.+-- the selector's record actually exists in the environment. -- -- >>> :{ --  data SomeRecord m = SomeRecord { someSelector :: String -> m () }
lib/Dep/Injects.hs view
@@ -20,6 +20,7 @@ module Dep.Injects   ( -- * General-purpose injector.     Injects (..),+    InjectsAll   ) where @@ -65,3 +66,10 @@  instance (Monoid a, Monoid b, Monoid c) => Injects r_ m (a, b, c, r_ m) where   inject r = (mempty, mempty, mempty, r)+++-- | Mirror image of 'Dep.Has.HasAll'.+type InjectsAll :: [(Type -> Type) -> Type] -> (Type -> Type) -> Type -> Constraint+type family InjectsAll rs_ m e where+  InjectsAll '[] m e = ()+  InjectsAll (r_ : rs_) m e = (Injects r_ m e, InjectsAll rs_ m e)
lib/Dep/Phases.hs view
@@ -1,4 +1,29 @@+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DefaultSignatures #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE StandaloneKindSignatures #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE ViewPatterns #-} module Dep.Phases (+    -- * Managing phases+    Phased (..),+    liftAH,+    pullPhase,+    mapPhase,+    liftA2Phase,     -- * Qualified do-notation for building phases     -- $warning     (>>=), @@ -7,12 +32,286 @@     Compose (..),     ) where ++import Control.Applicative+import Data.Coerce+import Data.Function (fix)+import Data.Functor (($>), (<&>))+import Data.Functor.Identity+import Data.Kind+import Data.Proxy+import Data.String+import Data.Type.Equality (type (==))+import Data.Typeable+import GHC.Generics qualified as G+import GHC.Records+import GHC.TypeLits import Data.Functor.Compose-import Prelude (Functor, (<$>), (<$))+import Prelude (Functor, (<$>), (<$), ($), (.)) +--+--+-- Managing Phases++-- see also https://github.com/haskell/cabal/issues/7394#issuecomment-861767980++-- | Class of 2-parameter environments for which the first parameter @h@ wraps+-- each field and corresponds to phases in the construction of the environment,+-- and the second parameter @m@ is the effect monad used by each component.+--+-- @h@ will typically be a composition of applicative functors, each one+-- representing a phase. We advance through the phases by \"pulling out\" the+-- outermost phase and running it in some way, until we are are left with a+-- 'Constructor' phase, which we can remove using 'fixEnv'.+--+-- 'Phased' resembles [FunctorT, TraversableT and+-- ApplicativeT](https://hackage.haskell.org/package/barbies-2.0.3.0/docs/Data-Functor-Transformer.html)+-- from the [barbies](https://hackage.haskell.org/package/barbies) library,+-- although 'Phased' instances /can't/ be written in terms of them because of the extra 'Typeable' constraints.+type Phased :: ((Type -> Type) -> (Type -> Type) -> Type) -> Constraint+class Phased (env_ :: (Type -> Type) -> (Type -> Type) -> Type) where+  -- | Used to implement 'pullPhase' and 'mapPhase',  typically you should use those functions instead.+  traverseH ::+    forall+      (h :: Type -> Type)+      (f :: Type -> Type)+      (g :: Type -> Type)+      (m :: Type -> Type).+    ( +      Applicative f,+      Typeable f,+      Typeable g,+      Typeable h,+      Typeable m+    ) =>+    -- | Transform to be applied to each field.+    (forall x. Typeable x => h x -> f (g x)) ->+    env_ h m ->+    f (env_ g m)+  default traverseH ::+    forall+      (h :: Type -> Type)+      (f :: Type -> Type)+      (g :: Type -> Type)+      (m :: Type -> Type).+    ( Applicative f,+      Typeable f,+      Typeable g,+      Typeable h,+      Typeable m,+      G.Generic (env_ h m),+      G.Generic (env_ g m),+      GTraverseH h g (G.Rep (env_ h m)) (G.Rep (env_ g m))+    ) =>+    -- | Transform to be applied to each field.+    (forall x. Typeable x => h x -> f (g x)) ->+    env_ h m ->+    f (env_ g m)+  traverseH t env = G.to <$> gTraverseH t (G.from env)++  -- | Used to implement 'liftA2Phase', typically you should use that function instead.+  liftA2H ::+    forall (a :: Type -> Type) (f :: Type -> Type) (f' :: Type -> Type) (m :: Type -> Type).+    ( Typeable a,+      Typeable f,+      Typeable f',+      Typeable m+    ) =>+    -- | Binary operation to combine corresponding fields.+    (forall x. Typeable x => a x -> f x -> f' x) ->+    env_ a m ->+    env_ f m ->+    env_ f' m+  default liftA2H ::+    forall (a :: Type -> Type) (f :: Type -> Type) (f' :: Type -> Type) m.+    ( Typeable a,+      Typeable f,+      Typeable f',+      Typeable m,+      G.Generic (env_ a m),+      G.Generic (env_ f m),+      G.Generic (env_ f' m),+      GLiftA2Phase a f f' (G.Rep (env_ a m)) (G.Rep (env_ f m)) (G.Rep (env_ f' m))+    ) =>+    -- | Transform to be applied to each field.+    (forall x. Typeable x => a x -> f x -> f' x) ->+    env_ a m ->+    env_ f m ->+    env_ f' m+  liftA2H f enva env = G.to (gLiftA2Phase f (G.from enva) (G.from env))++-- | Slightly less powerful version of 'traverseH'.+liftAH ::+  forall deps_ phases phases' m.+  (Phased deps_, Typeable phases, Typeable phases', Typeable m) =>+  -- | Transform to be applied to each field.+  (forall x. Typeable x => phases x -> phases' x) ->+  deps_ phases m ->+  deps_ phases' m+liftAH tweak =+  runIdentity . traverseH (Identity . tweak)++-- | Take the outermost phase wrapping each component and \"pull it outwards\",+-- aggregating the phase's applicative effects.+--+-- >>> :{+--  newtype Foo d = Foo {foo :: String -> d ()} deriving Generic+--  makeIOFoo :: MonadIO m => Foo m+--  makeIOFoo = Foo (liftIO . putStrLn)+--  env :: InductiveEnv '[Foo] (IO `Compose` Constructor (InductiveEnv '[Foo] Identity IO)) IO+--  env = EmptyEnv+--      & AddDep @Foo (putStrLn "io phase" `bindPhase` \() -> constructor (\_ -> makeIOFoo))+--  ioOutside :: IO (InductiveEnv '[Foo] (Constructor (InductiveEnv '[Foo] Identity IO)) IO)+--  ioOutside = pullPhase env+-- :}+pullPhase ::+  forall (f :: Type -> Type) (g :: Type -> Type) (m :: Type -> Type) env_.+  (Phased env_, Applicative f, Typeable f, Typeable g, Typeable m) =>+  env_ (Compose f g) m ->+  -- | Environment with the outer 'Applicative' layer pulled outward.+  f (env_ g m)+-- f first to help annotate the phase+pullPhase = traverseH @env_ getCompose++-- | Modify the outermost phase wrapping each component.+--+-- >>> :{+--  newtype Foo d = Foo {foo :: String -> d ()} deriving Generic+--  makeIOFoo :: MonadIO m => Foo m+--  makeIOFoo = Foo (liftIO . putStrLn)+--  env :: InductiveEnv '[Foo] ((,) Int `Compose` Constructor String) IO+--  env = EmptyEnv+--      & AddDep @Foo ((2,()) `bindPhase` \() -> constructor (\_ -> makeIOFoo))+--  env' :: InductiveEnv '[Foo] ((,) String `Compose` Constructor String) IO+--  env' = mapPhase (\(n,x) -> (show n,x)) env+-- :}+mapPhase ::+  forall (f :: Type -> Type) (f' :: Type -> Type) (g :: Type -> Type) (m :: Type -> Type) env_.+  (Phased env_, Typeable f, Typeable f', Typeable g, Typeable m) =>+  -- | Transform to be applied to each field.+  (forall x. Typeable x => f x -> f' x) ->+  env_ (Compose f g) m ->+  env_ (Compose f' g) m+-- f' first to help annotate the *target* of the transform?+mapPhase f env = runIdentity $ traverseH @env_ (\(Compose fg) -> Identity (Compose (f fg))) env++-- | Combine two environments with a function that works on their outermost phases.+liftA2Phase ::+  forall (a :: Type -> Type) (f' :: Type -> Type) (f :: Type -> Type) (g :: Type -> Type) (m :: Type -> Type) env_.+  (Phased env_, Typeable a, Typeable f, Typeable f', Typeable g, Typeable m) =>+  -- | Binary operation to combine corresponding fields.+  (forall x. Typeable x => a x -> f x -> f' x) ->+  env_ (Compose a g) m ->+  env_ (Compose f g) m ->+  env_ (Compose f' g) m+-- f' first to help annotate the *target* of the transform?+liftA2Phase f = liftA2H @env_ (\(Compose fa) (Compose fg) -> Compose (f fa fg))++class GTraverseH h g env env' | env -> h, env' -> g where+  gTraverseH :: Applicative f => (forall x. Typeable x => h x -> f (g x)) -> env x -> f (env' x)++instance+  (GTraverseH h g fields fields') =>+  GTraverseH+    h+    g+    (G.D1 metaData (G.C1 metaCons fields))+    (G.D1 metaData (G.C1 metaCons fields'))+  where+  gTraverseH t (G.M1 (G.M1 fields)) =+    G.M1 . G.M1 <$> gTraverseH @h @g t fields++instance+  ( GTraverseH h g left left',+    GTraverseH h g right right'+  ) =>+  GTraverseH h g (left G.:*: right) (left' G.:*: right')+  where+  gTraverseH t (left G.:*: right) =+    let left' = gTraverseH @h @g t left+        right' = gTraverseH @h @g t right+     in liftA2 (G.:*:) left' right'++instance+  Typeable bean =>+  GTraverseH+    h+    g+    (G.S1 metaSel (G.Rec0 (h bean)))+    (G.S1 metaSel (G.Rec0 (g bean)))+  where+  gTraverseH t (G.M1 (G.K1 hbean)) =+    G.M1 . G.K1 <$> t hbean++--+--+class GLiftA2Phase a f f' enva env env' | enva -> a, env -> f, env' -> f' where+  gLiftA2Phase :: (forall r. Typeable r => a r -> f r -> f' r) -> enva x -> env x -> env' x++instance+  GLiftA2Phase a f f' fieldsa fields fields' =>+  GLiftA2Phase+    a+    f+    f'+    (G.D1 metaData (G.C1 metaCons fieldsa))+    (G.D1 metaData (G.C1 metaCons fields))+    (G.D1 metaData (G.C1 metaCons fields'))+  where+  gLiftA2Phase f (G.M1 (G.M1 fieldsa)) (G.M1 (G.M1 fields)) =+    G.M1 (G.M1 (gLiftA2Phase @a @f @f' f fieldsa fields))++instance+  ( GLiftA2Phase a f f' lefta left left',+    GLiftA2Phase a f f' righta right right'+  ) =>+  GLiftA2Phase a f f' (lefta G.:*: righta) (left G.:*: right) (left' G.:*: right')+  where+  gLiftA2Phase f (lefta G.:*: righta) (left G.:*: right) =+    let left' = gLiftA2Phase @a @f @f' f lefta left+        right' = gLiftA2Phase @a @f @f' f righta right+     in (G.:*:) left' right'++instance+  Typeable bean =>+  GLiftA2Phase+    a+    f+    f'+    (G.S1 metaSel (G.Rec0 (a bean)))+    (G.S1 metaSel (G.Rec0 (f bean)))+    (G.S1 metaSel (G.Rec0 (f' bean)))+  where+  gLiftA2Phase f (G.M1 (G.K1 abean)) (G.M1 (G.K1 fgbean)) =+    G.M1 (G.K1 (f abean fgbean))+++-- | Examples without @-XQualifiedDo@:+--+-- >>> :{+--  type Phases = IO `Compose` IO `Compose` Identity+--  phased :: Phases Int+--  phased =+--      pure 1 Dep.Phases.>>= \i1 ->+--      pure 2 Dep.Phases.>>= \i2 ->+--      pure $ i1 + i2+-- :}+--+--+-- >>> :{+-- type Phases = (IO `Compose` Maybe `Compose` Either Char) Int+-- phases :: Phases+-- phases = +--    pure () Dep.Phases.>>= \_ ->+--    Just 5 Dep.Phases.>>= \_ ->+--    Left 'e'+-- :}+--+-- (>>=) :: Functor f => f x -> (x -> g y) -> Compose f g y f >>= k = Compose (k <$> f) +-- | Better not use this one without @-XQualifiedDo@ (>>) :: Functor f => f x -> g y -> Compose f g y f >> g = Compose (g <$ f) @@ -45,7 +344,7 @@ -- It would indeed be useful (it would allow pre-packaging and sharing initial -- phases as do-blocks) but it isn't supported. ----- __BEWARE__ #2! Do not use 'return' in this do-notation.+-- __BEWARE#2__! Do not use 'return' in this do-notation. -- -- Some valid examples: --@@ -97,6 +396,7 @@ -- >>> :set -fno-warn-deprecations -- >>> import Data.Kind -- >>> import Data.Function ((&))+-- >>> import Control.Monad.IO.Class -- >>> import Dep.Env -- >>> import GHC.Generics (Generic) -- >>> import Prelude hiding ((>>=), (>>))
test/tests_has.hs view
@@ -376,6 +376,12 @@ acc2 :: Accumulator IO  acc2 = inject (Constant ()) +accAll :: (InjectsAll '[Activities, Constant ()] IO accum, Monoid accum) => accum+accAll = inject (Activities []) <> inject (Constant ())++acc3 :: Accumulator IO +acc3 = accAll+ -- -- --