deriving-trans-0.10.0.0: src/Control/Monad/Trans/Compose/Stack.hs
{-# LANGUAGE GADTs #-}
{-# LANGUAGE TypeData #-}
{-# 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'.
type role RunStackT nominal nominal nominal
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.
type 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 AppT m a = AppT { unAppT :: 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.
--
-- @
-- runAppT :: AppT m a -> m a
-- runAppT appTma = 'runStackT' runAppStackT $ unAppT appTma
-- where
-- runAppStackT = '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 <- 'Control.Monad.Trans.Reader.ask'
-- let char = if bool then \'Y\' else \'N\'
-- 'Control.Monad.Trans.Reader.runReaderT' tma char
-- @