deriving-trans 0.2.0.0 → 0.2.1.0
raw patch · 4 files changed
+126/−45 lines, 4 filesdep −lifted-basePVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependencies removed: lifted-base
API changes (from Hackage documentation)
+ Control.Monad.Trans.Elevator: instance Control.Monad.Trans.Class.MonadTrans t => Control.Monad.Trans.Class.MonadTrans (Control.Monad.Trans.Elevator.Elevator t)
+ Control.Monad.Trans.Elevator: instance Control.Monad.Trans.Control.MonadTransControl t => Control.Monad.Trans.Control.MonadTransControl (Control.Monad.Trans.Elevator.Elevator t)
- Control.Monad.Trans.Compose: ComposeT :: t1 (t2 m) a -> ComposeT (t1 :: (Type -> Type) -> Type -> Type) (t2 :: (Type -> Type) -> Type -> Type) (m :: Type -> Type) (a :: Type)
+ Control.Monad.Trans.Compose: ComposeT :: t1 (t2 m) a -> ComposeT t1 t2 m a
- Control.Monad.Trans.Compose: [deComposeT] :: ComposeT (t1 :: (Type -> Type) -> Type -> Type) (t2 :: (Type -> Type) -> Type -> Type) (m :: Type -> Type) (a :: Type) -> t1 (t2 m) a
+ Control.Monad.Trans.Compose: [deComposeT] :: ComposeT t1 t2 m a -> t1 (t2 m) a
- Control.Monad.Trans.Compose: newtype ComposeT (t1 :: (Type -> Type) -> Type -> Type) (t2 :: (Type -> Type) -> Type -> Type) (m :: Type -> Type) (a :: Type)
+ Control.Monad.Trans.Compose: newtype ComposeT t1 t2 m a
- Control.Monad.Trans.Elevator: Ascend :: t m a -> Elevator (t :: (Type -> Type) -> Type -> Type) (m :: Type -> Type) (a :: Type)
+ Control.Monad.Trans.Elevator: Ascend :: t m a -> Elevator t m a
- Control.Monad.Trans.Elevator: [descend] :: Elevator (t :: (Type -> Type) -> Type -> Type) (m :: Type -> Type) (a :: Type) -> t m a
+ Control.Monad.Trans.Elevator: [descend] :: Elevator t m a -> t m a
- Control.Monad.Trans.Elevator: newtype Elevator (t :: (Type -> Type) -> Type -> Type) (m :: Type -> Type) (a :: Type)
+ Control.Monad.Trans.Elevator: newtype Elevator t m a
Files
- CHANGELOG.md +5/−0
- deriving-trans.cabal +13/−8
- src/Control/Monad/Trans/Compose.hs +32/−17
- src/Control/Monad/Trans/Elevator.hs +76/−20
CHANGELOG.md view
@@ -1,5 +1,10 @@ # Revision history for deriving-trans +## 0.2.1.0 *27 Jan 2022*++* Use StandaloneKindSignatures.+* Add Haddock documentation.+ ## 0.2.0.0 *25 Jan 2022* * Completely change the idea of this project. Pretty much everything has been changed.
deriving-trans.cabal view
@@ -1,12 +1,17 @@ cabal-version: 3.0 name: deriving-trans-version: 0.2.0.0-synopsis: Derive monad type classes with DerivingVia-description: Deriving type class instances for monad transformer stacks can be tedious.- By actually composing transformers with `ComposeT` we don't need to implement- each type class for each transformer.- Instead we need a single instance for `Elevator`, which will then help us pick- the correct instance with DerivingVia.+version: 0.2.1.0+synopsis: Derive instances for monad transformer stacks+description: Implementing instances for monad transformer stacks can be tedious.+ <https://hackage.haskell.org/package/mtl mtl> defines each instance for each transfomer.++ 'Elevator' let's you access instances of the underlying monad of a transformer.+ Composing transformers with 'ComposeT' brings instances into scope during the+ initialization of a transformer stack.++ 'Elevator' can be used with DerivingVia to select a specific transformer to+ derive instances for a transformer stack.+ 'ComposeT' composes transformers and accumulates instances in a transformer stack. license: BSD-3-Clause license-file: LICENSE author: Felix Springer@@ -24,7 +29,6 @@ --other-modules: build-depends: , base >= 4.5 && < 5- , lifted-base >= 0.2.3.2 && < 0.2.4 , monad-control >= 1.0.2.0 && < 1.1 , mtl >= 2.2.2 && < 2.3 , transformers >= 0.5.6.2 && < 0.5.7@@ -51,6 +55,7 @@ OverloadedStrings RankNTypes StandaloneDeriving+ StandaloneKindSignatures TypeApplications TypeFamilies ghc-options: -Wall
src/Control/Monad/Trans/Compose.hs view
@@ -13,12 +13,16 @@ import Control.Monad.Writer.Class import Data.Kind -newtype ComposeT- (t1 :: (Type -> Type) -> Type -> Type)- (t2 :: (Type -> Type) -> Type -> Type)- (m :: Type -> Type)- (a :: Type)- = ComposeT { deComposeT :: t1 (t2 m) a }+-- | A newtype wrapper for two stacked monad transformers.+--+-- Access instances of the intermediate monad @('t2' 'm')@, whenever 't1' implements+-- 'MonadTrans'/'MonadTransControl'.+type ComposeT :: ((Type -> Type) -> Type -> Type) -- ^ 't1'+ -> ((Type -> Type) -> Type -> Type) -- ^ 't2'+ -> (Type -> Type) -- ^ 'm'+ -> Type -- ^ 'a'+ -> Type+newtype ComposeT t1 t2 m a = ComposeT { deComposeT :: t1 (t2 m) a } deriving newtype (Applicative, Functor, Monad) instance (forall m. Monad m => Monad (t2 m), MonadTrans t1, MonadTrans t2) => MonadTrans (ComposeT t1 t2) where@@ -29,7 +33,7 @@ liftWith f = defaultLiftWith2 ComposeT deComposeT $ \ x -> f x restoreT = defaultRestoreT2 ComposeT --- | Elevated to `m`.+-- | Elevated to 'm'. deriving via Elevator (ComposeT t1 t2) m instance ( Monad (t1 (t2 m))@@ -37,7 +41,7 @@ , MonadIO m ) => MonadIO (ComposeT t1 t2 m) --- | Elevated to `m`.+-- | Elevated to 'm'. deriving via Elevator (ComposeT t1 t2) m instance ( Monad (t1 (t2 m))@@ -45,7 +49,7 @@ , MonadBase b m ) => MonadBase b (ComposeT t1 t2 m) --- | Elevated to `m`.+-- | Elevated to 'm'. deriving via Elevator (ComposeT t1 t2) m instance ( Monad (t1 (t2 m))@@ -53,7 +57,7 @@ , MonadBaseControl b m ) => MonadBaseControl b (ComposeT t1 t2 m) --- | Elevated to `t2 m`.+-- | Elevated to @('t2' 'm')@. deriving via Elevator t1 (t2 (m :: * -> *)) instance ( Monad (t1 (t2 m))@@ -61,7 +65,7 @@ , MonadError e (t2 m) ) => MonadError e (ComposeT t1 t2 m) --- | Elevated to `t2 m`.+-- | Elevated to @('t2' 'm')@. deriving via Elevator t1 (t2 (m :: * -> *)) instance ( Monad (t1 (t2 m))@@ -69,7 +73,7 @@ , MonadReader r (t2 m) ) => MonadReader r (ComposeT t1 t2 m) --- | Elevated to `t2 m`.+-- | Elevated to @('t2' 'm')@. deriving via Elevator t1 (t2 (m :: * -> *)) instance ( Monad (t1 (t2 m))@@ -77,7 +81,7 @@ , MonadState s (t2 m) ) => MonadState s (ComposeT t1 t2 m) --- | Elevated to `t2 m`.+-- | Elevated to @('t2' 'm')@. deriving via Elevator t1 (t2 (m :: * -> *)) instance ( Monad (t1 (t2 m))@@ -85,12 +89,23 @@ , MonadWriter w (t2 m) ) => MonadWriter w (ComposeT t1 t2 m) -runComposeT :: (forall a. t1 (t2 m) a -> t2 m (StT t1 a))- -> (forall a. t2 m a -> m (StT t2 a))+-- | Run a transformer stack.+--+-- This function takes the two individual monad transformer runners as arguments.+runComposeT :: (forall a. t1 (t2 m) a -> t2 m (StT t1 a)) -- ^ run 't1'+ -> (forall a. t2 m a -> m (StT t2 a)) -- ^ run 't2' -> (forall a. ComposeT t1 t2 m a -> m (StT t2 (StT t1 a))) runComposeT runT1 runT2 = runT2 . runT1 . deComposeT -runComposeT' :: (t1 (t2 m) a -> t2 m a)- -> (t2 m a -> m a)+-- | Equivalent to 'runComposeT', but discards the monadic state 'StT'.+-- This is a simple approach when your monad transformer stack doesn't carry monadic state.+--+-- @+-- 'StT' ('ComposeT' 't1' 't2') a ~ a+-- @+--+-- This can be used to improve error messages when modifying a monad transformer stack.+runComposeT' :: (t1 (t2 m) a -> t2 m a) -- ^ run 't1'+ -> (t2 m a -> m a) -- ^ run 't2' -> (ComposeT t1 t2 m a -> m a) runComposeT' runT1 runT2 = runT2 . runT1 . deComposeT
src/Control/Monad/Trans/Elevator.hs view
@@ -11,40 +11,96 @@ import Control.Monad.Writer.Class import Data.Kind -newtype Elevator- (t :: (Type -> Type) -> Type -> Type)- (m :: Type -> Type)- (a :: Type)- = Ascend { descend :: t m a }+-- | A newtype wrapper for monad transformers.+--+-- Access instances of the inner monad 'm'.+type Elevator :: ((Type -> Type) -> Type -> Type) -- ^ 't'+ -> (Type -> Type) -- ^ 'm'+ -> Type -- ^ 'a'+ -> Type+newtype Elevator t m a = Ascend { descend :: t m a } deriving newtype (Applicative, Functor, Monad)+ deriving newtype (MonadTrans, MonadTransControl) instance (Monad (t m), MonadTrans t, MonadBase b m) => MonadBase b (Elevator t m) where- liftBase = Ascend . lift . liftBase+ liftBase = lift . liftBase instance (Monad (t m), MonadTransControl t, MonadBaseControl b m) => MonadBaseControl b (Elevator t m) where type StM (Elevator t m) a = StM m (StT t a)- liftBaseWith f = Ascend $ liftWith $ \ runT -> liftBaseWith $ \ runInBase -> f $ runInBase . runT . descend- restoreM = Ascend . restoreT . restoreM+ liftBaseWith f = liftWith $ \ runT -> liftBaseWith $ \ runInBase -> f $ runInBase . runT+ restoreM = restoreT . restoreM instance (Monad (t m), MonadTrans t, MonadIO m) => MonadIO (Elevator t m) where- liftIO = Ascend . lift . liftIO+ liftIO = lift . liftIO instance (Monad (t m), MonadTransControl t, MonadError e m) => MonadError e (Elevator t m) where- throwError = Ascend . lift . throwError- catchError throwing catching = Ascend $ (restoreT . pure =<<) $ liftWith $ \ runT ->- catchError (runT $ descend throwing) (runT . descend . catching)+ throwError = lift . throwError+ catchError throwing catching = (restoreT . pure =<<) $ liftWith $ \ runT ->+ catchError (runT throwing) (runT . catching) instance (Monad (t m), MonadTransControl t, MonadReader r m) => MonadReader r (Elevator t m) where- ask = Ascend $ lift ask- local f tma = Ascend $ (restoreT . pure =<<) $ liftWith $ \ runT ->- local f $ runT $ descend tma+ ask = lift ask+ local f tma = (restoreT . pure =<<) $ liftWith $ \ runT ->+ local f $ runT tma instance (Monad (t m), MonadTrans t, MonadState s m) => MonadState s (Elevator t m) where- get = Ascend $ lift get- put = Ascend . lift . put+ get = lift get+ put = lift . put instance (Monad (t m), MonadTransControl t, MonadWriter w m) => MonadWriter w (Elevator t m) where- tell = Ascend . lift . tell- listen tma = Ascend $ liftWith (\ runT -> listen $ runT $ descend tma) >>= \ (sta, w) ->+ tell = lift . tell+ listen tma = liftWith (\ runT -> listen $ runT tma) >>= \ (sta, w) -> (, w) <$> restoreT (pure sta)- pass tma = Ascend $ lift . pass . pure =<< descend tma+ pass tma = lift . pass . pure =<< tma++-- * Examples+--+-- $examples+--+-- == Example 1: Recover submerged instances+--+-- Let's assume you want to define a monad transformer stack.+--+-- @+-- newtype StackT m a = StackT { unStackT :: ReaderT Char (ReaderT Bool m) a }+-- deriving newtype (Functor, Applicative Monad)+-- @+--+-- Now you want to expose the inner @(MonadReader Bool)@ instance with @(StackT m)@.+--+-- Normally it's shadowed by the @(MonadReader Char)@ instance, but we can use @Elevator@ to access+-- the inner transformer.+--+-- @+-- deriving (MonadReader Bool) via (Elevator (ReaderT Char) (ReaderT Bool m))+-- @+--+-- == Example 2: Custom transformer without boilerplate+--+-- Let's assume you have defined a monad transformer.+--+-- @+-- newtype CustomT m a = CustomT { unComposeT :: IdentityT m a }+-- deriving newtype (Functor, Applicative Monad)+-- deriving newtype (MonadTrans, MonadTransControl)+--+-- runCustomT :: CustomT m a -> m a+-- runCustomT = runIdentityT . unComposeT+-- @+--+-- Now you want to use this monad transformer in a transformer stack.+--+-- @+-- newtype StackT m a = { unStackT :: CustomT (ReaderT Bool m) a }+-- deriving newtype (Functor, Applicative Monad)+-- deriving newtype (MonadTrans, MonadTransControl)+-- @+--+-- Unfortunately we can't derive a @Monad m => MonadReader Bool (StackT m)@ instance with+-- GeneralizedNewtypeDeriving, without also adding the instance to @CustomT@.+--+-- To still derive this trivial instance we can use @Elevator@ with DerivingVia.+--+-- @+-- deriving (MonadReader Bool) via (Elevator CustomT (ReaderT Bool m))+-- @