packages feed

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

module Control.Monad.Ology.Specific.ComposeInner where

import Control.Monad.Ology.General.Exception.Class
import Control.Monad.Ology.General.Extract
import Control.Monad.Ology.General.Function
import Control.Monad.Ology.General.IO
import Control.Monad.Ology.General.Identity
import Control.Monad.Ology.General.Inner
import Control.Monad.Ology.General.Outer
import Control.Monad.Ology.General.Trans.Constraint
import Control.Monad.Ology.General.Trans.Hoist
import Control.Monad.Ology.General.Trans.Trans
import Control.Monad.Ology.Specific.Result
import Import

type ComposeInner :: (Type -> Type) -> (Type -> Type) -> Type -> Type
newtype ComposeInner inner outer a = MkComposeInner
    { unComposeInner :: outer (inner a)
    }

instance (Foldable inner, Foldable outer, Functor outer) => Foldable (ComposeInner inner outer) where
    foldMap am (MkComposeInner oia) = foldMap id $ fmap (foldMap am) oia

instance (Traversable inner, Traversable outer) => Traversable (ComposeInner inner outer) where
    traverse afb (MkComposeInner oia) = fmap MkComposeInner $ traverse (traverse afb) oia

instance Traversable inner => TransConstraint Traversable (ComposeInner inner) where
    hasTransConstraint = Dict

instance (Functor inner, Functor outer) => Functor (ComposeInner inner outer) where
    fmap ab (MkComposeInner oia) = MkComposeInner $ fmap (fmap ab) oia

instance Functor inner => TransConstraint Functor (ComposeInner inner) where
    hasTransConstraint = Dict

instance (MonadInner inner, Monad outer) => Applicative (ComposeInner inner outer) where
    pure a = MkComposeInner $ pure $ pure a
    -- cannot use obvious definition for <*>, because that would incorrectly execute the outer part of ma even if mab fails
    mab <*> ma = do
        ab <- mab
        a <- ma
        return $ ab a

instance (MonadInner inner, Monad outer, Alternative inner) => Alternative (ComposeInner inner outer) where
    empty = MkComposeInner $ pure empty
    -- cannot use obvious definition for <|> for similar reasons as in <*>
    (MkComposeInner oia) <|> cb = do
        ma <-
            MkComposeInner $ do
                ia <- oia
                return $ fmap Just ia <|> return Nothing
        case ma of
            Just a -> return a
            Nothing -> cb

instance (MonadInner inner, Monad outer) => Monad (ComposeInner inner outer) where
    return = pure
    (MkComposeInner oia) >>= p =
        MkComposeInner $ do
            ia <- oia
            case retrieveInner ia of
                SuccessResult a -> do
                    ib <- unComposeInner $ p a
                    return $ ia >> ib
                FailureResult e -> return $ throwExc e

instance MonadInner inner => TransConstraint Monad (ComposeInner inner) where
    hasTransConstraint = Dict

instance (MonadInner inner, MonadFail outer) => MonadFail (ComposeInner inner outer) where
    fail s = lift $ fail s

instance MonadInner inner => TransConstraint MonadFail (ComposeInner inner) where
    hasTransConstraint = Dict

instance (MonadInner inner, MonadInner outer) => MonadInner (ComposeInner inner outer) where
    retrieveInner (MkComposeInner oia) =
        case retrieveInner oia of
            SuccessResult ia ->
                case retrieveInner ia of
                    SuccessResult a -> SuccessResult a
                    FailureResult e -> FailureResult $ Left e
            FailureResult e -> FailureResult $ Right e

instance MonadInner inner => TransConstraint MonadInner (ComposeInner inner) where
    hasTransConstraint = Dict

instance (MonadInner inner, MonadOuter inner, MonadOuter outer) => MonadOuter (ComposeInner inner outer) where
    getExtract =
        MkComposeInner $ do
            MkWExtract oaa <- getExtract
            return $ do
                MkWExtract iaa <- getExtract
                return $ MkWExtract $ \(MkComposeInner oia) -> iaa $ oaa oia

instance (MonadInner inner, MonadOuter inner) => TransConstraint MonadOuter (ComposeInner inner) where
    hasTransConstraint = Dict

instance (MonadInner inner, MonadFix outer) => MonadFix (ComposeInner inner outer) where
    mfix ama =
        MkComposeInner $
        mfix $ \ia ->
            unComposeInner $
            ama $
            case retrieveInner ia of
                SuccessResult a -> a
                FailureResult _ -> error "bad ComposeInner mfix"

instance MonadInner inner => TransConstraint MonadFix (ComposeInner inner) where
    hasTransConstraint = Dict

instance (MonadInner inner, Monad outer, Alternative inner) => MonadPlus (ComposeInner inner outer)

instance (MonadExtract inner, MonadExtract outer) => MonadExtract (ComposeInner inner outer) where
    mToValue (MkComposeInner oia) = mToValue $ mToValue oia

instance MonadExtract inner => TransConstraint MonadExtract (ComposeInner inner) where
    hasTransConstraint = Dict

instance (MonadIdentity inner, MonadIdentity outer) => MonadIdentity (ComposeInner inner outer)

instance MonadIdentity inner => TransConstraint MonadIdentity (ComposeInner inner) where
    hasTransConstraint = Dict

instance (MonadInner inner, MonadIO outer) => MonadIO (ComposeInner inner outer) where
    liftIO ioa = lift $ liftIO ioa

instance MonadInner inner => TransConstraint MonadIO (ComposeInner inner) where
    hasTransConstraint = Dict

liftInner :: Applicative outer => inner --> ComposeInner inner outer
liftInner na = MkComposeInner $ pure na

instance (MonadInner inner, MonadException inner, MonadException m) => MonadException (ComposeInner inner m) where
    type Exc (ComposeInner inner m) = Either (Exc inner) (Exc m)
    throwExc (Left e) = liftInner $ throwExc e
    throwExc (Right e) = lift $ throwExc e
    catchExc (MkComposeInner mia) handler =
        MkComposeInner $ do
            ira <- tryExc mia
            case fmap retrieveInner ira of
                FailureResult e -> unComposeInner $ handler $ Right e
                SuccessResult (FailureResult e) -> unComposeInner $ handler $ Left e
                SuccessResult (SuccessResult a) -> return $ return a

instance (MonadInner inner, MonadException inner) => TransConstraint MonadException (ComposeInner inner) where
    hasTransConstraint = Dict

instance MonadInner inner => MonadTrans (ComposeInner inner) where
    lift ma = MkComposeInner $ fmap pure ma

instance MonadInner inner => MonadTransHoist (ComposeInner inner) where
    hoist ii (MkComposeInner ma) = MkComposeInner $ ii ma