{-# LANGUAGE DerivingVia, MagicHash #-} module Control.Effect.BaseControl ( -- * Effects BaseControl -- * Actions , withLowerToBase , gainBaseControl -- * Interpretations , runBaseControl , baseControlToFinal -- * MonadBaseControl , MonadBaseControl(..) , control -- * Threading utilities , threadBaseControlViaClass -- * Combinators for 'Algebra's -- Intended to be used for custom 'Carrier' instances when -- defining 'algPrims'. , powerAlgBaseControl , powerAlgBaseControlFinal -- * Carriers , GainBaseControlC(..) , BaseControlC , BaseControlToFinalC ) where import Data.Coerce import Control.Monad import Control.Effect import Control.Effect.Carrier import Control.Effect.Type.Internal.BaseControl import Control.Effect.Internal.BaseControl import Control.Effect.Internal.Itself import Control.Effect.Internal.Utils import Control.Monad.Trans.Identity import Control.Monad.Trans.Control import GHC.Exts (Proxy#, proxy#) newtype GainBaseControlC b z m a = GainBaseControlC { unGainBaseControlC :: m a } deriving ( Functor, Applicative, Monad , Alternative, MonadPlus , MonadFix, MonadFail, MonadIO , MonadThrow, MonadCatch, MonadMask , Carrier ) deriving (MonadTrans, MonadTransControl) via IdentityT instance (Monad m, MonadBase b z, Coercible z m) => MonadBase b (GainBaseControlC b z m) where liftBase = coerce #. liftBase @_ @z {-# INLINE liftBase #-} instance (Monad m, MonadBaseControl b z, Coercible z m) => MonadBaseControl b (GainBaseControlC b z m) where type StM (GainBaseControlC b z m) a = StM z a liftBaseWith m = coerce $ liftBaseWith @_ @z $ \lower -> m (coerceTrans lower) {-# INLINE liftBaseWith #-} restoreM = coerce (restoreM @_ @z @a) :: forall a. StM z a -> GainBaseControlC b z m a {-# INLINE restoreM #-} newtype Stateful m a = Stateful { getStateful :: StM m a } -- | Gain access to a function that allows for lowering @m@ to the -- base monad @b@. -- -- This is less versatile, but easier to use than 'gainBaseControl'. withLowerToBase :: forall b m a . Eff (BaseControl b) m => (forall f. (forall x. m x -> b (f x)) -> b (f a)) -> m a withLowerToBase main = join $ send $ GainBaseControl @b $ \(_ :: Proxy# z) -> coerceM $ control @_ @z $ \lower -> getStateful @z @a <$> main (fmap (Stateful @z) . coerceTrans lower) {-# INLINE withLowerToBase #-} -- | Locally gain access to a @'MonadBaseControl' b@ instance -- within a region. -- -- You'll need to use 'lift' if you want to use the 'MonadBaseControl' instance -- with computations outside of the region. -- This is common with effect handlers. For example: -- -- @ -- import System.IO (FilePath, IOMode, Handle) -- import qualified System.IO as SysIO -- -- data WithFile m a where -- WithFile :: FilePath -> IOMode -> (Handle -> m a) -> WithFile m a -- -- runWithFile :: 'Eff' ('BaseControl' IO) m => 'SimpleInterpreterFor' WithFile m -- runWithFile = 'interpretSimple' $ \case -- WithFile fp mode c -> 'gainBaseControl' $ 'control' $ \lower -> -- SysIO.withFile fp mode (\hdl -> lower (lift (c hdl))) -- @ -- gainBaseControl :: forall b m a . Eff (BaseControl b) m => ( forall z . (MonadBaseControl b z, Coercible z m) => GainBaseControlC b z m a ) -> m a gainBaseControl main = join $ send $ GainBaseControl @b (\(_ :: Proxy# z) -> unGainBaseControlC (main @z)) {-# INLINE gainBaseControl #-} -- | Run a @'BaseControl' m@ effect, where the base @m@ is the current monad. -- -- @'Derivs' ('BaseControlC' m) = 'BaseControl' m ': 'Derivs' m@ -- -- @'Prims' ('BaseControlC' m) = 'BaseControl' m ': 'Prims' m@ runBaseControl :: Carrier m => BaseControlC m a -> m a runBaseControl = unBaseControlC {-# INLINE runBaseControl #-} data BaseControlToFinalH type BaseControlToFinalC b = InterpretPrimC BaseControlToFinalH (BaseControl b) instance ( MonadBaseControl b m , Carrier m ) => PrimHandler BaseControlToFinalH (BaseControl b) m where effPrimHandler (GainBaseControl main) = return $ main (proxy# :: Proxy# m) {-# INLINEABLE effPrimHandler #-} -- | Run a @'BaseControl' b@ effect, where the base @b@ is the final base monad. -- -- @'Derivs' ('BaseControlToFinalC' b m) = 'BaseControl' b ': 'Derivs' m@ -- -- @'Prims' ('BaseControlToFinalC' b m) = 'BaseControl' b ': 'Prims' m@ baseControlToFinal :: (MonadBaseControl b m, Carrier m) => BaseControlToFinalC b m a -> m a baseControlToFinal = interpretPrimViaHandler {-# INLINE baseControlToFinal #-} -- | Strengthen an @'Algebra' p m@ by adding a @'BaseControl' m@ handler powerAlgBaseControl :: forall m p a . Monad m => Algebra' p m a -> Algebra' (BaseControl m ': p) m a powerAlgBaseControl alg = powerAlg alg $ \case GainBaseControl main -> return $ main (proxy# :: Proxy# (Itself m)) {-# INLINEABLE powerAlgBaseControl #-} -- | Strengthen an @'Algebra' p m@ by adding a @'BaseControl' b@ handler, -- where @b@ is the final base monad. powerAlgBaseControlFinal :: forall b m p a . MonadBaseControl b m => Algebra' p m a -> Algebra' (BaseControl b ': p) m a powerAlgBaseControlFinal alg = powerAlg alg $ \case GainBaseControl main -> return $ main (proxy# :: Proxy# m) {-# INLINEABLE powerAlgBaseControlFinal #-}