packages feed

dep-t 0.6.1.0 → 0.6.2.0

raw patch · 5 files changed

+102/−12 lines, 5 filesdep ~doctestPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependency ranges changed: doctest

API changes (from Hackage documentation)

+ Dep.Env: fromBare :: Coercible phases (Bare phases) => Bare phases -> phases
+ Dep.Env: toBare :: Coercible phases (Bare phases) => phases -> Bare phases
- Dep.Env: type family FindFieldByType env (r :: Type) :: Symbol;
+ Dep.Env: type family Bare x

Files

CHANGELOG.md view
@@ -1,5 +1,11 @@ # Revision history for dep-t
 
+## 0.6.2
+
+* Moved `fromBare` and `toBare` from dep-t-dynamic.
+
+* Doc fix by @eyeinsky (PR #20)
+
 ## 0.6.1.0
 
 * Re-export `Data.Functor.Constant` from `Control.Monad.Dep`. https://github.com/danidiaz/dep-t/issues/18
README.md view
@@ -344,6 +344,8 @@   Patterns](https://www.goodreads.com/book/show/44416307-dependency-injection-principles-practices-and-patterns)
   This is a good book on the general princples of DI. 
 
+- A [series of posts](https://twitter.com/ploeh/status/1485514524962738179)—by one of the authors of the DI book—about building a DI container.
+
 - [Lessons learned while writing a Haskell
   application](https://gvolpe.com/blog/lessons-learned-while-writing-a-haskell-app/).
   This post recommends a "polymorphic record of functions" style, which fits
@@ -352,3 +354,6 @@ - One [big disadvantage](https://www.reddit.com/r/haskell/comments/r6foxv/opinions_on_reader_continuationbased_io/hmthsoy/) of the records-of-functions approach:
 
   > representing effects as records of functions rather than typeclasses/fused effect invocations destroys inlining, so you’ll generate significantly worse Core if you use this on a hot path.
+
+- [ReaderT pattern is just extensible effects](https://www.reddit.com/r/haskell/comments/sjhatp/readert_pattern_is_just_extensible_effects/)
+
dep-t.cabal view
@@ -1,7 +1,7 @@ cabal-version:       3.0
 
 name:                dep-t
-version:             0.6.1.0
+version:             0.6.2.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!
@@ -19,9 +19,9 @@ 
 common common
   build-depends:       base >=4.10.0.0 && < 5,
-                       transformers ^>= 0.5.0.0,
-                       mtl ^>= 2.2,
-                       unliftio-core ^>= 0.2.0.0,
+                       transformers >= 0.5.0.0,
+                       mtl >= 2.2,
+                       unliftio-core >= 0.2.0.0,
   default-language:    Haskell2010
 
 library
@@ -102,5 +102,5 @@   build-depends:       
                        dep-t, 
                        rank2classes       ^>= 1.4.1,
-                       doctest            ^>= 0.18,
+                       doctest            ^>= 0.20,
 
lib/Dep/Env.hs view
@@ -81,6 +81,10 @@       -- $phasehelpers
     , bindPhase
     , skipPhase  
+      -- $phasehelpers2
+    , Bare
+    , fromBare
+    , toBare
       -- * Injecting dependencies by tying the knot
     , fixEnv
     , Constructor
@@ -134,6 +138,7 @@ -- >>> :set -XStandaloneDeriving
 -- >>> :set -XUndecidableInstances
 -- >>> :set -XTypeOperators
+-- >>> :set -XScopedTypeVariables
 -- >>> import Data.Kind
 -- >>> import Data.Function ((&))
 -- >>> import Control.Monad.IO.Class
@@ -510,9 +515,15 @@ 
 -- $phasehelpers
 --
--- Small convenience functions to help build nested compositions of functors.
+-- '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.
@@ -545,6 +556,50 @@ 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)
+
+-- | This type family clears newtypes like 'Compose', 'Identity' and 'Constant' from a composite type,
+-- leaving you with a newtypeless nested type as result.
+--
+-- The idea is that it might be easier to construct values of the \"bare\" version of a composite type,
+-- and later coerce them to the newtyped version using 'fromBare'.
+--
+-- This is mainly intended for defining the nested 'Applicative' \"phases\" of components that live in a 'Phased'
+-- environment. It's an alternative to functions like `Dep.Env.bindPhase' and 'Dep.Env.skipPhase'.
+type Bare :: Type -> Type
+type family Bare x where
+  Bare (Compose outer inner x) = Bare (outer (Bare (inner x)))
+  Bare (Identity x) = Bare x
+  Bare (Const x k) = Bare x
+  Bare (Constant x k) = Bare x
+  Bare other = other
+
+-- | Convert a value from its bare version to the newtyped one, usually as a step
+-- towards inserting it into a 'Phased' environment.
+--
+-- >>> :{
+-- type Phases = IO `Compose` IO `Compose` IO
+-- wrapped :: Phases Int = fromBare $ pure $ pure $ pure 3
+-- :}
+--
+-- >>> :{
+-- type Phases = Constructor Int
+-- wrapped :: Phases Int
+-- wrapped = fromBare $ succ
+-- :}
+--
+-- >>> :{
+-- type Phases = IO `Compose` Constructor Int
+-- wrapped :: Phases Int
+-- wrapped = fromBare $ pure $ succ
+-- :}
+--
+fromBare :: Coercible phases (Bare phases) => Bare phases -> phases
+fromBare = coerce
+
+-- | Convert from the newtyped value to the bare one. 'fromBare' tends to be more useful.
+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\". 
lib/Dep/Has.hs view
@@ -25,7 +25,6 @@ -- find the correct records in the environment.
 --
 -- >>> :{
---  type Logger :: (Type -> Type) -> Type
 --  newtype Logger d = Logger {log :: String -> d ()}
 --  instance Dep Logger where
 --    type DefaultFieldName Logger = "logger"
@@ -101,7 +100,7 @@ -- record-of-functions @r_@, produces a 2-place constraint 
 --  saying that the environment @e@ has the record @r_@ with effect monad @m@.
 --
--- The constraint can used on its own, or with "Control.Monad.Dep.Class".
+-- The constraint can be used on its own, or with "Control.Monad.Dep.Class".
 type Has :: ((Type -> Type) -> Type) -> (Type -> Type) -> Type -> Constraint
 class Has r_ (m :: Type -> Type) (env :: Type) | env -> m where
   -- |  Given an environment @e@, produce a record-of-functions parameterized by the environment's effect monad @m@.
@@ -124,13 +123,37 @@ 
 -- | Transforms an environment with suitable 'Has' instances into a \"helper\"
 --   function that looks in the environment for the arguments of other functions.
---   Typically, the \"helped\" functions will be record field selectors.
+--   Typically, the \"helped\" functions will be record field selectors:
 --
---   In practice, this means that you can write @call foo@ instead of @foo (dep
---   env)@.
+-- >>> :{
+--  data SomeRecord m = SomeRecord { someSelector :: String -> m () }
+--  data Env m = Env
+--    { someRecord :: SomeRecord m
+--    }
+--  instance Has SomeRecord m (Env m) where
+--    dep (Env{someRecord}) = someRecord
+--  :}
 --
+--   In practice, this means that you can write @call someSelector@ instead of @someSelector (dep
+--   env)@:
+--
+-- >>> :{
+--    twoInvocations :: (IO (), IO ()) 
+--    twoInvocations = 
+--      let env :: Env IO = Env { someRecord = SomeRecord { someSelector = putStrLn } }
+--          call = asCall env
+--       in (someSelector (dep env) "foo", call someSelector "foo")  
+-- :}
+--
 --   Using 'asCall' in a view pattern avoids having to name the
---   environment.
+--   environment:
+--
+--
+-- >>> :{
+--    functionThatCalls :: Has SomeRecord m e => e -> m ()
+--    functionThatCalls (asCall -> call) = call someSelector "foo"
+-- :}
+--
 asCall :: forall env m . env -> forall r_ x. Has r_ m env => (r_ m -> x) -> x
 asCall env = \f -> f (dep env)
 
@@ -162,6 +185,7 @@ -- >>> :set -XTypeFamilies
 -- >>> :set -XDeriveGeneric
 -- >>> :set -XViewPatterns
+-- >>> :set -XScopedTypeVariables
 -- >>> import Data.Kind
 -- >>> import Control.Monad.Dep
 -- >>> import GHC.Generics (Generic)