packages feed

effet-0.1.0.0: src/Control/Effect/Embed.hs

{-# LANGUAGE TemplateHaskell #-}
-----------------------------------------------------------------------------
-- |
-- Module      :  Control.Effect.Embed
-- Copyright   :  (c) Michael Szvetits, 2020
-- License     :  BSD3 (see the file LICENSE)
-- Maintainer  :  typedbyte@qualified.name
-- Stability   :  stable
-- Portability :  portable
--
-- The embed effect for integrating arbitrary monads into the effect system.
-----------------------------------------------------------------------------
module Control.Effect.Embed
  ( -- * Embed Effect
    Embed(..)
    -- * Interpretations
  , Transformation
  , runEmbed
  ) where

-- transformers
import Control.Monad.Trans.Reader (ReaderT(ReaderT), runReaderT)

import Control.Effect.Machinery hiding (embed)

-- | An effect that integrates a monad @n@ into the computation @m@.
class Monad m => Embed n m where
  -- | Monadic actions in @n@ can be lifted into @m@ via 'embed'.
  --
  -- 'embed' is like 'liftIO', but not limited to 'IO'. In fact, 'liftIO' can
  -- be realized using 'embed' by specializing @n@ to @IO@.
  embed :: n a -> m a

makeEffect ''Embed

instance Monad m => Embed m m where
  embed = id
  {-# INLINE embed #-}

newtype F n t = F (forall b. n b -> t b)

-- | The transformation interpreter of the embed effect. This type implements the
-- 'Embed' type class by transforming the integrated monad @n@ into another
-- integrated monad @t@ via natural transformation.
--
-- When interpreting the effect, you usually don\'t interact with this type directly,
-- but instead use one of its corresponding interpretation functions.
newtype Transformation n t m a =
  Transformation { runTransformation :: ReaderT (F n t) m a }
    deriving (Applicative, Functor, Monad, MonadIO)
    deriving (MonadTrans, MonadTransControl)
    deriving (MonadBase b, MonadBaseControl b)

instance Embed t m => Embed n (Transformation n t m) where
  embed na = Transformation . ReaderT $
    \(F f) -> embed (f na)
  {-# INLINE embed #-}

-- | Runs the embed effect by transforming the integrated monad @n@ into another
-- integrated monad @t@.
runEmbed :: (forall b. n b -> t b)                 -- ^ The natural transformation from monad @n@ to monad @t@.
         -> (Embed n `Via` Transformation n t) m a -- ^ The program whose embed effect should be handled.
         -> m a                                    -- ^ The program with its embed effect handled.
runEmbed f = flip runReaderT (F f) . runTransformation . runVia
{-# INLINE runEmbed #-}