module Control.Monad.Ology.General.Outer where

import Control.Monad.Ology.General.Function
import Import

-- | Monads that can compose as the outer monad with any inner monad to make a monad.
-- See 'Control.Monad.Ology.Specific.ComposeOuter.ComposeOuter'.
-- Instances of this type are isomorphic to @P -> a@ for some type @P@.
--
-- Must satisfy:
--
-- * @fmap (\\ex -> unWExtract ex ma) getExtract = ma@.
class Monad m => MonadOuter m where
    getExtract :: m (WExtract m)

instance MonadOuter Identity where
    getExtract :: Identity (WExtract Identity)
getExtract = WExtract Identity -> Identity (WExtract Identity)
forall a. a -> Identity a
forall (m :: Type -> Type) a. Monad m => a -> m a
return (WExtract Identity -> Identity (WExtract Identity))
-> WExtract Identity -> Identity (WExtract Identity)
forall a b. (a -> b) -> a -> b
$ Extract Identity -> WExtract Identity
forall (m :: Type -> Type). Extract m -> WExtract m
MkWExtract Identity a -> a
Extract Identity
runIdentity

instance MonadOuter ((->) r) where
    getExtract :: r -> WExtract ((->) r)
getExtract r
r = Extract ((->) r) -> WExtract ((->) r)
forall (m :: Type -> Type). Extract m -> WExtract m
MkWExtract (Extract ((->) r) -> WExtract ((->) r))
-> Extract ((->) r) -> WExtract ((->) r)
forall a b. (a -> b) -> a -> b
$ \r -> a
ra -> r -> a
ra r
r

commuteOuter ::
       forall m f a. (MonadOuter m, Functor f)
    => f (m a)
    -> m (f a)
commuteOuter :: forall (m :: Type -> Type) (f :: Type -> Type) a.
(MonadOuter m, Functor f) =>
f (m a) -> m (f a)
commuteOuter f (m a)
fma = do
    MkWExtract Extract m
ext <- m (WExtract m)
forall (m :: Type -> Type). MonadOuter m => m (WExtract m)
getExtract
    f a -> m (f a)
forall a. a -> m a
forall (m :: Type -> Type) a. Monad m => a -> m a
return (f a -> m (f a)) -> f a -> m (f a)
forall a b. (a -> b) -> a -> b
$ (m a -> a) -> f (m a) -> f a
forall a b. (a -> b) -> f a -> f b
forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
fmap m a -> a
Extract m
ext f (m a)
fma