packages feed

dep-t 0.6.3.0 → 0.6.4.0

raw patch · 4 files changed

+413/−341 lines, 4 filesPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

- Dep.Env: instance forall k1 k2 (a :: k1 -> *) (f :: k1 -> *) (f' :: k1 -> *) (metaSel :: GHC.Generics.Meta) (bean :: k1). 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 (h :: k1 -> *) (g :: k1 -> *) (metaSel :: GHC.Generics.Meta) (bean :: k1). 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: fixEnvAccum :: (Phased env_, Typeable env_, Typeable m, Monoid w, Typeable w) => env_ (AccumConstructor w (env_ Identity m)) m -> (w, env_ Identity m)
+ 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: type AccumConstructor (w :: Type) (env :: Type) = (->) (w, env) `Compose` (,) w `Compose` Identity
- Dep.Env: [AddDep] :: forall r_ m rs h. h (r_ m) -> InductiveEnv rs h m -> InductiveEnv (r_ : rs) h m
+ Dep.Env: [AddDep] :: forall r_ m rs h. Typeable r_ => h (r_ m) -> InductiveEnv rs h m -> InductiveEnv (r_ : rs) h m
- Dep.Env: addDep :: forall r_ m rs. r_ m -> InductiveEnv rs Identity m -> InductiveEnv (r_ : rs) Identity m
+ Dep.Env: addDep :: forall r_ m rs. Typeable r_ => r_ m -> InductiveEnv rs Identity m -> InductiveEnv (r_ : rs) Identity m
- Dep.Env: 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. a x -> f x -> f' x) -> env_ a m -> env_ f m -> env_ f' m
+ Dep.Env: 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.Env: 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. a x -> f x -> f' x) -> env_ (Compose a g) m -> env_ (Compose f g) m -> env_ (Compose f' g) m
+ Dep.Env: 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.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) => (forall x. f x -> f' x) -> env_ (Compose f g) m -> env_ (Compose f' g) m
+ Dep.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) => (forall x. Typeable x => f x -> f' x) -> env_ (Compose f g) m -> env_ (Compose f' g) m
- Dep.Env: mapPhaseWithFieldNames :: forall (f :: Type -> Type) (f' :: Type -> Type) (g :: Type -> Type) (m :: Type -> Type) env_. (Phased env_, DemotableFieldNames env_, Typeable f, Typeable f', Typeable g, Typeable m) => (forall x. String -> f x -> f' x) -> env_ (Compose f g) m -> env_ (Compose f' g) m
+ Dep.Env: mapPhaseWithFieldNames :: forall (f :: Type -> Type) (f' :: Type -> Type) (g :: Type -> Type) (m :: Type -> Type) env_. (Phased env_, DemotableFieldNames env_, Typeable f, Typeable f', Typeable g, Typeable m) => (forall x. Typeable x => String -> f x -> f' x) -> env_ (Compose f g) m -> env_ (Compose f' g) m
- Dep.Env: 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. h x -> f (g x)) -> env_ h m -> f (env_ g m)
+ Dep.Env: 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,5 +1,20 @@ # Revision history for dep-t +## 0.6.4++* Added AccumConstructor, a generalization of Constructor that threads a+  monoidal summary along with the environment record.++* Now Dep.Env.Phased.traverseH and related functions provide a 'Typeable'+  constraint on the polymorphic field variable in the function argument.++  This *should* be backwards compatible because, with the 'Typeable' constraint,+  the HOF is providing *more* info to the caller of 'traverseH' to work with.++  However it *does* cause a little breakage, because now you can't use FunctorT,+  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  * Now the Bare typeclass doesn't recurse inside Identity or Const.
dep-t.cabal view
@@ -1,7 +1,7 @@ cabal-version:       3.0  name:                dep-t-version:             0.6.3.0+version:             0.6.4.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/Env.hs view
@@ -1,22 +1,22 @@ {-# 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 FlexibleInstances #-}-{-# LANGUAGE RankNTypes #-} {-# LANGUAGE ViewPatterns #-}-{-# LANGUAGE ImportQualifiedPost #-}-{-# LANGUAGE TypeOperators #-}-{-# LANGUAGE ConstraintKinds #-}-{-# LANGUAGE GADTs #-}  -- | This module provides helpers for building dependency injection -- environments composed of records.@@ -36,11 +36,11 @@ --   , insert :: String -> d Int --   } -- ----- data Controller d = Controller +-- data Controller d = Controller --   { create :: d Int---   , append :: Int -> String -> d Bool +--   , append :: Int -> String -> d Bool --   , inspect :: Int -> d (Maybe String)---   } +--   } -- -- -- type EnvHKD :: (Type -> Type) -> (Type -> Type) -> Type -- data EnvHKD h m = EnvHKD@@ -55,66 +55,78 @@ -- -- The module also provides a monad transformer-less way of performing dependency -- injection, by means of 'fixEnv'.----module Dep.Env (-      -- * A general-purpose Has-      Has-      -- * Helpers for deriving Has-      -- ** via the default field name-    , TheDefaultFieldName (..)-      -- ** via arbitrary field name-    , TheFieldName (..)-      -- ** via autowiring-    , FieldsFindableByType (..)-    , Autowired (..)-    , Autowireable-      -- * Managing phases-    , Phased (..)-    , pullPhase-    , mapPhase-    , liftA2Phase-      -- ** Working with field names-    , DemotableFieldNames (..)-    , demoteFieldNames-    , mapPhaseWithFieldNames-      -- ** Constructing phases-      -- $phasehelpers-    , bindPhase-    , skipPhase  -      -- $phasehelpers2-    , Bare-    , fromBare-    , toBare-      -- * Injecting dependencies by tying the knot-    , fixEnv-    , Constructor-    , constructor-      -- * Inductive environment with anonymous fields-    , InductiveEnv (..)-    , addDep-    , emptyEnv+module Dep.Env+  ( -- * A general-purpose Has+    Has,++    -- * Helpers for deriving Has++    -- ** via the default field name+    TheDefaultFieldName (..),++    -- ** via arbitrary field name+    TheFieldName (..),++    -- ** via autowiring+    FieldsFindableByType (..),+    Autowired (..),+    Autowireable,++    -- * Managing phases+    Phased (..),+    pullPhase,+    mapPhase,+    liftA2Phase,++    -- ** Working with field names+    DemotableFieldNames (..),+    demoteFieldNames,+    mapPhaseWithFieldNames,++    -- ** Constructing phases+    -- $phasehelpers+    bindPhase,+    skipPhase,+    -- $phasehelpers2+    Bare,+    fromBare,+    toBare,++    -- * Injecting dependencies by tying the knot+    fixEnv,+    Constructor,+    constructor,+    fixEnvAccum,+    AccumConstructor,++    -- * Inductive environment with anonymous fields+    InductiveEnv (..),+    addDep,+    emptyEnv,+     -- * Re-exports-    , Identity (..)-    , Constant (..)-    , Compose (..)-    ) where+    Identity (..),+    Constant (..),+    Compose (..),+  )+where -import Dep.Has -import Data.Kind-import GHC.Records-import GHC.TypeLits-import Data.Coerce-import GHC.Generics qualified as G import Control.Applicative-import Data.Proxy-import Data.Functor ((<&>), ($>))+import Data.Coerce+import Data.Function (fix)+import Data.Functor (($>), (<&>)) import Data.Functor.Compose import Data.Functor.Constant import Data.Functor.Identity-import Data.Function (fix)+import Data.Kind+import Data.Proxy import Data.String import Data.Type.Equality (type (==)) import Data.Typeable+import Dep.Has+import GHC.Generics qualified as G+import GHC.Records+import GHC.TypeLits  -- $setup --@@ -145,9 +157,7 @@ -- >>> import Dep.Env -- >>> import Dep.Env -- >>> import GHC.Generics (Generic)--- - -- via the default field name  -- | Helper for @DerivingVia@ 'HasField' instances.@@ -160,9 +170,11 @@ -- explicitness. newtype TheDefaultFieldName (env :: Type) = TheDefaultFieldName env -instance (Dep r_, HasField (DefaultFieldName r_) (env_ m) u, Coercible u (r_ m)) -         => Has r_ m (TheDefaultFieldName (env_ m)) where-   dep (TheDefaultFieldName env) = coerce . getField @(DefaultFieldName r_) $ env+instance+  (Dep r_, HasField (DefaultFieldName r_) (env_ m) u, Coercible u (r_ m)) =>+  Has r_ m (TheDefaultFieldName (env_ m))+  where+  dep (TheDefaultFieldName env) = coerce . getField @(DefaultFieldName r_) $ env  -- | Helper for @DerivingVia@ 'HasField' instances. --@@ -170,9 +182,11 @@ type TheFieldName :: Symbol -> Type -> Type newtype TheFieldName (name :: Symbol) (env :: Type) = TheFieldName env -instance (HasField name (env_ m) u, Coercible u (r_ m)) -         => Has r_ m (TheFieldName name (env_ m)) where-   dep (TheFieldName env) = coerce . getField @name $ env+instance+  (HasField name (env_ m) u, Coercible u (r_ m)) =>+  Has r_ m (TheFieldName name (env_ m))+  where+  dep (TheFieldName env) = coerce . getField @name $ env  -- via autowiring @@ -182,8 +196,8 @@ -- instance, but users can write their own implementations. type FieldsFindableByType :: Type -> Constraint class FieldsFindableByType (env :: Type) where-    type FindFieldByType env (r :: Type) :: Symbol -    type FindFieldByType env r = FindFieldByType_ env r+  type FindFieldByType env (r :: Type) :: Symbol+  type FindFieldByType env r = FindFieldByType_ env r  -- | Helper for @DerivingVia@ 'HasField' instances. --@@ -203,46 +217,48 @@ -- "Data.Functor.Identity". type Autowireable r_ (m :: Type -> Type) (env :: Type) = HasField (FindFieldByType env (r_ m)) env (Identity (r_ m)) -instance (-           FieldsFindableByType (env_ m),-           HasField (FindFieldByType (env_ m) (r_ m)) (env_ m) u,-           Coercible u (r_ m) -         ) -         => Has r_ m (Autowired (env_ m)) where-   dep (Autowired env) = coerce @u $ getField @(FindFieldByType (env_ m) (r_ m)) env+instance+  ( FieldsFindableByType (env_ m),+    HasField (FindFieldByType (env_ m) (r_ m)) (env_ m) u,+    Coercible u (r_ m)+  ) =>+  Has r_ m (Autowired (env_ m))+  where+  dep (Autowired env) = coerce @u $ getField @(FindFieldByType (env_ m) (r_ m)) env  type FindFieldByType_ :: Type -> Type -> Symbol type family FindFieldByType_ env r where-    FindFieldByType_ env r = IfMissing r (GFindFieldByType (ExtractProduct (G.Rep env)) r)+  FindFieldByType_ env r = IfMissing r (GFindFieldByType (ExtractProduct (G.Rep env)) r)  type ExtractProduct :: (k -> Type) -> k -> Type type family ExtractProduct envRep where-    ExtractProduct (G.D1 _ (G.C1 _ z)) = z+  ExtractProduct (G.D1 _ (G.C1 _ z)) = z  type IfMissing :: Type -> Maybe Symbol -> Symbol type family IfMissing r ms where-    IfMissing r Nothing = -        TypeError (-                 Text "The component " -            :<>: ShowType r -            :<>: Text " could not be found in environment.")-    IfMissing _ (Just name) = name+  IfMissing r Nothing =+    TypeError+      ( Text "The component "+          :<>: ShowType r+          :<>: Text " could not be found in environment."+      )+  IfMissing _ (Just name) = name  -- The k -> Type alwasy trips me up type GFindFieldByType :: (k -> Type) -> Type -> Maybe Symbol type family GFindFieldByType r x where-    GFindFieldByType (left G.:*: right)                                          r = -        WithLeftResult_ (GFindFieldByType left r) right r-    GFindFieldByType (G.S1 (G.MetaSel ('Just name) _ _ _) (G.Rec0 r))            r = Just name-    -- Here we are saying "any wrapper whatsoever over r". Too general?-    -- If the wrapper is not coercible to the underlying r, we'll fail later.-    GFindFieldByType (G.S1 (G.MetaSel ('Just name) _ _ _) (G.Rec0 (_ r)))        r = Just name-    GFindFieldByType _                                                           _ = Nothing+  GFindFieldByType (left G.:*: right) r =+    WithLeftResult_ (GFindFieldByType left r) right r+  GFindFieldByType (G.S1 (G.MetaSel ('Just name) _ _ _) (G.Rec0 r)) r = Just name+  -- Here we are saying "any wrapper whatsoever over r". Too general?+  -- If the wrapper is not coercible to the underlying r, we'll fail later.+  GFindFieldByType (G.S1 (G.MetaSel ('Just name) _ _ _) (G.Rec0 (_ r))) r = Just name+  GFindFieldByType _ _ = Nothing -type WithLeftResult_ :: Maybe Symbol -> (k -> Type) -> Type -> Maybe Symbol +type WithLeftResult_ :: Maybe Symbol -> (k -> Type) -> Type -> Maybe Symbol type family WithLeftResult_ leftResult right r where-    WithLeftResult_ ('Just ls) right r = 'Just ls-    WithLeftResult_ Nothing    right r = GFindFieldByType right r+  WithLeftResult_ ('Just ls) right r = 'Just ls+  WithLeftResult_ Nothing right r = GFindFieldByType right r  -- --@@ -262,70 +278,76 @@ -- '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. 'Phased' instances can be written in terms of them. 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-        ) => -        -- |-        (forall 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))-        )-        => (forall 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-        )-        => -        (forall 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))-        )-        => (forall 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))+  -- | 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))+ -- | Take the outermost phase wrapping each component and \"pull it outwards\", -- aggregating the phase's applicative effects. --@@ -334,19 +356,17 @@ --  makeIOFoo :: MonadIO m => Foo m --  makeIOFoo = Foo (liftIO . putStrLn) --  env :: InductiveEnv '[Foo] (IO `Compose` Constructor (InductiveEnv '[Foo] Identity IO)) IO---  env = EmptyEnv +--  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 -          -> -          -- |-          f (env_ g m)+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 @@ -357,131 +377,150 @@ --  makeIOFoo :: MonadIO m => Foo m --  makeIOFoo = Foo (liftIO . putStrLn) --  env :: InductiveEnv '[Foo] ((,) Int `Compose` Constructor String) IO---  env = EmptyEnv +--  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) -         => -         -- |-         (forall x. f x -> f' x) -         -> -         -- |-         env_ (Compose f g) m -         -> -         -- |-         env_ (Compose f' g) m+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) -    => -    -- |-    (forall x. a x -> f x -> f' x) -    -> -    -- |-    env_ (Compose a g) m -    -> -    -- |-    env_ (Compose f g) m -    -> -    -- |-    env_ (Compose f' g) m+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 . h x -> f (g x)) -> env x -> f (env' x)+  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 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+  ( 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 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 +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. a r -> f r -> f' r) -> enva x -> env x -> env' x+  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' 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+  ( 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   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))+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. type DemotableFieldNames :: ((Type -> Type) -> (Type -> Type) -> Type) -> Constraint class DemotableFieldNames env_ where-    demoteFieldNamesH :: (forall x. String -> h String x) -> env_ (h String) m-    default demoteFieldNamesH -        :: ( G.Generic (env_ (h String) m)-           , GDemotableFieldNamesH h (G.Rep (env_ (h String) m)))-           => (forall x. String -> h String x) -           -> env_ (h String) m-    demoteFieldNamesH f = G.to (gDemoteFieldNamesH f)+  demoteFieldNamesH :: (forall x. String -> h String x) -> env_ (h String) m+  default demoteFieldNamesH ::+    ( G.Generic (env_ (h String) m),+      GDemotableFieldNamesH h (G.Rep (env_ (h String) m))+    ) =>+    (forall x. String -> h String x) ->+    env_ (h String) m+  demoteFieldNamesH f = G.to (gDemoteFieldNamesH f)  -- | Bring down the field names of the environment to the term level and store -- them in the accumulator of "Data.Functor.Constant".-demoteFieldNames :: forall env_ m . DemotableFieldNames env_ => env_ (Constant String) m+demoteFieldNames :: forall env_ m. DemotableFieldNames env_ => env_ (Constant String) m demoteFieldNames = demoteFieldNamesH Constant  class GDemotableFieldNamesH h env | env -> h where-    gDemoteFieldNamesH :: (forall x. String -> h String x) -> env x-            -instance GDemotableFieldNamesH h fields-    => GDemotableFieldNamesH h (G.D1 metaData (G.C1 metaCons fields)) where-    gDemoteFieldNamesH f = G.M1 (G.M1 (gDemoteFieldNamesH f))+  gDemoteFieldNamesH :: (forall x. String -> h String x) -> env x -instance ( GDemotableFieldNamesH h left,-           GDemotableFieldNamesH h right) -        => GDemotableFieldNamesH h (left G.:*: right) where-     gDemoteFieldNamesH f = -         gDemoteFieldNamesH f G.:*: gDemoteFieldNamesH f+instance+  GDemotableFieldNamesH h fields =>+  GDemotableFieldNamesH h (G.D1 metaData (G.C1 metaCons fields))+  where+  gDemoteFieldNamesH f = G.M1 (G.M1 (gDemoteFieldNamesH f)) +instance+  ( GDemotableFieldNamesH h left,+    GDemotableFieldNamesH h right+  ) =>+  GDemotableFieldNamesH h (left G.:*: right)+  where+  gDemoteFieldNamesH f =+    gDemoteFieldNamesH f G.:*: gDemoteFieldNamesH f+ instance KnownSymbol name => GDemotableFieldNamesH h (G.S1 (G.MetaSel ('Just name) u v w) (G.Rec0 (h String bean))) where-     gDemoteFieldNamesH f = -         G.M1 (G.K1 (f (symbolVal (Proxy @name))))+  gDemoteFieldNamesH f =+    G.M1 (G.K1 (f (symbolVal (Proxy @name))))  -- | Modify the outermost phase wrapping each component, while having access to -- the field name of the component.@@ -489,41 +528,33 @@ -- A typical usage is modifying a \"parsing the configuration\" phase so that -- each component looks into a different section of the global configuration -- field.-mapPhaseWithFieldNames :: -    forall (f :: Type -> Type) (f' :: Type -> Type) (g :: Type -> Type) (m :: Type -> Type) env_ . -    ( Phased env_ -    , DemotableFieldNames env_-    , Typeable f-    , Typeable f'-    , Typeable g -    , Typeable m ) -    => -    -- |-    (forall x. String -> f x -> f' x) -    -> -    -- |-    env_ (Compose f g) m -    -> -    -- |-    env_ (Compose f' g) m+mapPhaseWithFieldNames ::+  forall (f :: Type -> Type) (f' :: Type -> Type) (g :: Type -> Type) (m :: Type -> Type) env_.+  ( Phased env_,+    DemotableFieldNames env_,+    Typeable f,+    Typeable f',+    Typeable g,+    Typeable m+  ) =>+  -- | Transform to be applied to each field. Has access to the field name.+  (forall x. Typeable x => String -> f x -> f' x) ->+  env_ (Compose f g) m ->+  env_ (Compose f' g) m -- f' first to help annotate the *target* of the transform?-mapPhaseWithFieldNames  f env =-    liftA2Phase (\(Constant name) z -> f name z) (runIdentity $ traverseH @env_ (\(Constant z) -> Identity (Compose (Constant z))) demoteFieldNames) env-+mapPhaseWithFieldNames f env =+  liftA2Phase (\(Constant name) z -> f name z) (runIdentity $ traverseH @env_ (\(Constant z) -> Identity (Compose (Constant z))) demoteFieldNames) env  -- constructing phases  -- $phasehelpers -- -- 'bindPhase' and 'skipPhase' are small convenience functions to help build nested compositions of functors.---  -- $phasehelpers2 -- -- 'fromBare' and 'toBare' are an alternative method to build nested compositions of functors, which relies on "coerce".--- - -- | Use the result of the previous phase to build the next one. -- -- Can be useful infix.@@ -532,13 +563,11 @@ --  type Phases = IO `Compose` IO `Compose` Identity --  phased :: Phases Int --  phased =---      pure 1 `bindPhase` \i1 -> ---      pure 2 `bindPhase` \i2 -> +--      pure 1 `bindPhase` \i1 ->+--      pure 2 `bindPhase` \i2 -> --      Identity (i1 + i2) -- :}------  -bindPhase :: forall f g a b . Functor f => f a -> (a -> g b) -> Compose f g b +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) @@ -552,8 +581,7 @@ --      skipPhase $ --      Identity 1 -- :}----skipPhase :: forall f g a . Applicative f => g a -> Compose f g a +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) @@ -592,7 +620,6 @@ -- wrapped :: Phases Int -- wrapped = fromBare $ pure $ succ -- :}--- fromBare :: Coercible phases (Bare phases) => Bare phases -> phases fromBare = coerce @@ -600,21 +627,26 @@ toBare :: Coercible phases (Bare phases) => phases -> Bare phases toBare = coerce - -- | A phase with the effect of \"constructing each component by reading its--- dependencies from a completed environment\". +-- dependencies from a completed environment\". It should be the final phase. -- -- The 'Constructor' phase for an environment will typically be parameterized -- with the environment itself. type Constructor (env :: Type) = ((->) env) `Compose` Identity ---- | Turn an environment-consuming function into a 'Constructor' that can be slotted +-- | Turn an environment-consuming function into a 'Constructor' that can be slotted -- into some field of a 'Phased' environment.-constructor :: forall r_ m env . (env -> r_ m) -> Constructor env (r_ m)+constructor :: forall r_ m env. (env -> r_ m) -> Constructor env (r_ m) -- same order of type parameters as Has constructor = coerce +-- | A generalization of 'Constructor' which produces, in addition to the result+-- value, a value @w@ which is then aggregated across all components and fed+-- back along with the completed environment.+--+-- Like 'Constructor', 'AccumConstructor' should be the final phase.+type AccumConstructor (w :: Type) (env :: Type) = (->) (w, env) `Compose` (,) w `Compose` Identity+ -- | This is a method of performing dependency injection that doesn't require -- "Control.Monad.Dep.DepT" at all. In fact, it doesn't require the use of -- /any/ monad transformer!@@ -640,9 +672,9 @@ --  makeBar :: Has Foo m env => env -> Bar m --  makeBar (asCall -> call) = Bar (call foo) --  env :: InductiveEnv [Bar,Foo] (Constructor (InductiveEnv [Bar,Foo] Identity IO)) IO---  env = EmptyEnv +--  env = EmptyEnv --      & AddDep @Foo (constructor (\_ -> makeIOFoo))---      & AddDep @Bar (constructor makeBar) +--      & AddDep @Bar (constructor makeBar) --  envReady :: InductiveEnv [Bar,Foo] Identity IO --  envReady = fixEnv env -- :}@@ -651,14 +683,39 @@ --  bar (dep envReady) "this is bar" -- :} -- this is bar----fixEnv :: (Phased env_, Typeable env_, Typeable m) => -        -- | Environment where each field is wrapped in a 'Constructor' -        env_ (Constructor (env_ Identity m)) m -> -        -- | Fully constructed environment, ready for use.-        env_ Identity m+fixEnv ::+  (Phased env_, Typeable env_, Typeable m) =>+  -- | Environment where each field is wrapped in a 'Constructor'+  env_ (Constructor (env_ Identity m)) m ->+  -- | Fully constructed environment, ready for use.+  env_ Identity m fixEnv env = fix (pullPhase env) +-- | A generalized version of 'fixEnv' which threads a monoidal accumulator+-- along with the environment.+--+-- Sometimes, we need constructors to produce a monoidal value along with the+-- component. Think for example about some kind of composable startup action for+-- the component.+--+-- And on the input side, some constructors need access to the monoidal value+-- accumulated across all components. Think for example about a component which+-- publishes accumulated diagnostics coming from all other components.+--+-- __/LAZY PATTERN MATCH REQUIRED!/__ Any constructor that matches on the+-- (accumulator, environment) tuple needs to use a lazy pattern match like+-- @~(w,env)@. Otherwise 'fixEnvAccum' enters an infinite loop! Such are the+-- dangers of knot-tying.+fixEnvAccum ::+  (Phased env_, Typeable env_, Typeable m, Monoid w, Typeable w) =>+  -- | Environment where each field is wrapped in an 'AccumConstructor'+  env_ (AccumConstructor w (env_ Identity m)) m ->+  -- | Fully constructed accumulator and environment, ready for use.+  (w, env_ Identity m)+fixEnvAccum env =+  let f = pullPhase <$> pullPhase env+   in fix f+ -- | An inductively constructed environment with anonymous fields. -- -- Can be useful for simple tests. Also for converting `Has`-based@@ -671,53 +728,53 @@ -- > makeControllerPositional a b = makeController $ addDep @Logger a $ addDep @Repository b $ emptyEnv -- > makeController' :: (Monad m, Has Logger m env, Has Repository m env) => env -> Controller m -- > makeController' env = makeControllerPositional (dep env) (dep env)------ data InductiveEnv (rs :: [(Type -> Type) -> Type]) (h :: Type -> Type) (m :: Type -> Type) where-    AddDep :: forall r_ m rs h . h (r_ m) -> InductiveEnv rs h m -> InductiveEnv (r_ : rs) h m-    EmptyEnv :: forall m h . InductiveEnv '[] h m+  AddDep :: forall r_ m rs h. Typeable r_ => h (r_ m) -> InductiveEnv rs h m -> InductiveEnv (r_ : rs) h m+  EmptyEnv :: forall m h. InductiveEnv '[] h m  -- | Unlike the 'AddDep' constructor, this sets @h@ to 'Identity'.-addDep :: forall r_ m rs . r_ m -> InductiveEnv rs Identity m -> InductiveEnv (r_ : rs) Identity m+addDep :: forall r_ m rs. Typeable r_ => r_ m -> InductiveEnv rs Identity m -> InductiveEnv (r_ : rs) Identity m addDep = AddDep @r_ @m @rs . Identity  -- | Unlike the 'EmptyEnv' constructor, this sets @h@ to 'Identity'.-emptyEnv :: forall m . InductiveEnv '[] Identity m+emptyEnv :: forall m. InductiveEnv '[] Identity m emptyEnv = EmptyEnv @m @Identity  instance Phased (InductiveEnv rs) where-    traverseH t EmptyEnv = pure EmptyEnv-    traverseH t (AddDep hx rest) = -        let headF = t hx-            restF = traverseH t rest-         in AddDep <$> headF <*> restF-    liftA2H t EmptyEnv EmptyEnv = EmptyEnv-    liftA2H t (AddDep ax arest) (AddDep hx hrest) = -        AddDep (t ax hx) (liftA2H t arest hrest)- +  traverseH t EmptyEnv = pure EmptyEnv+  traverseH t (AddDep hx rest) =+    let headF = t hx+        restF = traverseH t rest+     in AddDep <$> headF <*> restF+  liftA2H t EmptyEnv EmptyEnv = EmptyEnv+  liftA2H t (AddDep ax arest) (AddDep hx hrest) =+    AddDep (t ax hx) (liftA2H t arest hrest)+ -- | Works by searching on the list of types. instance InductiveEnvFind r_ m rs => Has r_ m (InductiveEnv rs Identity m) where-    dep = inductiveEnvDep+  dep = inductiveEnvDep  class InductiveEnvFind r_ m rs where-    inductiveEnvDep :: InductiveEnv rs Identity m -> r_ m+  inductiveEnvDep :: InductiveEnv rs Identity m -> r_ m -instance  TypeError (-                 Text "The component " -            :<>: ShowType r_ -            :<>: Text " could not be found in environment.") => InductiveEnvFind r_ m '[] where-    inductiveEnvDep = error "never happens"+instance+  TypeError+    ( Text "The component "+        :<>: ShowType r_+        :<>: Text " could not be found in environment."+    ) =>+  InductiveEnvFind r_ m '[]+  where+  inductiveEnvDep = error "never happens"  instance InductiveEnvFind' (r_ == r_') r_ m (r_' : rs) => InductiveEnvFind r_ m (r_' : rs) where-    inductiveEnvDep = inductiveEnvDep' @(r_ == r_')+  inductiveEnvDep = inductiveEnvDep' @(r_ == r_')  class InductiveEnvFind' (matches :: Bool) r_ m rs where-    inductiveEnvDep' :: InductiveEnv rs Identity m -> r_ m+  inductiveEnvDep' :: InductiveEnv rs Identity m -> r_ m  instance InductiveEnvFind' True r_ m (r_ : rs) where-    inductiveEnvDep' (AddDep (Identity r) _) = r+  inductiveEnvDep' (AddDep (Identity r) _) = r  instance InductiveEnvFind r_ m rs => InductiveEnvFind' False r_ m (x : rs) where-    inductiveEnvDep' (AddDep _ rest) = inductiveEnvDep rest--+  inductiveEnvDep' (AddDep _ rest) = inductiveEnvDep rest
test/tests_has.hs view
@@ -296,17 +296,17 @@ --     traverseH f (B e) = B <$> ttraverse f e --     liftA2H f (B ax) (B hx) = B $ tmap (\(Pair az hz) -> f az hz) $ tprod ax hx -type EnvHKD7 :: (Type -> Type) -> (Type -> Type) -> Type-data EnvHKD7 h m = EnvHKD7-  { logger :: h (Logger m),-    repository :: h (Repository m),-    controller :: h (Controller m)-  } deriving stock Generic-    deriving anyclass (FunctorT, TraversableT, ApplicativeT)--instance Phased EnvHKD7 where-    traverseH f e = ttraverse f e-    liftA2H f ax hx = tmap (\(Pair az hz) -> f az hz) $ tprod ax hx+-- type EnvHKD7 :: (Type -> Type) -> (Type -> Type) -> Type+-- data EnvHKD7 h m = EnvHKD7+--   { logger :: h (Logger m),+--     repository :: h (Repository m),+--     controller :: h (Controller m)+--   } deriving stock Generic+--     deriving anyclass (FunctorT, TraversableT, ApplicativeT)+-- +-- instance Phased EnvHKD7 where+--     traverseH f e = ttraverse f e+--     liftA2H f ax hx = tmap (\(Pair az hz) -> f az hz) $ tprod ax hx   -- This is an example of how to combine the "Phased" approach