deriving-trans 0.2.2.0 → 0.2.2.1
raw patch · 4 files changed
+95/−93 lines, 4 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
Files
- CHANGELOG.md +4/−0
- deriving-trans.cabal +1/−1
- src/Control/Monad/Trans/Compose.hs +64/−63
- src/Control/Monad/Trans/Elevator.hs +26/−29
CHANGELOG.md view
@@ -1,5 +1,9 @@ # Revision history for deriving-trans +## 0.2.2.1 *01 Feb 2022*++* Polish Haddock comments.+ ## 0.2.2.0 *30 Jan 2022* * Add "base-case" instances for mtl's type classes to `ComposeT`.
deriving-trans.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: deriving-trans-version: 0.2.2.0+version: 0.2.2.1 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
src/Control/Monad/Trans/Compose.hs view
@@ -15,25 +15,25 @@ -- -- $composet ----- 'ComposeT' can be used in monad transformer stacks to derive instances in a clean way.+-- 'ComposeT' can be used in monad transformer stacks to derive instances. -- -- This also allows the usage of these instances, while in the middle of the transformer stack. -- This proves particularly useful, when writing a runner for a transformer stack. -- | A newtype wrapper for two stacked monad transformers. ----- Access instances of the intermediate monad @('t2' 'm')@, whenever 't1' implements--- 'MonadTrans'/'MonadTransControl'.+-- Access instances of the intermediate monad @(t2 m)@, whenever @t1@ implements 'MonadTrans' /+-- 'MonadTransControl'. -- -- ==== Type level arguments--- [@'t1' :: ('Type' -> 'Type') -> 'Type' -> 'Type'@] outer monad transformer--- [@'t2' :: ('Type' -> 'Type') -> 'Type' -> 'Type'@] inner monad transformer--- [@'m' :: 'Type' -> 'Type'@] monad--- [@'a' :: 'Type'@] value-type ComposeT :: ((Type -> Type) -> Type -> Type) -- ^ 't1'- -> ((Type -> Type) -> Type -> Type) -- ^ 't2'- -> (Type -> Type) -- ^ 'm'- -> Type -- ^ 'a'+-- [@t1 :: ('Type' -> 'Type') -> 'Type' -> 'Type'@] outer monad transformer+-- [@t2 :: ('Type' -> 'Type') -> 'Type' -> 'Type'@] inner monad transformer+-- [@m :: 'Type' -> 'Type'@] monad+-- [@a :: 'Type'@] value+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)@@ -46,7 +46,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))@@ -54,7 +54,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))@@ -62,7 +62,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))@@ -71,7 +71,7 @@ ) => MonadBaseControl b (ComposeT t1 t2 m) -- | /OVERLAPPABLE/.--- Elevated to @('t2' 'm')@.+-- Elevated to @(t2 m)@. deriving via Elevator t1 (t2 (m :: * -> *)) instance {-# OVERLAPPABLE #-} ( Monad (t1 (t2 m))@@ -86,7 +86,7 @@ ) => MonadError e ((ComposeT (ExceptT e) t2) m) -- | /OVERLAPPABLE/.--- Elevated to @('t2' 'm')@.+-- Elevated to @(t2 m)@. deriving via Elevator t1 (t2 (m :: * -> *)) instance {-# OVERLAPPABLE #-} ( Monad (t1 (t2 m))@@ -101,7 +101,7 @@ ) => MonadReader r ((ComposeT (ReaderT r) t2) m) -- | /OVERLAPPABLE/.--- Elevated to @('t2' 'm')@.+-- Elevated to @(t2 m)@. deriving via Elevator t1 (t2 (m :: * -> *)) instance {-# OVERLAPPABLE #-} ( Monad (t1 (t2 m))@@ -116,7 +116,7 @@ ) => MonadState s ((ComposeT (StateT s) t2) m) -- | /OVERLAPPABLE/.--- Elevated to @('t2' 'm')@.+-- Elevated to @(t2 m)@. deriving via Elevator t1 (t2 (m :: * -> *)) instance {-# OVERLAPPABLE #-} ( Monad (t1 (t2 m))@@ -166,57 +166,59 @@ -- $example1 -- -- When creating a new type class that supports 'ComposeT', you want to add recursive instances for--- `ComposeT`.+-- 'ComposeT'. -- -- @--- class Monad m => MonadCustom m where+-- class 'Monad' m => MonadCustom m where -- simpleMethod :: a -> m a -- complicatedMethod :: (a -> m a) -> m a -- @ -- -- You can easily derive those instances, after implementing an instance for 'Elevator'.+-- This is explained in "Control.Monad.Trans.Elevator". -- -- Then it's possible to derive the recursive instance.--- This is an /OVERLAPPABLE/ instance, because we want to be able to add new instances through--- transformers in a stack.+-- This is an /OVERLAPPABLE/ instance, because we want to be able to add new "base-case" instances+-- through transformers in a stack. -- -- @--- deriving via Elevator t1 (t2 (m :: * -> *))+-- deriving via 'Elevator' t1 (t2 (m :: * -> *)) -- instance {-# OVERLAPPABLE #-}--- ( Monad (t1 (t2 m))--- , MonadTransControl t1+-- ( 'Monad' (t1 (t2 m))+-- , 'MonadTransControl' t1 -- , MonadCustom (t2 m)--- ) => MonadCustom (ComposeT t1 t2 m)+-- ) => MonadCustom ('ComposeT' t1 t2 m) -- @ -- ** Example 2: Add an instance -- -- $example2 ----- Add a type class instance for a new monad transformer, when there already is a recursive instance for 'ComposeT'.+-- Add a type class instance for a new monad transformer, when there already is a recursive instance+-- for 'ComposeT'. -- -- @--- newtype CustomT m a = CustomT { unCustomT :: IdentityT m a }--- deriving newtype (Functor, Applicative, Monad)--- deriving newtype (MonadTrans, MonadTransControl)+-- newtype CustomT m a = CustomT { unCustomT :: 'Control.Monad.Trans.Identity.IdentityT' m a }+-- deriving newtype ('Functor', 'Applicative', 'Monad')+-- deriving newtype ('MonadTrans', 'MonadTransControl') -- @ -- -- First we need the regular instance.--- The method implementations are 'undefined' here, because they are not related to 'ComposeT'.+-- The method implementations are 'undefined' here, because they would only distract from+-- 'ComposeT'. -- -- @--- instance Monad m => MonadCustom (CustomT m) where--- simpleMethod = undefined--- complicatedMethod = undefined+-- instance 'Monad' m => MonadCustom (CustomT m) where+-- simpleMethod = 'undefined'+-- complicatedMethod = 'undefined' -- @ ----- To add an instance that takes priority over the recursive instance /FlexibleInstances/ are required.+-- To add a "base-case" instance, that takes priority over the recursive instance,+-- /FlexibleInstances/ are required. -- -- @ -- deriving via CustomT (t2 (m :: * -> *))--- instance--- ( Monad (t2 m)--- ) => MonadCustom ((ComposeT CustomT t2) m)+-- instance 'Monad' (t2 m) => MonadCustom (('ComposeT' CustomT t2) m) -- @ -- ** Example 3: Build a transformer stack@@ -226,29 +228,25 @@ -- Create a monad transformer stack and wrap it using a newtype. -- -- @--- type (|.) = ComposeT--- type Stack = StateT Int |. ReaderT Char |. CustomT |. ReaderT Bool |. IdentityT+-- type (|.) = 'ComposeT'+-- type Stack = 'StateT' 'Int' |. 'ReaderT' 'Char' |. CustomT |. 'ReaderT' 'Bool' |. 'Control.Monad.Trans.Identity.IdentityT' -- newtype StackT m a = StackT { unStackT :: Stack m a }--- deriving newtype (Functor, Applicative, Monad)+-- deriving newtype ('Functor', 'Applicative', 'Monad') -- @ ----- We are adding 'IdentityT' to the stack, so that all the other transformer instances end up in the stack.+-- We are adding 'Control.Monad.Trans.Identity.IdentityT' to the end of the stack, so that all the+-- other transformer instances end up in the stack. -- Now we can simply derive just the instances, that we want. -- -- @--- deriving newtype (MonadState Int)+-- deriving newtype ('MonadState' 'Int') -- deriving newtype MonadCustom -- @ ----- We can even use 'Elevator' to access instances, that have been shadowed in the stack.+-- We can even access instances, that would have been shadowed in a regular transformer stack. -- -- @--- deriving (MonadReader Bool) via--- ( (StateT Int)--- ( Elevator (ReaderT Char)--- ( CustomT--- ( ReaderT Bool--- ( IdentityT m)))))+-- deriving newtype ('MonadReader' 'Bool') -- @ -- ** Example 4: Run a transformer stack@@ -256,28 +254,31 @@ -- $example4 -- -- This is the part, that actually contains your application logic.--- Because of the setup with `ComposeT`, we won't have to worry about 'lift'ing during the+-- Because of the setup with 'ComposeT', we won't have to worry about 'lift'ing during the -- initialization. -- -- @--- runStackT :: MonadBaseControl IO m+-- runStackT :: 'MonadBaseControl' 'IO' m -- => StackT m a -- -> m (StT StackT a)--- runStackT stackTma = do--- let--- runReaderT' :: MonadReader Bool m => ReaderT Char m a -> m a+-- runStackT stackTma =+-- runStateT' |.+-- runReaderT' |.+-- runCustomT |.+-- (\\ tma -> 'runReaderT' tma 'True') |.+-- 'Control.Monad.Trans.Identity.runIdentityT' $ unStackT stackTma+-- where+-- runReaderT' :: 'MonadReader' 'Bool' m => 'ReaderT' 'Char' m a -> m a -- runReaderT' tma = do--- bool <- ask+-- bool <- 'ask' -- let char = if bool -- then \'Y\' -- else \'N\'--- runReaderT tma char+-- 'runReaderT' tma char ----- runStateT' :: MonadReader Char m => StateT Int m a -> m (a, Int)+-- runStateT' :: 'MonadReader' 'Char' m => 'StateT' 'Int' m a -> m (a, 'Int') -- runStateT' tma = do--- char <- ask--- let num = fromEnum char--- runStateT tma num------ runStateT' |. runReaderT' |. runCustomT |. (\\ tma -> runReaderT tma True) |. runIdentityT $ unStackT stackTma+-- char <- 'ask'+-- let num = 'fromEnum' char+-- 'runStateT' tma num -- @
src/Control/Monad/Trans/Elevator.hs view
@@ -22,15 +22,15 @@ -- | A newtype wrapper for monad transformers. ----- Access instances of the inner monad 'm'.+-- Access instances of the inner monad @m@. -- -- ==== Type level arguments--- [@'t' :: ('Type' -> 'Type') -> 'Type' -> 'Type'@] monad transformer--- [@'m' :: 'Type' -> 'Type'@] monad--- [@'a' :: 'Type'@] value-type Elevator :: ((Type -> Type) -> Type -> Type) -- ^ 't'- -> (Type -> Type) -- ^ 'm'- -> Type -- ^ 'a'+-- [@t :: ('Type' -> 'Type') -> 'Type' -> 'Type'@] monad transformer+-- [@m :: 'Type' -> 'Type'@] monad+-- [@a :: 'Type'@] value+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)@@ -76,17 +76,17 @@ -- 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)+-- newtype StackT m a = StackT { unStackT :: 'Control.Monad.Trans.Reader.ReaderT' 'Char' ('Control.Monad.Trans.Reader.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.+-- 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)+-- deriving ('MonadReader' 'Bool') via 'Elevator' ('Control.Monad.Trans.Reader.ReaderT' 'Char') ('Control.Monad.Trans.Reader.ReaderT' 'Bool' m) -- @ -- ** Example 2: Custom transformer without boilerplate@@ -96,19 +96,19 @@ -- 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)+-- newtype CustomT m a = CustomT { unCustomT :: 'Control.Monad.Trans.Identity.IdentityT' m a }+-- deriving newtype ('Functor', 'Applicative', 'Monad')+-- deriving newtype ('MonadTrans', 'MonadTransControl') -- -- runCustomT :: CustomT m a -> m a--- runCustomT = runIdentityT . unComposeT+-- runCustomT = 'Control.Monad.Trans.Identity.runIdentityT' . unCustomT -- @ -- -- Now you want to use this monad transformer in a transformer stack. -- -- @--- newtype StackT m a = StackT { unStackT :: CustomT (ReaderT Bool m) a }--- deriving newtype (Functor, Applicative Monad)+-- newtype StackT m a = StackT { unStackT :: CustomT ('Control.Monad.Trans.Reader.ReaderT' 'Bool' m) a }+-- deriving newtype ('Functor', 'Applicative', 'Monad') -- @ -- -- Unfortunately we can't derive a @('Monad' m => 'MonadReader' 'Bool' (StackT m))@ instance with@@ -117,7 +117,7 @@ -- To still derive this trivial instance we can use 'Elevator' with /DerivingVia/. -- -- @--- deriving (MonadReader Bool) via (Elevator CustomT (ReaderT Bool m))+-- deriving ('MonadReader' 'Bool') via ('Elevator' CustomT ('Control.Monad.Trans.Reader.ReaderT' 'Bool' m)) -- @ -- ** Example 3: Adding an instance for 'Elevator'@@ -127,7 +127,7 @@ -- Suppose you define a new type class. -- -- @--- class Monad m => MonadCustom m where+-- class 'Monad' m => MonadCustom m where -- simpleMethod :: a -> m a -- complicatedMethod :: (a -> m b) -> m b -- @@@ -139,15 +139,12 @@ -- 'MonadTransControl'. -- -- @--- instance (MonadCustom m, MonadTrans t) => MonadCustom (Elevator t m) where--- simpleMethod = lift . simpleMethod--- complicatedMethod f = (restoreT . pure =\<\<) $ liftWith $ \\ runT ->+-- instance (MonadCustom m, 'MonadTransControl' t) => MonadCustom ('Elevator' t m) where+-- simpleMethod = 'lift' . simpleMethod+-- complicatedMethod f = ('restoreT' . 'pure' '=<<') $ 'liftWith' $ \\ runT -> -- complicatedMethod $ runT . f -- @ ----- Some useful examples (or exercises) are the instances for [mtl](https://hackage.haskell.org/package/mtl)'s type classes:------ * 'MonadError'--- * 'MonadReader'--- * 'MonadState'--- * 'MonadWriter'+-- Some useful examples (or exercises) are the instances for+-- [mtl](https://hackage.haskell.org/package/mtl)'s type classes ('MonadError', 'MonadReader',+-- 'MonadState', 'MonadWriter').