packages feed

monadology-0.1: src/Control/Monad/Ology/Specific/ComposeT.hs

module Control.Monad.Ology.Specific.ComposeT
    ( ComposeT(..)
    , composeTUnlift
    , composeTWUnlift
    , liftOuterComposeT
    , liftInnerComposeT
    , liftOuterComposeTWithUnlift
    , liftInnerComposeTWithUnlift
    ) where

import Control.Monad.Ology.General
import Control.Monad.Ology.Specific.ComposeInner
import Import

-- | Compose two monad transformers.
type ComposeT :: TransKind -> TransKind -> TransKind
newtype ComposeT (outerT :: TransKind) (innerT :: TransKind) (m :: Type -> Type) (a :: Type) = MkComposeT
    { unComposeT :: outerT (innerT m) a
    } deriving (Functor, Applicative, Alternative, Monad, MonadFail, MonadIO, MonadFix, MonadPlus)

liftOuterComposeT ::
       forall outerT innerT m a. (MonadTransTunnel outerT, MonadTrans innerT, TransConstraint Monad innerT, Monad m)
    => outerT m a
    -> ComposeT outerT innerT m a
liftOuterComposeT oma =
    case hasTransConstraint @Monad @innerT @m of
        Dict -> MkComposeT $ hoist lift oma

liftInnerComposeT ::
       forall outerT innerT m a. (MonadTrans outerT, TransConstraint Monad innerT, Monad m)
    => innerT m a
    -> ComposeT outerT innerT m a
liftInnerComposeT ima =
    case hasTransConstraint @Monad @innerT @m of
        Dict -> MkComposeT $ lift ima

liftOuterComposeTWithUnlift ::
       forall outerT innerT m r. (MonadTransTunnel outerT, MonadTransUnlift innerT, MonadTunnelIOInner m)
    => ((forall a. ComposeT outerT innerT m a -> outerT m a) -> outerT m r)
    -> ComposeT outerT innerT m r
liftOuterComposeTWithUnlift call =
    case hasTransConstraint @MonadIO @innerT @m of
        Dict ->
            MkComposeT $
            tunnel $ \tun -> liftWithUnlift $ \unlift -> tun $ call $ \(MkComposeT ttma) -> hoist unlift ttma

liftInnerComposeTWithUnlift ::
       forall outerT innerT m r. (MonadTransUnlift outerT, MonadTransUnlift innerT, MonadTunnelIOInner m)
    => ((forall a. ComposeT outerT innerT m a -> innerT m a) -> innerT m r)
    -> ComposeT outerT innerT m r
liftInnerComposeTWithUnlift call =
    case hasTransConstraint @MonadTunnelIOInner @innerT @m of
        Dict -> MkComposeT $ liftWithUnlift $ \unlift -> call $ \(MkComposeT ttma) -> unlift ttma

composeTUnlift ::
       forall c outerT innerT. (MonadTransUnlift innerT, TransConstraint c innerT)
    => Unlift c outerT
    -> Unlift c innerT
    -> Unlift c (ComposeT outerT innerT)
composeTUnlift ua ub (MkComposeT tatbma) = ub $ withTransConstraintTM @c $ ua tatbma

composeTWUnlift ::
       forall c outerT innerT. (MonadTransUnlift innerT, TransConstraint c innerT)
    => WUnlift c outerT
    -> WUnlift c innerT
    -> WUnlift c (ComposeT outerT innerT)
composeTWUnlift (MkWUnlift ua) (MkWUnlift ub) = MkWUnlift $ composeTUnlift @c ua ub

instance (MonadTrans outerT, MonadTrans innerT, TransConstraint Monad innerT) => MonadTrans (ComposeT outerT innerT) where
    lift (ma :: m a) =
        case hasTransConstraint @Monad @innerT @m of
            Dict -> MkComposeT $ lift $ lift ma

instance (TransConstraint Functor outerT, TransConstraint Functor innerT) =>
             TransConstraint Functor (ComposeT outerT innerT) where
    hasTransConstraint ::
           forall m. Functor m
        => Dict (Functor (ComposeT outerT innerT m))
    hasTransConstraint =
        case hasTransConstraint @Functor @innerT @m of
            Dict ->
                case hasTransConstraint @Functor @outerT @(innerT m) of
                    Dict -> Dict

instance (TransConstraint Applicative outerT, TransConstraint Applicative innerT) =>
             TransConstraint Applicative (ComposeT outerT innerT) where
    hasTransConstraint ::
           forall m. Applicative m
        => Dict (Applicative (ComposeT outerT innerT m))
    hasTransConstraint =
        case hasTransConstraint @Applicative @innerT @m of
            Dict ->
                case hasTransConstraint @Applicative @outerT @(innerT m) of
                    Dict -> Dict

instance (TransConstraint Monad outerT, TransConstraint Monad innerT) => TransConstraint Monad (ComposeT outerT innerT) where
    hasTransConstraint ::
           forall m. Monad m
        => Dict (Monad (ComposeT outerT innerT m))
    hasTransConstraint =
        case hasTransConstraint @Monad @innerT @m of
            Dict ->
                case hasTransConstraint @Monad @outerT @(innerT m) of
                    Dict -> Dict

instance (TransConstraint MonadIO outerT, TransConstraint Monad innerT, TransConstraint MonadIO innerT) =>
             TransConstraint MonadIO (ComposeT outerT innerT) where
    hasTransConstraint ::
           forall m. MonadIO m
        => Dict (MonadIO (ComposeT outerT innerT m))
    hasTransConstraint =
        case hasTransConstraint @MonadIO @innerT @m of
            Dict ->
                case hasTransConstraint @MonadIO @outerT @(innerT m) of
                    Dict -> Dict

instance (TransConstraint MonadFail outerT, TransConstraint Monad innerT, TransConstraint MonadFail innerT) =>
             TransConstraint MonadFail (ComposeT outerT innerT) where
    hasTransConstraint ::
           forall m. MonadFail m
        => Dict (MonadFail (ComposeT outerT innerT m))
    hasTransConstraint =
        case hasTransConstraint @MonadFail @innerT @m of
            Dict ->
                case hasTransConstraint @MonadFail @outerT @(innerT m) of
                    Dict -> Dict

instance (TransConstraint MonadFix outerT, TransConstraint Monad innerT, TransConstraint MonadFix innerT) =>
             TransConstraint MonadFix (ComposeT outerT innerT) where
    hasTransConstraint ::
           forall m. MonadFix m
        => Dict (MonadFix (ComposeT outerT innerT m))
    hasTransConstraint =
        case hasTransConstraint @MonadFix @innerT @m of
            Dict ->
                case hasTransConstraint @MonadFix @outerT @(innerT m) of
                    Dict -> Dict

instance (TransConstraint MonadPlus outerT, TransConstraint Monad innerT, TransConstraint MonadPlus innerT) =>
             TransConstraint MonadPlus (ComposeT outerT innerT) where
    hasTransConstraint ::
           forall m. MonadPlus m
        => Dict (MonadPlus (ComposeT outerT innerT m))
    hasTransConstraint =
        case hasTransConstraint @MonadPlus @innerT @m of
            Dict ->
                case hasTransConstraint @MonadPlus @outerT @(innerT m) of
                    Dict -> Dict

instance (MonadTransHoist outerT, MonadTransHoist innerT) => MonadTransHoist (ComposeT outerT innerT) where
    hoist ::
           forall m1 m2. (Monad m1, Monad m2)
        => (m1 --> m2)
        -> ComposeT outerT innerT m1 --> ComposeT outerT innerT m2
    hoist f (MkComposeT ma) =
        case hasTransConstraint @Monad @innerT @m1 of
            Dict ->
                case hasTransConstraint @Monad @innerT @m2 of
                    Dict -> MkComposeT $ hoist (hoist f) ma

instance (MonadTransTunnel outerT, MonadTransTunnel innerT) => MonadTransTunnel (ComposeT outerT innerT) where
    type Tunnel (ComposeT outerT innerT) = ComposeInner (Tunnel outerT) (Tunnel innerT)
    tunnel ::
           forall m2 r. Monad m2
        => ((forall m1 a. Monad m1 => ComposeT outerT innerT m1 a -> m1 (ComposeInner (Tunnel outerT) (Tunnel innerT) a)) -> m2 (ComposeInner (Tunnel outerT) (Tunnel innerT) r))
        -> ComposeT outerT innerT m2 r
    tunnel call =
        case hasTransConstraint @Monad @innerT @m2 of
            Dict ->
                MkComposeT $
                tunnel $ \unlift1 ->
                    tunnel $ \unlift2 ->
                        fmap unComposeInner $
                        call $ \(MkComposeT ff :: _ m1 _) ->
                            case hasTransConstraint @Monad @innerT @m1 of
                                Dict -> fmap MkComposeInner $ unlift2 $ unlift1 $ ff

instance (MonadTransCoerce outerT, MonadTransCoerce innerT, TransConstraint Monad innerT) =>
             MonadTransCoerce (ComposeT outerT innerT) where
    transCoerce ::
           forall m1 m2. Coercible m1 m2
        => Dict (Coercible (ComposeT outerT innerT m1) (ComposeT outerT innerT m2))
    transCoerce =
        case transCoerce @innerT @m1 @m2 of
            Dict ->
                case transCoerce @outerT @(innerT m1) @(innerT m2) of
                    Dict -> Dict

instance (MonadTransUnlift outerT, MonadTransUnlift innerT) => MonadTransUnlift (ComposeT outerT innerT) where
    liftWithUnlift ::
           forall m r. MonadIO m
        => (Unlift MonadTunnelIOInner (ComposeT outerT innerT) -> m r)
        -> ComposeT outerT innerT m r
    liftWithUnlift call =
        case hasTransConstraint @MonadIO @innerT @m of
            Dict ->
                MkComposeT $
                liftWithUnlift $ \unlift1 ->
                    liftWithUnlift $ \unlift2 ->
                        call $ \(MkComposeT t1t2ma) ->
                            unlift2 $ withTransConstraintTM @MonadTunnelIOInner $ unlift1 t1t2ma
    getDiscardingUnlift ::
           forall m. Monad m
        => ComposeT outerT innerT m (WUnlift MonadTunnelIOInner (ComposeT outerT innerT))
    getDiscardingUnlift =
        case hasTransConstraint @Monad @innerT @m of
            Dict ->
                MkComposeT $
                withTransConstraintTM @Monad $ do
                    unlift1 <- getDiscardingUnlift
                    unlift2 <- lift getDiscardingUnlift
                    return $ composeTWUnlift unlift1 unlift2

instance (MonadTransAskUnlift outerT, MonadTransAskUnlift innerT) => MonadTransAskUnlift (ComposeT outerT innerT) where
    askUnlift ::
           forall m. Monad m
        => ComposeT outerT innerT m (WUnlift Monad (ComposeT outerT innerT))
    askUnlift =
        case hasTransConstraint @Monad @innerT @m of
            Dict ->
                MkComposeT $
                withTransConstraintTM @Monad $ do
                    unlift1 <- askUnlift
                    unlift2 <- lift askUnlift
                    return $ composeTWUnlift unlift1 unlift2