packages feed

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 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