{-# LANGUAGE CPP #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
module Control.Monad.Factory(
MonadFactory(..)
, defer
, asksEnv
, modifyEnv
, withEnv
, runEnv
, Factory(..)
, running
, boot
, within
, withFactory
, wrap
, liftFT
, natTrans
, tryBuild
, (C.>>>)
, (C.<<<)
, (<>)
, MonadThrow(..)
, MonadCatch
, MonadMask
, MonadIO(..)
, lift
) where
import qualified Control.Category as C
import Control.Monad.Catch
import Control.Monad.Cont
import Control.Monad.Factory.Class
import Control.Monad.State
#if __GLASGOW_HASKELL__ < 804
import Data.Semigroup
#endif
newtype Factory m env component
= Factory { unFactory :: StateT env (ContT () m) component }
deriving (Functor, Applicative, Monad, MonadState env, MonadIO)
instance MonadThrow m => MonadThrow (Factory m env) where
{-# INLINE throwM #-}
throwM = liftFT . throwM
instance Monad m => MonadCont (Factory m env) where
{-# INLINE callCC #-}
callCC a = do
env <- get
wrap . running env $ callCC a
instance C.Category (Factory m) where
{-# INLINE id #-}
id = get
{-# INLINE (.) #-}
a . b = b >>= (`within` a)
instance MonadMask m => MonadFactory env m (Factory m env) where
getEnv = get
putEnv = put
produce o = wrap . bracket o
running :: env -> Factory m env c -> (c -> m ()) -> m ()
running env pma = runContT (evalStateT (unFactory pma) env)
{-# INLINE running #-}
boot :: Monad m => Factory m () (m ()) -> m ()
boot factory = running () factory id
within :: env -> Factory m env component -> Factory m env' component
within env = Factory . lift . (`evalStateT` env) . unFactory
{-# INLINE within #-}
withFactory :: (env' -> env) -> Factory m env component -> Factory m env' component
withFactory f (Factory ma) = do
env <- get
Factory (lift $ evalStateT ma (f env))
{-# INLINE withFactory #-}
wrap :: ((c -> m ()) -> m ()) -> Factory m env c
wrap = Factory . lift . ContT
{-# INLINE wrap #-}
liftFT :: Monad m => m a -> Factory m env a
liftFT ma = wrap (ma >>=)
{-# INLINE liftFT #-}
natTrans :: (n () -> m ()) -> (m () -> n ()) -> Factory n env component -> Factory m env component
natTrans fnm fmn fac = do
env <- get
wrap $ \fm -> fnm $ running env fac (fmn . fm)
{-# INLINE natTrans #-}
{-# INLINE tryBuild #-}
tryBuild :: Bool -> Factory n env () -> Factory n env ()
tryBuild b p = if b then p else return ()