dep-t 0.1.0.0 → 0.1.0.1
raw patch · 5 files changed
+36/−7 lines, 5 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
Files
- CHANGELOG.md +4/−0
- README.md +25/−3
- dep-t.cabal +1/−1
- lib/Control/Monad/Dep.hs +1/−1
- test/tests.hs +5/−2
CHANGELOG.md view
@@ -1,5 +1,9 @@ # Revision history for dep-t +## 0.1.0.1 ++* Minor documentation changes.+ ## 0.1.0.0 -- YYYY-mm-dd * First version. Released on an unsuspecting world.
README.md view
@@ -10,7 +10,7 @@ ## Rationale To achieve dependency injection in Haskell, a common solution is to build a-record of functions and pass it to the program logic some variant of+record of functions and pass it to the program logic using some variant of [`ReaderT`](http://hackage.haskell.org/package/mtl-2.2.2/docs/Control-Monad-Reader.html). Let's start by defining some auxiliary typeclasses to extract functions from an@@ -46,8 +46,8 @@ Here's a function which obtains its dependencies from the environment record: - _mkControllerIO :: (HasLogger e IO, HasRepository e IO) => Int -> ReaderT e IO Int- _mkControllerIO x = do+ mkControllerIO :: (HasLogger e IO, HasRepository e IO) => Int -> ReaderT e IO Int+ mkControllerIO x = do doLog <- asks logger liftIO $ doLog "I'm going to insert in the db!" insert <- asks repository@@ -169,6 +169,24 @@ collecting the outputs of functions working as ["test doubles"](https://martinfowler.com/bliki/TestDouble.html). +## Invoking the functions in the environment is cumbersome++Yeah, it's annoying to perform the "ask for function, invoke function" dance each time:++ mkController x = do+ doLog <- asks logger+ doLog "I'm going to insert in the db!"++One workaround (at the cost of more boilerplate) is to define helper functions like: ++ logger' :: (MonadReader e m, HasLogger e m) => String -> m ()+ logger' msg = asks logger >>= \f -> f msg++Which you can invoke like this:++ mkController x = do+ logger' "I'm going to insert in the db!"+ ## Caveats The structure of the `DepT` type might be prone to trigger a [known infelicity@@ -181,6 +199,10 @@ question](https://stackoverflow.com/a/61782258/1364288). - The implementation of `mapDepT` was teased out in [this other SO question](https://stackoverflow.com/questions/65710657/writing-a-zooming-function-for-a-readert-like-monad-transformer).++- An [SO+ answer](https://stackoverflow.com/questions/57703898/how-to-call-impure-functions-from-pure-ones/57714058#57714058)+ about records-of-functions and the "veil of polymorphism". - I'm unsure of the relationship between `DepT` and the technique described in [Adventures assembling records of
dep-t.cabal view
@@ -1,7 +1,7 @@ cabal-version: 3.0 name: dep-t-version: 0.1.0.0+version: 0.1.0.1 synopsis: Reader-like monad transformer for dependency injection. description: Put all your functions in the environment record! Let all your functions read from the environment record! No favorites!
lib/Control/Monad/Dep.hs view
@@ -121,7 +121,7 @@ -- Makes the functions inside a small environment require a bigger environment. -- -- This can be useful if we are encasing the small environment as a field of--- the big environment, ir order to make the types match.+-- the big environment, in order to make the types match. -- -- The scary first parameter is a function that, given a natural -- transformation of monads, changes the monad parameter of the environment
test/tests.hs view
@@ -29,6 +29,9 @@ class HasLogger r m | r -> m where logger :: r -> String -> m () +logger' :: (MonadReader e m, HasLogger e m) => String -> m ()+logger' msg = asks logger >>= \f -> f msg+ type HasRepository :: Type -> (Type -> Type) -> Constraint class HasRepository r m | r -> m where repository :: r -> Int -> m ()@@ -98,8 +101,8 @@ -- In a sufficiently complex app, the diverse functions will form a DAG of -- dependencies between each other. So it would be nice if the functions were -- treated uniformly, all having access to (views of) the environment record.-_mkControllerIO :: (HasLogger e IO, HasRepository e IO) => Int -> ReaderT e IO Int-_mkControllerIO x = do+mkControllerIO :: (HasLogger e IO, HasRepository e IO) => Int -> ReaderT e IO Int+mkControllerIO x = do doLog <- asks logger liftIO $ doLog "I'm going to insert in the db!" insert <- asks repository