deriving-trans 0.4.0.0 → 0.5.0.0
raw patch · 7 files changed
+235/−34 lines, 7 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Control.Monad.Trans.Compose.Infix: (..>) :: (t2 m a -> m a) -> (t1 (t2 m) a -> t2 m a) -> (t2 .|> t1) m a -> m a
+ Control.Monad.Trans.Compose.Infix: (./>) :: (forall a. t2 m a -> m (StT t2 a)) -> (forall a. t1 (t2 m) a -> t2 m (StT t1 a)) -> forall a. (t2 .|> t1) m a -> m (StT t2 (StT t1 a))
+ Control.Monad.Trans.Compose.Infix: infixl 1 ..>
+ Control.Monad.Trans.Compose.Infix: type (.|>) t2 t1 = ComposeT t1 t2
+ Control.Monad.Trans.Compose.Stack: [:..>] :: RunStackT ts m a -> (t (StackT ts m) a -> StackT ts m a) -> RunStackT (ts :.|> t) m a
+ Control.Monad.Trans.Compose.Stack: [:.|>] :: Stack -> ((Type -> Type) -> Type -> Type) -> Stack
+ Control.Monad.Trans.Compose.Stack: [NilT] :: Stack
+ Control.Monad.Trans.Compose.Stack: [RunNilT] :: RunStackT NilT m a
+ Control.Monad.Trans.Compose.Stack: data RunStackT :: Stack -> (Type -> Type) -> Type -> Type
+ Control.Monad.Trans.Compose.Stack: data Stack
+ Control.Monad.Trans.Compose.Stack: infixl 1 :.|>
+ Control.Monad.Trans.Compose.Stack: runStackT :: RunStackT ts m a -> StackT ts m a -> m a
+ Control.Monad.Trans.Compose.Stack: type family StackT (ts :: Stack) = (t :: (Type -> Type) -> Type -> Type) | t -> ts
+ Control.Monad.Trans.Compose.Transparent: MkNoT :: IdentityT m a -> NoT m a
+ Control.Monad.Trans.Compose.Transparent: [unNoT] :: NoT m a -> IdentityT m a
+ Control.Monad.Trans.Compose.Transparent: instance Control.Monad.Trans.Class.MonadTrans Control.Monad.Trans.Compose.Transparent.NoT
+ Control.Monad.Trans.Compose.Transparent: instance Control.Monad.Trans.Control.Identity.MonadTransControlIdentity Control.Monad.Trans.Compose.Transparent.NoT
+ Control.Monad.Trans.Compose.Transparent: instance Control.Monad.Trans.Control.MonadTransControl Control.Monad.Trans.Compose.Transparent.NoT
+ Control.Monad.Trans.Compose.Transparent: instance GHC.Base.Applicative m => GHC.Base.Applicative (Control.Monad.Trans.Compose.Transparent.NoT m)
+ Control.Monad.Trans.Compose.Transparent: instance GHC.Base.Functor m => GHC.Base.Functor (Control.Monad.Trans.Compose.Transparent.NoT m)
+ Control.Monad.Trans.Compose.Transparent: instance GHC.Base.Monad m => GHC.Base.Monad (Control.Monad.Trans.Compose.Transparent.NoT m)
+ Control.Monad.Trans.Compose.Transparent: newtype NoT m a
+ Control.Monad.Trans.Compose.Transparent: runTransparentT :: TransparentT m a -> m a
+ Control.Monad.Trans.Compose.Transparent: type TransparentT = Elevator NoT
Files
- CHANGELOG.md +7/−0
- deriving-trans.cabal +5/−1
- src/Control/Monad/Trans/Compose.hs +33/−30
- src/Control/Monad/Trans/Compose/Infix.hs +25/−0
- src/Control/Monad/Trans/Compose/Stack.hs +131/−0
- src/Control/Monad/Trans/Compose/Transparent.hs +27/−0
- src/Control/Monad/Trans/Elevator.hs +7/−3
CHANGELOG.md view
@@ -1,5 +1,12 @@ # Revision history for deriving-trans +## 0.5.0.0 *25 Jul 2022*++* Add module `Control.Monad.Trans.Compose.Infix`.+* Add module `Control.Monad.Trans.Compose.Stack`.+* Add module `Control.Monad.Trans.Compose.Transparent`.+* Improve and update Haddock documentation and examples.+ ## 0.4.0.0 *18 Jul 2022* * Add dependency on monad-control-identity.
deriving-trans.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: deriving-trans-version: 0.4.0.0+version: 0.5.0.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@@ -27,6 +27,9 @@ library exposed-modules: Control.Monad.Trans.Compose+ Control.Monad.Trans.Compose.Infix+ Control.Monad.Trans.Compose.Stack+ Control.Monad.Trans.Compose.Transparent Control.Monad.Trans.Elevator --other-modules: build-depends:@@ -61,6 +64,7 @@ StandaloneKindSignatures TypeApplications TypeFamilies+ TypeOperators ghc-options: -Wall -Wunused-packages
src/Control/Monad/Trans/Compose.hs view
@@ -1,4 +1,5 @@-{-# LANGUAGE QuantifiedConstraints, UndecidableInstances #-}+{-# LANGUAGE QuantifiedConstraints #-}+{-# LANGUAGE UndecidableInstances #-} module Control.Monad.Trans.Compose where @@ -36,10 +37,11 @@ -- | 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' \/ 'MonadTransControlIdentity'. ----- ==== Type level arguments+-- __Type level arguments:__+-- -- [@t1 :: ('Type' -> 'Type') -> 'Type' -> 'Type'@] outer monad transformer -- [@t2 :: ('Type' -> 'Type') -> 'Type' -> 'Type'@] inner monad transformer -- [@m :: 'Type' -> 'Type'@] monad@@ -257,7 +259,7 @@ -- -- You have to run the composed monad transformers to get back into the base monad at some point. --- | Run a transformer stack.+-- | Run two stacked monad transformers. -- -- 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@@@ -317,9 +319,9 @@ -- for 'ComposeT'. -- -- @--- newtype CustomT m a = CustomT { unCustomT :: 'Control.Monad.Trans.Identity.IdentityT' m a }+-- newtype CustomT m a = CustomT { unCustomT :: t'Control.Monad.Trans.Identity.IdentityT' m a } -- deriving newtype ('Functor', 'Applicative', 'Monad')--- deriving newtype ('MonadTrans', 'MonadTransControl')+-- deriving newtype ('MonadTrans', 'MonadTransControl', 'MonadTransControlIdentity') -- @ -- -- First we need the regular instance.@@ -347,17 +349,18 @@ -- Create a monad transformer stack and wrap it using a newtype. -- -- @--- type (|.) = 'ComposeT'--- type Stack = 'LT.StateT' 'Int' |. 'T.ReaderT' 'Char' |. CustomT |. 'T.ReaderT' 'Bool' |. 'Control.Monad.Trans.Identity.IdentityT'+-- type Stack = t'Control.Monad.Trans.Compose.Transparent.TransparentT' t'Control.Monad.Trans.Compose.Infix..|>' 'T.ReaderT' 'Bool' t'Control.Monad.Trans.Compose.Infix..|>' CustomT t'Control.Monad.Trans.Compose.Infix..|>' 'T.ReaderT' 'Char' t'Control.Monad.Trans.Compose.Infix..|>' 'LT.StateT' 'Int' -- newtype StackT m a = StackT { unStackT :: Stack m a } -- deriving newtype ('Functor', 'Applicative', 'Monad') -- @ ----- 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.+-- Using t'Control.Monad.Trans.Compose.Infix..|>' we can write @Stack@ in the order of initialization.+-- We are adding t'Control.Monad.Trans.Compose.Transparent.TransparentT' to the bottom of the stack,+-- so that all the other transformer instances actually end up in the stack. -- Now we can simply derive just the instances, that we want. -- -- @+-- deriving newtype ('MonadTrans', 'MonadTransControl') -- deriving newtype ('MonadState' 'Int') -- deriving newtype MonadCustom -- @@@ -375,29 +378,29 @@ -- 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 -- initialization.+-- With 'Control.Monad.Trans.Compose.Infix...>' we can use the order of initialization again. -- -- @ -- runStackT :: 'MonadBaseControl' 'IO' m -- => StackT m a--- -> m (StT StackT a)+-- -> m ('StT' StackT a) -- runStackT stackTma =--- runStateT' |.--- runReaderT' |.--- runCustomT |.--- (\\ tma -> 'T.runReaderT' tma 'True') |.--- 'Control.Monad.Trans.Identity.runIdentityT' $ unStackT stackTma--- where--- runReaderT' :: 'MonadReader' 'Bool' m => 'T.ReaderT' 'Char' m a -> m a--- runReaderT' tma = do--- bool <- 'ask'--- let char = if bool--- then \'Y\'--- else \'N\'--- 'T.runReaderT' tma char+-- 'Control.Monad.Trans.Compose.Transparent.runTransparentT'+-- 'Control.Monad.Trans.Compose.Infix../>' (\\ tma -> 'T.runReaderT' tma 'True')+-- 'Control.Monad.Trans.Compose.Infix../>' runCustomT+-- 'Control.Monad.Trans.Compose.Infix../>' runReaderT'+-- 'Control.Monad.Trans.Compose.Infix../>' runStateT'+-- $ unStackT stackTma+-- where+-- runReaderT' :: 'MonadReader' 'Bool' m => 'T.ReaderT' 'Char' m a -> m a+-- runReaderT' tma = do+-- bool <- 'ask'+-- let char = if bool then \'Y\' else \'N\'+-- 'T.runReaderT' tma char ----- runStateT' :: 'MonadReader' 'Char' m => 'LT.StateT' 'Int' m a -> m (a, 'Int')--- runStateT' tma = do--- char <- 'ask'--- let num = 'fromEnum' char--- 'LT.runStateT' tma num+-- runStateT' :: 'MonadReader' 'Char' m => 'LT.StateT' 'Int' m a -> m (a, 'Int')+-- runStateT' tma = do+-- char <- 'ask'+-- let num = 'fromEnum' char+-- 'LT.runStateT' tma num -- @
+ src/Control/Monad/Trans/Compose/Infix.hs view
@@ -0,0 +1,25 @@+module Control.Monad.Trans.Compose.Infix where++import Control.Monad.Trans.Compose+import Control.Monad.Trans.Control++-- | A flipped type-level infix operator for 'ComposeT'.+type (.|>) t2 t1 = ComposeT t1 t2++infixl 1 .|>++-- | A 'flip'ped infix operator for 'runComposeT'.+(./>) :: (forall a. t2 m a -> m (StT t2 a)) -- ^ run @t2@+ -> (forall a. t1 (t2 m) a -> t2 m (StT t1 a)) -- ^ run @t1@+ -> (forall a. (t2 .|> t1) m a -> m (StT t2 (StT t1 a)))+(./>) runT2 runT1 = runComposeT runT1 runT2++infixl 1 ./>++-- | A 'flip'ped infix operator for 'runComposeT''.+(..>) :: (t2 m a -> m a) -- ^ run @t2@+ -> (t1 (t2 m) a -> t2 m a) -- ^ run @t1@+ -> ((t2 .|> t1) m a -> m a)+(..>) runT2 runT1 = runComposeT' runT1 runT2++infixl 1 ..>
+ src/Control/Monad/Trans/Compose/Stack.hs view
@@ -0,0 +1,131 @@+{-# LANGUAGE GADTs #-}+{-# LANGUAGE TypeFamilyDependencies #-}+{-# LANGUAGE UndecidableInstances #-}+{-# OPTIONS -Wno-unticked-promoted-constructors #-}++-- | This module gives an alternative interface to build and run monad transformer stacks.+--+-- Using this approach is supposed to improve error messages and reduce errors, such as forgetting+-- to add 'TransparentT' at the bottom of the stack.+module Control.Monad.Trans.Compose.Stack where++import Control.Monad.Trans.Compose+import Control.Monad.Trans.Compose.Transparent+import Data.Kind++-- * 'StackT'+--+-- $stackt+--+-- 'StackT' is a more ergonomic way to define a monad transformer stack.++-- | An isomorphism between a 'Stack' and the corresponding monad transformer, which can be built+-- using 'ComposeT'.+--+-- An additional 'TransparentT' will automatically be used at the bottom of the stack.+-- You only have to worry about the semantically relevant transformers.+type family StackT (ts :: Stack) = (t :: (Type -> Type) -> Type -> Type) | t -> ts where+ StackT NilT = TransparentT+ StackT (ts :.|> t) = ComposeT t (StackT ts)++-- ** 'runStackT' and 'RunStackT'+--+-- $runStackt+--+-- Monad transformer stacks, that only consist of t'Control.Monad.Trans.Identity.IdentityT' and+-- t'Control.Monad.Trans.Reader.ReaderT', can be run with 'runStackT'.+--+-- If your transformer stack contains monadic state t'Control.Monad.Trans.Control.StT', you will have to use 'runComposeT' or 'Control.Monad.Trans.Compose.Infix../>'!+-- You can still use 'StackT' for the type definition.++-- | Run a monad transformer stack.+--+-- This takes a 'RunStackT' as an argument containing the individual runners.+--+-- 'runStackT' can only be used for monad transformer stacks without monadic state t'Control.Monad.Trans.Control.StT'.+runStackT :: RunStackT ts m a -> StackT ts m a -> m a+runStackT RunNilT = runTransparentT+runStackT (runRemainingStackT :..> runNextT) = runStackT runRemainingStackT . runNextT . deComposeT++-- | A data type representing the runner function of a monad transformer stack.+--+-- This is basically a heterogeneous list of monad transformer runners.+--+-- 'RunStackT' can only be used for monad transformer stacks without monadic state t'Control.Monad.Trans.Control.StT'.+data RunStackT :: Stack -> (Type -> Type) -> Type -> Type where+ -- | run an empty monad transformer stack+ RunNilT :: RunStackT NilT m a+ -- | run the next monad transformer on a stack+ (:..>) :: RunStackT ts m a -- ^ run remaining stack+ -> (t (StackT ts m) a -> StackT ts m a) -- ^ run next monad transformer+ -> RunStackT (ts :.|> t) m a++infixl 1 :..>++-- * 'Stack'+--+-- $stack+--+-- 'Stack' is used to define monad transformer stacks with 'StackT'.++-- | A data kind representing a monad transformer stack.+--+-- This is basically a type-level list of monad transformers.+data Stack where+ -- | an empty monad transformer stack+ NilT :: Stack+ -- | add a monad transformer to a stack+ (:.|>) :: Stack -- ^ remaining stack+ -> ((Type -> Type) -> Type -> Type) -- ^ next monad transformer+ -> Stack++infixl 1 :.|>++-- * Examples+--+-- $examples+--+-- Feel free to compare these examples to the ones in "Control.Monad.Trans.Compose".++-- TODO: Qualified identifiers such as 'Control.Monad.Trans.Class.MonadTrans' need a "t"-prefix to+-- work correctly, but it doesn't work with a leading opening parenthesis "(".++-- ** Example 1: Build a transformer stack+--+-- $example1+--+-- Apply the type family 'StackT' to a type of kind 'Stack' and generate a monad transformer stack built with 'ComposeT'.+--+-- @+-- type AppStack = 'NilT' ':.|>' t'Control.Monad.Trans.Reader.ReaderT' 'Bool' ':.|>' CustomT ':.|>' t'Control.Monad.Trans.Reader.ReaderT' 'Char'+-- newtype AppStackT m a = AppStackT { unAppStackT :: StackT AppStack m a }+-- deriving newtype ('Functor', 'Applicative', 'Monad')+-- deriving newtype ('Control.Monad.Trans.Class.MonadTrans', t'Control.Monad.Trans.Control.MonadTransControl', t'Control.Monad.Trans.Control.Identity.MonadTransControlIdentity')+-- deriving newtype MonadCustom+-- deriving newtype ('Control.Monad.Reader.Class.MonadReader' 'Bool')+-- @++-- ** Example 2: Run a transformer stack+--+-- $example2+--+-- Use 'runStackT' and supply it with a 'RunStackT' argument.+--+-- @+-- runAppStackT :: t'Control.Monad.Trans.Control.MonadBaseControl' 'IO' m+-- => AppStackT m a+-- -> m a+-- runAppStackT stackTma = runStackT runAccStackT $ unStackT stackTma+-- where+-- runAccStackT :: RunStackT AppStack+-- runAccStackT = RunNilT+-- :..> (\\ tma -> 'Control.Monad.Trans.Reader.runReaderT' tma 'True')+-- :..> runCustomT+-- :..> runReaderT'+--+-- runReaderT' :: t'Control.Monad.Reader.Class.MonadReader' 'Bool' m => t'Control.Monad.Trans.Reader.ReaderT' 'Char' m a -> m a+-- runReaderT' tma = do+-- bool <- 'ask'+-- let char = if bool then \'Y\' else \'N\'+-- 'Control.Monad.Trans.Reader.runReaderT' tma char+-- @
+ src/Control/Monad/Trans/Compose/Transparent.hs view
@@ -0,0 +1,27 @@+{-# LANGUAGE UndecidableInstances #-}++module Control.Monad.Trans.Compose.Transparent where++import Control.Monad.Trans.Class+import Control.Monad.Trans.Control+import Control.Monad.Trans.Control.Identity+import Control.Monad.Trans.Elevator+import Control.Monad.Trans.Identity++-- | A monad transformer, that passes through all instances via 'Elevator'.+--+-- This cannot be defined as a newtype, because we want all the instances, that are defined for+-- 'Elevator' to work for 'TransparentT'.+type TransparentT = Elevator NoT++runTransparentT :: TransparentT m a -> m a+runTransparentT = runIdentityT . unNoT . descend++-- | A newtype wrapper around 'IdentityT'.+--+-- This is used in 'TransparentT' to encourage the use of 'runTransparentT'.+newtype NoT m a = MkNoT {unNoT :: IdentityT m a}+ deriving newtype (Functor, Applicative, Monad)+ deriving newtype (MonadTrans, MonadTransControl, MonadTransControlIdentity)++{-# WARNING NoT, MkNoT, unNoT "This is an implementation detail of 'TransparentT'." #-}
src/Control/Monad/Trans/Elevator.hs view
@@ -1,4 +1,6 @@-{-# LANGUAGE QuantifiedConstraints, UndecidableInstances, TupleSections #-}+{-# LANGUAGE QuantifiedConstraints #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE TupleSections #-} module Control.Monad.Trans.Elevator where @@ -23,15 +25,17 @@ -- $elevator -- -- 'Elevator' can be used to lift instances through monad transformers as long as they implement--- 'MonadTrans' and 'MonadTransControl' instances.+-- a 'MonadTrans' \/ 'MonadTransControl' \/ 'MonadTransControlIdentity' instance. -- -- 'MonadTransControl' is only necessary when there is atleast one method with a monadic argument.+-- 'MonadTransControlIdentity' is even stronger and only required for a few specific instances. -- | A newtype wrapper for monad transformers. -- -- Access instances of the inner monad @m@. ----- ==== Type level arguments+-- __Type level arguments:__+-- -- [@t :: ('Type' -> 'Type') -> 'Type' -> 'Type'@] monad transformer -- [@m :: 'Type' -> 'Type'@] monad -- [@a :: 'Type'@] value