packages feed

dep-t-advice 0.3.0.0 → 0.4.0.0

raw patch · 7 files changed

+342/−150 lines, 7 filesdep +criteriondep ~dep-tPVP ok

version bump matches the API change (PVP)

Dependencies added: criterion

Dependency ranges changed: dep-t

API changes (from Hackage documentation)

- Control.Monad.Dep.Advice: instance Control.Monad.Dep.Advice.Multicurryable as e m r curried => Control.Monad.Dep.Advice.Multicurryable (a : as) e m r (a -> curried)
- Control.Monad.Dep.Advice: instance GHC.Base.Monad m => Control.Monad.Dep.Advice.Multicurryable '[] e m r (Control.Monad.Dep.DepT e m r)
- Control.Monad.Dep.Advice: instance GHC.Base.Monad m => GHC.Base.Monoid (Control.Monad.Dep.Advice.Advice ca e m r)
- Control.Monad.Dep.Advice: instance GHC.Base.Monad m => GHC.Base.Semigroup (Control.Monad.Dep.Advice.Advice ca e m r)
+ Control.Monad.Dep.Advice: deceive :: forall as newtyped e_ m r curried. Deceivable as newtyped e_ m r curried => (e_ (DepT e_ m) -> newtyped) -> Deceived as newtyped e_ m r curried -> curried
+ Control.Monad.Dep.Advice: instance Control.Monad.Dep.Advice.Deceivable as newtyped e_ m r curried => Control.Monad.Dep.Advice.Deceivable (a : as) newtyped e_ m r (a -> curried)
+ Control.Monad.Dep.Advice: instance Control.Monad.Dep.Advice.Multicurryable as e_ m r curried => Control.Monad.Dep.Advice.Multicurryable (a : as) e_ m r (a -> curried)
+ Control.Monad.Dep.Advice: instance GHC.Base.Monad m => Control.Monad.Dep.Advice.Deceivable '[] newtyped e_ m r (Control.Monad.Dep.DepT e_ m r)
+ Control.Monad.Dep.Advice: instance GHC.Base.Monad m => Control.Monad.Dep.Advice.Multicurryable '[] e_ m r (Control.Monad.Dep.DepT e_ m r)
+ Control.Monad.Dep.Advice: instance GHC.Base.Monad m => GHC.Base.Monoid (Control.Monad.Dep.Advice.Advice ca e_ m r)
+ Control.Monad.Dep.Advice: instance GHC.Base.Monad m => GHC.Base.Semigroup (Control.Monad.Dep.Advice.Advice ca e_ m r)
- Control.Monad.Dep.Advice: advise :: forall ca e m r as advisee. (Multicurryable as e m r advisee, All ca as, Monad m) => Advice ca e m r -> advisee -> advisee
+ Control.Monad.Dep.Advice: advise :: forall ca e_ m r as advisee. (Multicurryable as e_ m r advisee, All ca as, Monad m) => Advice ca e_ m r -> advisee -> advisee
- Control.Monad.Dep.Advice: data Advice ca e m r
+ Control.Monad.Dep.Advice: data Advice ca e_ m r
- Control.Monad.Dep.Advice: makeAdvice :: forall u ca e m r. (forall as. All ca as => NP I as -> DepT e m (u, NP I as)) -> (u -> DepT e m r -> DepT e m r) -> Advice ca e m r
+ Control.Monad.Dep.Advice: makeAdvice :: forall u ca e_ m r. (forall as. All ca as => NP I as -> DepT e_ m (u, NP I as)) -> (u -> DepT e_ m r -> DepT e_ m r) -> Advice ca e_ m r
- Control.Monad.Dep.Advice: makeArgsAdvice :: forall ca e m r. Monad m => (forall as. All ca as => NP I as -> DepT e m (NP I as)) -> Advice ca e m r
+ Control.Monad.Dep.Advice: makeArgsAdvice :: forall ca e_ m r. Monad m => (forall as. All ca as => NP I as -> DepT e_ m (NP I as)) -> Advice ca e_ m r
- Control.Monad.Dep.Advice: makeExecutionAdvice :: forall ca e m r. Applicative m => (DepT e m r -> DepT e m r) -> Advice ca e m r
+ Control.Monad.Dep.Advice: makeExecutionAdvice :: forall ca e_ m r. Applicative m => (DepT e_ m r -> DepT e_ m r) -> Advice ca e_ m r
- Control.Monad.Dep.Advice: restrictArgs :: forall more less e m r. (forall x. Dict more x -> Dict less x) -> Advice less e m r -> Advice more e m r
+ Control.Monad.Dep.Advice: restrictArgs :: forall more less e_ m r. (forall x. Dict more x -> Dict less x) -> Advice less e_ m r -> Advice more e_ m r
- Control.Monad.Dep.Advice: runFinalDepT :: forall as e m r curried. Multicurryable as e m r curried => m (e (DepT e m)) -> curried -> DownToBaseMonad as e m r curried
+ Control.Monad.Dep.Advice: runFinalDepT :: forall as e_ m r curried. Multicurryable as e_ m r curried => m (e_ (DepT e_ m)) -> curried -> DownToBaseMonad as e_ m r curried
- Control.Monad.Dep.Advice: runFromEnv :: forall as e m r curried. Multicurryable as e m r curried => m (e (DepT e m)) -> (e (DepT e m) -> curried) -> DownToBaseMonad as e m r curried
+ Control.Monad.Dep.Advice: runFromEnv :: forall as e_ m r curried. Multicurryable as e_ m r curried => m (e_ (DepT e_ m)) -> (e_ (DepT e_ m) -> curried) -> DownToBaseMonad as e_ m r curried
- Control.Monad.Dep.Advice: type Ensure c e m = c (e (DepT e m)) (DepT e m)
+ Control.Monad.Dep.Advice: type Ensure c e_ m = c (DepT e_ m) (e_ (DepT e_ m))
- Control.Monad.Dep.Advice.Basic: doAsyncBadly :: forall ca e. Advice ca e IO ()
+ Control.Monad.Dep.Advice.Basic: doAsyncBadly :: forall ca e_. Advice ca e_ IO ()
- Control.Monad.Dep.Advice.Basic: doCachingBadly :: forall e m r. Monad m => (AnyEq -> m (Maybe r)) -> (AnyEq -> r -> m ()) -> Advice (Eq `And` Typeable) e m r
+ Control.Monad.Dep.Advice.Basic: doCachingBadly :: forall e_ m r. Monad m => (AnyEq -> m (Maybe r)) -> (AnyEq -> r -> m ()) -> Advice (Eq `And` Typeable) e_ m r
- Control.Monad.Dep.Advice.Basic: doLocally :: forall ca e m r. Monad m => (forall n. e n -> e n) -> Advice ca e m r
+ Control.Monad.Dep.Advice.Basic: doLocally :: forall ca e_ m r. Monad m => (forall n. e_ n -> e_ n) -> Advice ca e_ m r
- Control.Monad.Dep.Advice.Basic: printArgs :: forall e m r. MonadIO m => Handle -> String -> Advice Show e m r
+ Control.Monad.Dep.Advice.Basic: printArgs :: forall e_ m r. MonadIO m => Handle -> String -> Advice Show e_ m r
- Control.Monad.Dep.Advice.Basic: returnMempty :: forall ca e m r. (Monad m, Monoid r) => Advice ca e m r
+ Control.Monad.Dep.Advice.Basic: returnMempty :: forall ca e_ m r. (Monad m, Monoid r) => Advice ca e_ m r

Files

CHANGELOG.md view
@@ -1,5 +1,13 @@ # Revision history for dep-t-advice
 
+## 0.4.0.0 
+
+* Added 'deceive' function.
+
+* BREAKING CHANGE. The form of the 'HasX' constraints expected by 'Ensure' has
+  changed. Now they expect the effect monad as their first parameter. This is
+  in order to help with deriving.
+
 ## 0.3.0.0 
 
 * BREAKING CHANGE. Removed the dependency on "constraints" to reduce the
README.md view
@@ -35,18 +35,18 @@ 
 Imagine that you want to make this function print its argument to stdout:
 
-    foo :: Int -> DepT e IO () 
+    foo :: Int -> DepT e_ IO () 
 
 Easy enough:
 
-    foo' :: Int -> DepT e IO ()
+    foo' :: Int -> DepT e_ IO ()
     foo' arg1 = do
         liftIO $ putStrLn (show arg1)
         foo arg1
 
 You can even write your own general "printArgs" combinator:
 
-    printArgs :: Show a => (a -> DepT e IO ()) -> (a -> DepT e IO ())
+    printArgs :: Show a => (a -> DepT e_ IO ()) -> (a -> DepT e_ IO ())
     printArgs f arg1 = do
         liftIO $ putStrLn (show arg1)
         f arg1
@@ -82,7 +82,7 @@ 
 Here's how a `printArgs` advice might be defined:
 
-    printArgs :: forall e m r. MonadIO m => Handle -> String -> Advice Show e m r
+    printArgs :: forall e_ m r. MonadIO m => Handle -> String -> Advice Show e_ m r
     printArgs h prefix =
       makeArgsAdvice
         ( \args -> do
+ benchmark/benchmarks.hs view
@@ -0,0 +1,72 @@+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE FunctionalDependencies #-}
+{-# LANGUAGE ImportQualifiedPost #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE NamedFieldPuns #-}
+{-# LANGUAGE RankNTypes #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE StandaloneKindSignatures #-}
+{-# LANGUAGE TemplateHaskell #-}
+{-# LANGUAGE TypeApplications #-}
+{-# LANGUAGE UndecidableInstances #-}
+{-# LANGUAGE TypeOperators #-}
+{-# LANGUAGE ConstraintKinds #-}
+
+module Main where
+
+import Criterion.Main
+
+import Control.Monad.Dep
+import Control.Monad.Dep.Advice
+import Control.Monad.Dep.Advice.Basic
+import Data.Monoid
+import Data.Functor.Identity
+import Data.Foldable
+
+-- returnMempty' :: forall ca e m r. (Monad m, Monoid r) => Advice ca e m r
+-- returnMempty' =
+--   makeAdvice @()
+--     (\args -> pure (pure args))
+--     ( \() action -> do
+--         _ <- action
+--         pure (mempty :: r)
+--     )
+
+mempty' :: forall ca e m r. Monad m => Advice ca e m r
+mempty' = makeAdvice @()
+  (\args -> pure (pure args))
+  (\() -> id)
+
+summy :: Monad m => Int -> Int -> Int -> Int -> DepT NilEnv m (Sum Int)
+summy a1 a2 a3 a4 = pure $ Sum a1 <> Sum a2 <> Sum a3 <> Sum a4
+
+summyInstrumented :: Monad m => Int -> Int -> Int -> Int -> DepT NilEnv m (Sum Int)
+summyInstrumented = 
+    advise @Top mempty summy
+
+summyInstrumented' :: Monad m => Int -> Int -> Int -> Int -> DepT NilEnv m (Sum Int)
+summyInstrumented' = 
+    advise @Top mempty' summy
+
+main :: IO ()
+main =
+  defaultMain
+    [ bgroup
+        "adviceOverhead"
+        [ 
+            bench "not instrumented" $ 
+                whnf 
+                (foldl' (+) 0 . take 100000 . map (\(a1, a2, a3, a4) -> runIdentity $ summy a1 a2 a3 a4 `runDepT` NilEnv))
+                (repeat (0,0,0,0)),
+            bench "instrumented id advice" $ 
+                whnf 
+                (foldl' (+) 0 . take 100000 . map (\(a1, a2, a3, a4) -> runIdentity $ summyInstrumented a1 a2 a3 a4 `runDepT` NilEnv)) 
+                (repeat (0,0,0,0)),
+            bench "instrumented locally defined id advice" $ 
+                whnf 
+                (foldl' (+) 0 . take 100000 . map (\(a1, a2, a3, a4) -> runIdentity $ summyInstrumented' a1 a2 a3 a4 `runDepT` NilEnv)) 
+                (repeat (0,0,0,0))
+        ]
+    ] 
+
dep-t-advice.cabal view
@@ -1,7 +1,7 @@ cabal-version:       3.0
 
 name:                dep-t-advice
-version:             0.3.0.0
+version:             0.4.0.0
 synopsis:            Giving good advice to functions in a DepT environment.
 description:         Companion to the dep-t package. Easily add behaviour to functions living in a DepT environment, 
                      whatever the number of arguments they might have.
@@ -23,7 +23,8 @@ common common
   build-depends:       base >=4.10.0.0 && < 5,
                        sop-core ^>= 0.5.0.0,
-                       dep-t ^>= 0.1.3.0,
+                       transformers ^>= 0.5.0.0,
+                       dep-t ^>= 0.4.0.0,
   default-language:    Haskell2010
 
 common common-tests
@@ -70,3 +71,14 @@   build-depends:       
                        doctest            ^>= 0.17 
                        
+ -- https://cabal.readthedocs.io/en/3.4/cabal-package.html#benchmarks
+benchmark benchmark
+  import:              common-tests, 
+  type:                exitcode-stdio-1.0
+  ghc-options:         -O2
+  hs-source-dirs:      benchmark
+  main-is:             benchmarks.hs
+  build-depends:
+                       criterion ^>= 1.5,  
+
+
lib/Control/Monad/Dep/Advice.hs view
@@ -93,6 +93,9 @@     runFinalDepT,
     runFromEnv,
 
+    -- * Making functions see a different environment
+    deceive,
+
     -- * "sop-core" re-exports
     -- $sop
     Top,
@@ -101,11 +104,12 @@     NP (..),
     I (..),
     cfoldMap_NP,
-    Dict (..)
+    Dict (..),
   )
 where
 
 import Control.Monad.Dep
+import Control.Monad.Trans.Reader (ReaderT(..),withReaderT)
 import Data.Kind
 import Data.SOP
 import Data.SOP.Dict
@@ -122,10 +126,15 @@ -- >>> :set -XConstraintKinds
 -- >>> :set -XNamedFieldPuns
 -- >>> :set -XFlexibleContexts
+-- >>> :set -XDerivingStrategies
+-- >>> :set -XGeneralizedNewtypeDeriving
+-- >>> :set -XDataKinds
+-- >>> :set -XScopedTypeVariables
 -- >>> import Control.Monad
 -- >>> import Control.Monad.Dep
 -- >>> import Control.Monad.Dep.Advice
 -- >>> import Control.Monad.Dep.Advice.Basic (printArgs,returnMempty)
+-- >>> import Control.Monad.Writer
 -- >>> import Data.Kind
 -- >>> import Data.SOP
 -- >>> import Data.SOP.NP
@@ -134,11 +143,11 @@ -- >>> import Data.IORef
 
 -- | A generic transformation of 'DepT'-effectful functions with environment
--- @e@ of kind @(Type -> Type) -> Type@, base monad @m@ and return type @r@,
+-- @e_@ of kind @(Type -> Type) -> Type@, base monad @m@ and return type @r@,
 -- provided the functions satisfy certain constraint @ca@ of kind @Type ->
 -- Constraint@ on all of their arguments.
 --
--- Note that the type constructor for the environment @e@ is given unapplied.
+-- Note that the type constructor for the environment @e_@ is given unapplied.
 -- That is, @Advice Show NilEnv IO ()@ kind-checks but @Advice Show (NilEnv IO)
 -- IO ()@ doesn't. See also 'Ensure'.
 --
@@ -154,55 +163,52 @@   (Type -> Type) ->
   Type ->
   Type
-data Advice ca e m r where
+data Advice ca e_ m r where
   Advice ::
-    forall u ca e m r.
+    forall u ca e_ m r.
     Proxy u ->
     ( forall as.
       All ca as =>
       NP I as ->
-      DepT e m (u, NP I as)
+      DepT e_ m (u, NP I as)
     ) ->
-    ( 
-      u ->
-      DepT e m r ->
-      DepT e m r
+    ( u ->
+      DepT e_ m r ->
+      DepT e_ m r
     ) ->
-    Advice ca e m r
+    Advice ca e_ m r
 
 -- |
---    Aspects compose \"sequentially\" when tweaking the arguments, and
+--    'Advice's compose \"sequentially\" when tweaking the arguments, and
 --    \"concentrically\" when tweaking the final 'DepT' action.
 --
 --    The first 'Advice' is the \"outer\" one. It tweaks the function arguments
 --    first, and wraps around the execution of the second, \"inner\" 'Advice'.
-instance Monad m => Semigroup (Advice ca e m r) where
+instance Monad m => Semigroup (Advice ca e_ m r) where
   Advice outer tweakArgsOuter tweakExecutionOuter <> Advice inner tweakArgsInner tweakExecutionInner =
     let captureExistentials ::
-          forall ca e r outer inner.
+          forall ca e_ r outer inner.
           Proxy outer ->
           ( forall as.
             All ca as =>
             NP I as ->
-            DepT e m (outer, NP I as)
+            DepT e_ m (outer, NP I as)
           ) ->
-          ( 
-            outer ->
-            DepT e m r ->
-            DepT e m r
+          ( outer ->
+            DepT e_ m r ->
+            DepT e_ m r
           ) ->
           Proxy inner ->
           ( forall as.
             All ca as =>
             NP I as ->
-            DepT e m (inner, NP I as)
+            DepT e_ m (inner, NP I as)
           ) ->
-          ( 
-            inner ->
-            DepT e m r ->
-            DepT e m r
+          ( inner ->
+            DepT e_ m r ->
+            DepT e_ m r
           ) ->
-          Advice ca e m r
+          Advice ca e_ m r
         captureExistentials _ tweakArgsOuter' tweakExecutionOuter' _ tweakArgsInner' tweakExecutionInner' =
           Advice
             (Proxy @(Pair outer inner))
@@ -210,7 +216,7 @@                     forall as.
                     All ca as =>
                     NP I as ->
-                    DepT e m (Pair outer inner, NP I as)
+                    DepT e_ m (Pair outer inner, NP I as)
                   tweakArgs args =
                     do
                       (uOuter, argsOuter) <- tweakArgsOuter' @as args
@@ -220,17 +226,17 @@             )
             ( let tweakExecution ::
                     Pair outer inner ->
-                    DepT e m r ->
-                    DepT e m r
+                    DepT e_ m r ->
+                    DepT e_ m r
                   tweakExecution =
                     ( \(Pair uOuter uInner) action ->
                         tweakExecutionOuter' uOuter (tweakExecutionInner' uInner action)
                     )
                in tweakExecution
             )
-     in captureExistentials @ca @e outer tweakArgsOuter tweakExecutionOuter inner tweakArgsInner tweakExecutionInner
+     in captureExistentials @ca @e_ outer tweakArgsOuter tweakExecutionOuter inner tweakArgsInner tweakExecutionInner
 
-instance Monad m => Monoid (Advice ca e m r) where
+instance Monad m => Monoid (Advice ca e_ m r) where
   mappend = (<>)
   mempty = Advice (Proxy @()) (\args -> pure (pure args)) (const id)
 
@@ -249,7 +255,7 @@ --    type @u@ calculated earlier.
 --
 -- >>> :{
---  doesNothing :: forall ca e m r. Monad m => Advice ca e m r
+--  doesNothing :: forall ca e_ m r. Monad m => Advice ca e_ m r
 --  doesNothing = makeAdvice @() (\args -> pure (pure args)) (\() action -> action)
 -- :}
 --
@@ -257,20 +263,19 @@ --    type of the existential @u@ through a type application. Otherwise you'll
 --    get weird \"u is untouchable\" errors.
 makeAdvice ::
-  forall u ca e m r.
+  forall u ca e_ m r.
   -- | The function that tweaks the arguments.
   ( forall as.
     All ca as =>
     NP I as ->
-    DepT e m (u, NP I as)
+    DepT e_ m (u, NP I as)
   ) ->
   -- | The function that tweaks the execution.
-  ( 
-    u ->
-    DepT e m r ->
-    DepT e m r
+  ( u ->
+    DepT e_ m r ->
+    DepT e_ m r
   ) ->
-  Advice ca e m r
+  Advice ca e_ m r
 makeAdvice = Advice (Proxy @u)
 
 -- |
@@ -279,19 +284,19 @@ --    Notice that there's no @u@ parameter, unlike with 'makeAdvice'.
 --
 -- >>> :{
---  doesNothing :: forall ca e m r. Monad m => Advice ca e m r
+--  doesNothing :: forall ca e_ m r. Monad m => Advice ca e_ m r
 --  doesNothing = makeArgsAdvice pure
 -- :}
 makeArgsAdvice ::
-  forall ca e m r.
-  Monad m => 
+  forall ca e_ m r.
+  Monad m =>
   -- | The function that tweaks the arguments.
   ( forall as.
     All ca as =>
     NP I as ->
-    DepT e m (NP I as)
+    DepT e_ m (NP I as)
   ) ->
-  Advice ca e m r
+  Advice ca e_ m r
 makeArgsAdvice tweakArgs =
   makeAdvice @()
     ( \args -> do
@@ -306,18 +311,17 @@ --    Notice that there's no @u@ parameter, unlike with 'makeAdvice'.
 --
 -- >>> :{
---  doesNothing :: forall ca e m r. Monad m => Advice ca e m r
+--  doesNothing :: forall ca e_ m r. Monad m => Advice ca e_ m r
 --  doesNothing = makeExecutionAdvice id
 -- :}
 makeExecutionAdvice ::
-  forall ca e m r.
+  forall ca e_ m r.
   Applicative m =>
   -- | The function that tweaks the execution.
-  ( 
-    DepT e m r ->
-    DepT e m r
+  ( DepT e_ m r ->
+    DepT e_ m r
   ) ->
-  Advice ca e m r
+  Advice ca e_ m r
 makeExecutionAdvice tweakExecution = makeAdvice @() (\args -> pure (pure args)) (\() action -> tweakExecution action)
 
 data Pair a b = Pair !a !b
@@ -326,15 +330,15 @@ -- 'Ensure' is a helper for lifting typeclass definitions of the form:
 --
 -- >>> :{
---  type HasLogger :: Type -> (Type -> Type) -> Constraint
---  class HasLogger em m | em -> m where
---    logger :: em -> String -> m ()
+--  type HasLogger :: (Type -> Type) -> Type -> Constraint
+--  class HasLogger d e | e -> d where
+--    logger :: e -> String -> d ()
 -- :}
 --
--- To work as a constraints on the @e@ and @m@ parameters of an 'Advice', like this:
+-- To work as a constraints on the @e_@ and @m@ parameters of an 'Advice', like this:
 --
--- >>> :{ 
---  requiresLogger :: forall e m r. (Ensure HasLogger e m, Monad m) => Advice Show e m r
+-- >>> :{
+--  requiresLogger :: forall e_ m r. (Ensure HasLogger e_ m, Monad m) => Advice Show e_ m r
 --  requiresLogger = mempty
 --  :}
 --
@@ -344,15 +348,16 @@ -- (like those in <http://hackage.haskell.org/package/rio RIO>) whose type
 -- isn't parameterized by any monad.
 --
--- But the @e@ type parameter of 'Advice' has kind @(Type -> Type) -> Type@.
+-- But the @e_@ type parameter of 'Advice' has kind @(Type -> Type) -> Type@.
 -- That is: @NilEnv@ alone.
 --
 -- Furthermore, 'Advices' require @HasX@-style constraints to be placed on the
 -- @DepT@ transformer, not directly on the base monad @m@. @Ensure@ takes care
 -- of that as well.
-type Ensure :: (Type -> (Type -> Type) -> Constraint) -> ((Type -> Type) -> Type) -> (Type -> Type) -> Constraint
-type Ensure c e m = c (e (DepT e m)) (DepT e m) 
+type Ensure :: ((Type -> Type) -> Type -> Constraint) -> ((Type -> Type) -> Type) -> (Type -> Type) -> Constraint
 
+type Ensure c e_ m = c (DepT e_ m) (e_ (DepT e_ m)) 
+
 -- | Apply an 'Advice' to some compatible function. The function must have its
 -- effects in 'DepT', and all of its arguments must satisfy the @ca@ constraint.
 --
@@ -372,19 +377,19 @@ --  advisedBar2 = advise @Top returnMempty bar
 -- :}
 advise ::
-  forall ca e m r as advisee.
-  (Multicurryable as e m r advisee, All ca as, Monad m) =>
+  forall ca e_ m r as advisee.
+  (Multicurryable as e_ m r advisee, All ca as, Monad m) =>
   -- | The advice to apply.
-  Advice ca e m r ->
+  Advice ca e_ m r ->
   -- | A function to be adviced.
   advisee ->
   advisee
 advise (Advice _ tweakArgs tweakExecution) advisee = do
-  let uncurried = multiuncurry @as @e @m @r advisee
+  let uncurried = multiuncurry @as @e_ @m @r advisee
       uncurried' args = do
         (u, args') <- tweakArgs args
         tweakExecution u (uncurried args')
-   in multicurry @as @e @m @r uncurried'
+   in multicurry @as @e_ @m @r uncurried'
 
 type Multicurryable ::
   [Type] ->
@@ -393,25 +398,25 @@   Type ->
   Type ->
   Constraint
-class Multicurryable as e m r curried | curried -> as e m r where
-  type DownToBaseMonad as e m r curried :: Type
-  multiuncurry :: curried -> NP I as -> DepT e m r
-  multicurry :: (NP I as -> DepT e m r) -> curried
-  _runFromEnv :: m (e (DepT e m)) -> (e (DepT e m) -> curried) -> DownToBaseMonad as e m r curried
+class Multicurryable as e_ m r curried | curried -> as e_ m r where
+  type DownToBaseMonad as e_ m r curried :: Type
+  multiuncurry :: curried -> NP I as -> DepT e_ m r
+  multicurry :: (NP I as -> DepT e_ m r) -> curried
+  _runFromEnv :: m (e_ (DepT e_ m)) -> (e_ (DepT e_ m) -> curried) -> DownToBaseMonad as e_ m r curried
 
-instance Monad m => Multicurryable '[] e m r (DepT e m r) where
-  type DownToBaseMonad '[] e m r (DepT e m r) = m r
+instance Monad m => Multicurryable '[] e_ m r (DepT e_ m r) where
+  type DownToBaseMonad '[] e_ m r (DepT e_ m r) = m r
   multiuncurry action Nil = action
   multicurry f = f Nil
   _runFromEnv producer extractor = do
     e <- producer
     runDepT (extractor e) e
 
-instance Multicurryable as e m r curried => Multicurryable (a ': as) e m r (a -> curried) where
-  type DownToBaseMonad (a ': as) e m r (a -> curried) = a -> DownToBaseMonad as e m r curried
-  multiuncurry f (I a :* as) = multiuncurry @as @e @m @r @curried (f a) as
-  multicurry f a = multicurry @as @e @m @r @curried (f . (:*) (I a))
-  _runFromEnv producer extractor a = _runFromEnv @as @e @m @r @curried producer (\f -> extractor f a)
+instance Multicurryable as e_ m r curried => Multicurryable (a ': as) e_ m r (a -> curried) where
+  type DownToBaseMonad (a ': as) e_ m r (a -> curried) = a -> DownToBaseMonad as e_ m r curried
+  multiuncurry f (I a :* as) = multiuncurry @as @e_ @m @r @curried (f a) as
+  multicurry f a = multicurry @as @e_ @m @r @curried (f . (:*) (I a))
+  _runFromEnv producer extractor a = _runFromEnv @as @e_ @m @r @curried producer (\f -> extractor f a)
 
 -- | Given a base monad @m@ action that gets hold of the 'DepT' environment, run
 -- the 'DepT' transformer at the tip of a curried function.
@@ -423,14 +428,14 @@ --
 --  >>> runFinalDepT (pure NilEnv) foo 1 2 3 :: IO ()
 runFinalDepT ::
-  forall as e m r curried.
-  Multicurryable as e m r curried =>
+  forall as e_ m r curried.
+  Multicurryable as e_ m r curried =>
   -- | action that gets hold of the environment
-  m (e (DepT e m)) ->
+  m (e_ (DepT e_ m)) ->
   -- | function to invoke with effects in 'DepT'
   curried ->
   -- | a new function with effects in the base monad
-  DownToBaseMonad as e m r curried
+  DownToBaseMonad as e_ m r curried
 runFinalDepT producer extractor = _runFromEnv producer (const extractor)
 
 -- | Given a base monad @m@ action that gets hold of the 'DepT' environment,
@@ -461,14 +466,14 @@ -- Sum {getSum = 7}
 -- Sum {getSum = 0}
 runFromEnv ::
-  forall as e m r curried.
-  Multicurryable as e m r curried =>
+  forall as e_ m r curried.
+  Multicurryable as e_ m r curried =>
   -- | action that gets hold of the environment
-  m (e (DepT e m)) ->
+  m (e_ (DepT e_ m)) ->
   -- | gets a function from the environment with effects in 'DepT'
-  (e (DepT e m) -> curried) ->
+  (e_ (DepT e_ m) -> curried) ->
   -- | a new function with effects in the base monad
-  DownToBaseMonad as e m r curried
+  DownToBaseMonad as e_ m r curried
 runFromEnv = _runFromEnv
 
 -- $restrict
@@ -491,25 +496,23 @@ --    enough type information to the GADT, be it as an explicit signature:
 --
 -- >>> :{
---  stricterPrintArgs :: forall e m r. MonadIO m => Advice (Show `And` Eq `And` Ord) NilEnv m r
+--  stricterPrintArgs :: forall e_ m r. MonadIO m => Advice (Show `And` Eq `And` Ord) e_ m r
 --  stricterPrintArgs = restrictArgs (\Dict -> Dict) (printArgs stdout "foo")
 -- :}
 --
 --    or with a type application to 'restrictArgs':
 --
 -- >>> stricterPrintArgs = restrictArgs @(Show `And` Eq `And` Ord) (\Dict -> Dict) (printArgs stdout "foo")
---
---
 
 -- | Makes the constraint on the arguments more restrictive.
 restrictArgs ::
-  forall more less e m r.
+  forall more less e_ m r.
   -- | Evidence that one constraint implies the other. Every @x@ that has a @more@ instance also has a @less@ instance.
-  (forall x. Dict more x -> Dict less x) -> 
+  (forall x. Dict more x -> Dict less x) ->
   -- | Advice with less restrictive constraint on the args.
-  Advice less e m r ->
+  Advice less e_ m r ->
   -- | Advice with more restrictive constraint on the args.
-  Advice more e m r
+  Advice more e_ m r
 -- about the order of the type parameters... which is more useful?
 -- A possible principle to follow:
 -- We are likely to know the "less" constraint, because advices are likely to
@@ -520,24 +523,23 @@ -- type signature.  This seems to favor putting "more" first.
 restrictArgs evidence (Advice proxy tweakArgs tweakExecution) =
   let captureExistential ::
-        forall more less e m r u.
+        forall more less e_ m r u.
         (forall x. Dict more x -> Dict less x) ->
         Proxy u ->
         ( forall as.
           All less as =>
           NP I as ->
-          DepT e m (u, NP I as)
+          DepT e_ m (u, NP I as)
         ) ->
-        ( 
-          u ->
-          DepT e m r ->
-          DepT e m r
+        ( u ->
+          DepT e_ m r ->
+          DepT e_ m r
         ) ->
-        Advice more e m r
+        Advice more e_ m r
       captureExistential evidence' _ tweakArgs' tweakExecution' =
         Advice
           (Proxy @u)
-          ( let tweakArgs'' :: forall as. All more as => NP I as -> DepT e m (u, NP I as)
+          ( let tweakArgs'' :: forall as. All more as => NP I as -> DepT e_ m (u, NP I as)
                 tweakArgs'' = case Data.SOP.Dict.mapAll @more @less evidence' of
                   f -> case f (Dict @(All more) @as) of
                     Dict -> \args -> tweakArgs' @as args
@@ -545,6 +547,96 @@           )
           tweakExecution'
    in captureExistential evidence proxy tweakArgs tweakExecution
+
+--
+class Multicurryable as e_ m r curried => Deceivable as newtyped e_ m r curried where
+  type Deceived as newtyped e_ m r curried :: Type
+  _deceive :: (e_ (DepT e_ m) -> newtyped) -> Deceived as newtyped e_ m r curried -> curried
+
+instance Monad m => Deceivable '[] newtyped e_ m r (DepT e_ m r) where
+  type Deceived '[] newtyped e_ m r (DepT e_ m r) = ReaderT newtyped m r
+  _deceive f action = DepT (withReaderT f action)
+
+instance Deceivable as newtyped e_ m r curried => Deceivable (a ': as) newtyped e_ m r (a -> curried) where
+  type Deceived (a ': as) newtyped e_ m r (a -> curried) = a -> Deceived as newtyped e_ m r curried
+  _deceive f g a = deceive @as @newtyped @e_ @m @r f (g a)
+
+-- | Makes a function see a newtyped version of the environment record, a version that might have different @HasX@ instances.
+--
+-- The \"deception\" doesn't affect the dependencies used by the function, only the function itself.
+--
+-- For example, consider the following setup which features both a \"deceviced\"
+-- and an \"undeceived\" version of a controller function:
+--
+-- >>> :{
+--  type HasLogger :: (Type -> Type) -> Type -> Constraint
+--  class HasLogger d e | e -> d where
+--    logger :: e -> String -> d ()
+--  type HasIntermediate :: (Type -> Type) -> Type -> Constraint
+--  class HasIntermediate d e | e -> d where
+--    intermediate :: e -> String -> d ()
+--  type Env :: (Type -> Type) -> Type
+--  data Env m = Env
+--    { _logger1 :: String -> m (),
+--      _logger2 :: String -> m (),
+--      _intermediate :: String -> m (),
+--      _controllerA :: Int -> m (),
+--      _controllerB :: Int -> m ()
+--    }
+--  instance HasLogger m (Env m) where
+--    logger = _logger1
+--  instance HasIntermediate m (Env m) where
+--    intermediate = _intermediate
+--  newtype Switcheroo m = Switcheroo (Env m) 
+--      deriving newtype (HasIntermediate m)
+--  instance HasLogger m (Switcheroo m) where
+--    logger (Switcheroo e) = _logger2 e
+--  env :: Env (DepT Env (Writer [String]))
+--  env = 
+--    let mkController :: forall d e m. MonadDep [HasLogger, HasIntermediate] d e m => Int -> m () 
+--        mkController _ = do e <- ask; liftD $ logger e "foo" ; liftD $ intermediate e "foo"
+--        mkIntermediate :: forall d e m. MonadDep '[HasLogger] d e m => String -> m () 
+--        mkIntermediate _ = do e <- ask ; liftD $ logger e "foo" 
+--     in Env 
+--        {
+--          _logger1 = 
+--             \_ -> tell ["logger 1"],
+--          _logger2 = 
+--             \_ -> tell ["logger 2"],
+--          _intermediate =
+--             mkIntermediate, 
+--          _controllerA = 
+--             mkController,
+--          _controllerB = 
+--             deceive Switcheroo $
+--             mkController
+--        }
+-- :}
+--
+-- If we run @_controllerA@ the ouput is:
+--
+-- >>> execWriter $ runFromEnv (pure env) _controllerA 7
+-- ["logger 1","logger 1"]
+--
+-- But if we run the \"deceived\" @_controllerB@, we see that the function and its @_intermediate@ dependency use different loggers:
+--
+-- >>> execWriter $ runFromEnv (pure env) _controllerB 7
+-- ["logger 2","logger 1"]
+--
+-- Note that the function that is \"deceived\" must be polymorphic over
+-- 'Control.Monad.Dep.MonadDep'. Passing a function whose effect monad has
+-- already \"collapsed\" into 'DepT' won't work. Therefore, 'deceive' must be applied before any 'Advice'.
+--
+deceive ::
+  forall as newtyped e_ m r curried.
+  Deceivable as newtyped e_ m r curried =>
+  -- | The newtype constructor that masks the \"true\" environment.
+  (e_ (DepT e_ m) -> newtyped) ->
+  -- | A function to be deceived. It must be polymorphic over 'Control.Monad.Dep.MonadDep'.
+  Deceived as newtyped e_ m r curried ->
+  -- | The deceived function, that has effects in 'DepT'.
+  curried
+deceive = _deceive
 
 -- $sop
 -- Some useful definitions re-exported the from \"sop-core\" package.
lib/Control/Monad/Dep/Advice/Basic.hs view
@@ -54,7 +54,6 @@ -- >>> import Control.Monad.Dep
 -- >>> import Control.Monad.Dep.Advice
 -- >>> import Control.Monad.Dep.Advice.Basic (printArgs,returnMempty)
--- >>> import Data.Constraint
 -- >>> import Data.Kind
 -- >>> import Data.SOP
 -- >>> import Data.SOP.NP
@@ -68,7 +67,7 @@ --
 -- Because it doesn't touch the arguments or require some effect from the
 -- environment, this 'Advice' is polymorphic on @ca@ and @cem@.
-returnMempty :: forall ca e m r. (Monad m, Monoid r) => Advice ca e m r
+returnMempty :: forall ca e_ m r. (Monad m, Monoid r) => Advice ca e_ m r
 returnMempty =
   makeExecutionAdvice
     ( \action -> do
@@ -84,7 +83,7 @@ --
 -- Because it doesn't touch the return value of the advised function, this
 -- 'Advice' is polymorphic on @cr@.
-printArgs :: forall e m r. MonadIO m => Handle -> String -> Advice Show e m r
+printArgs :: forall e_ m r. MonadIO m => Handle -> String -> Advice Show e_ m r
 printArgs h prefix =
   makeArgsAdvice
     ( \args -> do
@@ -98,8 +97,12 @@ -- | Use 'local' on the final 'DepT' action of a function.
 --
 -- Allows tweaking the environment that will be seen by the function and all of
--- its sub-calls.
+-- its sub-calls into dependencies. 
 --
+-- Note that perhaps this is __not__ what you want; often it's better to tweak
+-- the environment for the current function only. For those cases,
+-- 'Control.Monad.Dep.Advice.deceive' might be a better fit. 
+--
 -- >>> :{
 --  type HasLogger :: Type -> (Type -> Type) -> Constraint
 --  class HasLogger em m | em -> m where
@@ -116,12 +119,16 @@ --  envIO :: Env (DepT Env IO)
 --  envIO = Env 
 --    {
---      _logger1 = \_ -> liftIO $ putStrLn "logger1 ran",
---      _logger2 = \_ -> liftIO $ putStrLn "logger2 ran",
---      _controllerA = \_ -> do e <- ask; logger e "foo",
---      _controllerB = advise @Top 
---                     (doLocally \e@Env{_logger2} -> e {_logger1 = _logger2}) 
---                     \_ -> do e <- ask; logger e "foo" 
+--      _logger1 = 
+--          \_ -> liftIO $ putStrLn "logger1 ran",
+--      _logger2 = 
+--          \_ -> liftIO $ putStrLn "logger2 ran",
+--      _controllerA = 
+--          \_ -> do e <- ask; logger e "foo",
+--      _controllerB = 
+--          advise @Top 
+--          (doLocally \e@Env{_logger2} -> e {_logger1 = _logger2}) 
+--          \_ -> do e <- ask; logger e "foo" 
 --    }
 -- :}
 --
@@ -131,7 +138,7 @@ --  >>> runFromEnv (pure envIO) _controllerB 0
 --  logger2 ran
 --
-doLocally :: forall ca e m r. Monad m => (forall n. e n -> e n) -> Advice ca e m r 
+doLocally :: forall ca e_ m r. Monad m => (forall n. e_ n -> e_ n) -> Advice ca e_ m r 
 doLocally transform = makeExecutionAdvice (local transform)  
 
 
@@ -162,7 +169,7 @@ --
 -- A better implementation of this advice would likely use an @AnyHashable@
 -- helper datatype for the keys.
-doCachingBadly :: forall e m r. Monad m => (AnyEq -> m (Maybe r)) -> (AnyEq -> r -> m ()) -> Advice (Eq `And` Typeable) e m r
+doCachingBadly :: forall e_ m r. Monad m => (AnyEq -> m (Maybe r)) -> (AnyEq -> r -> m ()) -> Advice (Eq `And` Typeable) e_ m r
 doCachingBadly cacheLookup cachePut =
   makeAdvice @AnyEq
     ( \args ->
@@ -186,7 +193,7 @@ -- package instead of bare `forkIO`. 
 --
 -- And the @MustBe IO@ constraint could be relaxed to @MonadUnliftIO@.
-doAsyncBadly :: forall ca e. Advice ca e IO ()
+doAsyncBadly :: forall ca e_ . Advice ca e_ IO ()
 doAsyncBadly = makeExecutionAdvice (\action -> do
         e <- ask 
         _ <- liftIO $ forkIO $ runDepT action e
test/tests.hs view
@@ -12,6 +12,7 @@ {-# LANGUAGE UndecidableInstances #-}
 {-# LANGUAGE TypeOperators #-}
 {-# LANGUAGE ConstraintKinds #-}
+{-# LANGUAGE DataKinds #-}
 
 module Main (main) where
 
@@ -36,29 +37,29 @@ -- Has-style typeclasses can be provided to avoid depending on concrete
 -- environments.
 -- Note that the environment determines the monad.
-type HasLogger :: Type -> (Type -> Type) -> Constraint
-class HasLogger r m | r -> m where
-  logger :: r -> String -> m ()
+type HasLogger :: (Type -> Type) -> Type -> Constraint
+class HasLogger d r | r -> d where
+  logger :: r -> String -> d ()
 
 -- Possible convenience function to avoid having to use ask before logging
 -- Worth the extra boilerplate, or not?
-logger' :: (MonadReader e m, HasLogger e m) => String -> m ()
-logger' msg = asks logger >>= \f -> f msg
+loggerD :: MonadDep '[HasLogger] d e m => String -> m ()
+loggerD msg = asks logger >>= \f -> liftD $ f msg
 
-type HasRepository :: Type -> (Type -> Type) -> Constraint
-class HasRepository r m | r -> m where
-  repository :: r -> Int -> m ()
+type HasRepository :: (Type -> Type) -> Type -> Constraint
+class HasRepository d r | r -> d where
+  repository :: r -> Int -> d ()
 
 -- Some possible implementations.
 --
 -- An implementation of the controller, done programming against interfaces
 -- (well, against typeclasses).
 -- Polymorphic on the monad.
-mkController :: (MonadReader e m, HasLogger e m, HasRepository e m) => Int -> m String
+mkController :: MonadDep [HasLogger, HasRepository] d e m => Int -> m String
 mkController x = do
   e <- ask
-  logger e "I'm going to insert in the db!"
-  repository e x
+  liftD $ logger e "I'm going to insert in the db!"
+  liftD $ repository e x
   return "view"
 
 -- A "real" logger implementation that interacts with the external world.
@@ -66,10 +67,10 @@ mkStdoutLogger msg = liftIO (putStrLn msg)
 
 -- A "real" repository implementation
-mkStdoutRepository :: (MonadReader e m, HasLogger e m, MonadIO m) => Int -> m ()
+mkStdoutRepository :: (MonadDep '[HasLogger] d e m, MonadIO m) => Int -> m ()
 mkStdoutRepository entity = do
   e <- ask
-  logger e "I'm going to write the entity!"
+  liftD $ logger e "I'm going to write the entity!"
   liftIO $ print entity
 
 -- The traces we accumulate from the fakes during tests
@@ -80,10 +81,10 @@ mkFakeLogger msg = tell ([msg], mempty)
 
 -- Ditto.
-mkFakeRepository :: (MonadReader e m, HasLogger e m, MonadWriter TestTrace m) => Int -> m ()
+mkFakeRepository :: (MonadDep '[HasLogger] d e m, MonadWriter TestTrace m) => Int -> m ()
 mkFakeRepository entity = do
   e <- ask
-  logger e "I'm going to write the entity!"
+  liftD $ logger e "I'm going to write the entity!"
   tell ([], [entity])
 
 --
@@ -95,10 +96,10 @@     _repositoryIO :: Int -> IO ()
   }
 
-instance HasLogger EnvIO IO where
+instance HasLogger IO EnvIO where
   logger = _loggerIO
 
-instance HasRepository EnvIO IO where
+instance HasRepository IO EnvIO where
   repository = _repositoryIO
 
 -- In the monomorphic environment, the controller function lives "separate",
@@ -112,7 +113,7 @@ -- 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 String
+mkControllerIO :: (HasLogger IO e, HasRepository IO e) => Int -> ReaderT e IO String
 mkControllerIO x = do
   e <- ask
   liftIO $ logger e "I'm going to insert in the db!"
@@ -134,10 +135,10 @@ 
 -- If our environment is parmeterized by the monad m, then logging is done in
 -- m.
-instance HasLogger (Env m) m where
+instance HasLogger m (Env m) where
   logger = _logger
 
-instance HasRepository (Env m) m where
+instance HasRepository m (Env m) where
   repository = _repository
 
 -- This bigger environment is for demonstrating how to "nest" environments.
@@ -272,28 +273,28 @@     _logger2 :: String -> m ()
     }
 
-instance HasLogger (CachingTestEnv m) m where
+instance HasLogger m (CachingTestEnv m) where
   logger = _logger2
 
-type HasExpensiveComputation :: Type -> (Type -> Type) -> Constraint
-class HasExpensiveComputation r m | r -> m where
-  expensiveComputation :: r -> Int -> Bool -> m String
-instance HasExpensiveComputation (CachingTestEnv m) m where
+type HasExpensiveComputation :: (Type -> Type) -> Type -> Constraint
+class HasExpensiveComputation d r | r -> d where
+  expensiveComputation :: r -> Int -> Bool -> d String
+instance HasExpensiveComputation m (CachingTestEnv m) where
   expensiveComputation = _expensiveComputation 
 
-mkFakeExpensiveComputation :: (MonadReader e m, HasLogger e m) => Int -> Bool -> m String
+mkFakeExpensiveComputation :: MonadDep '[HasLogger] d e m => Int -> Bool -> m String
 mkFakeExpensiveComputation i b = do
     e <- ask
-    logger e "Doing expensive computation"
+    liftD $ logger e "Doing expensive computation"
     return $ (show i ++ show b)
 
-cacheTestLogic :: (MonadReader e m, HasLogger e m, HasExpensiveComputation e m) => m ()
+cacheTestLogic :: MonadDep [HasLogger, HasExpensiveComputation] d e m => m ()
 cacheTestLogic = do
     e <- ask
-    expensiveComputation e 0 False >>= logger e
-    expensiveComputation e 1 True >>= logger e
-    expensiveComputation e 0 False >>= logger e
-    expensiveComputation e 1 True >>= logger e
+    liftD $ expensiveComputation e 0 False >>= logger e
+    liftD $ expensiveComputation e 1 True >>= logger e
+    liftD $ expensiveComputation e 0 False >>= logger e
+    liftD $ expensiveComputation e 1 True >>= logger e
 
 type ExpensiveComputationMonad = RWS () ([String],()) [(AnyEq,String)]