effet-0.4.0.0: src/Control/Effect/RWS.hs
{-# LANGUAGE TemplateHaskell #-}
-----------------------------------------------------------------------------
-- |
-- Module : Control.Effect.RWS
-- Copyright : (c) Michael Szvetits, 2020
-- License : BSD3 (see the file LICENSE)
-- Maintainer : typedbyte@qualified.name
-- Stability : stable
-- Portability : portable
--
-- The effect that combines the reader, writer and state effect, similar to the
-- @MonadRWS@ type class from the @mtl@ library.
--
-- Lazy and strict interpretations of the effect are available here:
-- "Control.Effect.RWS.Lazy" and "Control.Effect.RWS.Strict".
-----------------------------------------------------------------------------
module Control.Effect.RWS
( -- * Tagged RWS Effect
RWS'
-- * Untagged RWS Effect
-- | If you don't require disambiguation of multiple RWS effects
-- (i.e., you only have one RWS effect in your monadic context),
-- it is recommended to always use the untagged RWS effect.
, RWS
-- * Interpretations
, Separation
, runSeparatedRWS'
, runSeparatedRWS
-- * Tagging and Untagging
-- | Conversion functions between the tagged and untagged RWS effect,
-- usually used in combination with type applications, like:
--
-- @
-- 'tagRWS'' \@\"newTag\" program
-- 'retagRWS'' \@\"oldTag\" \@\"newTag\" program
-- 'untagRWS'' \@\"erasedTag\" program
-- @
--
, tagRWS'
, retagRWS'
, untagRWS'
) where
-- base
import Data.Coerce (coerce)
-- transformers
import qualified Control.Monad.Trans.RWS.CPS as Strict
import qualified Control.Monad.Trans.RWS.Lazy as Lazy
import qualified Control.Effect.Reader as R
import qualified Control.Effect.State as S
import qualified Control.Effect.Writer as W
import Control.Effect.Machinery hiding (Tagger)
-- | An effect that adds the following features to a given computation:
--
-- * (R) an immutable environment (the \"reader\" part)
-- * (W) a write-only, accumulated output (the \"writer\" part)
-- * (S) a mutable state (the \"state\" part)
--
-- @since 0.2.0.0
class (R.Reader' tag r m, W.Writer' tag w m, S.State' tag s m) => RWS' tag r w s m | tag m -> r w s
makeTaggedEffect ''RWS'
instance (Monad m, Monoid w) => RWS' tag r w s (Lazy.RWST r w s m)
instance (Monad m, Monoid w) => RWS' tag r w s (Strict.RWST r w s m)
-- | The separation interpreter of the RWS effect. This type implements the 'RWS''
-- type class by splitting the effect into separate 'R.Reader'', 'W.Writer'' and
-- 'S.State'' effects which can then be interpreted individually.
--
-- When interpreting the effect, you usually don\'t interact with this type directly,
-- but instead use one of its corresponding interpretation functions.
newtype Separation m a =
Separation { runSeparation :: m a }
deriving (Applicative, Functor, Monad, MonadIO)
deriving (MonadTrans, MonadTransControl) via IdentityT
deriving (MonadBase b, MonadBaseControl b)
-- The following three "boring" instances are needed by hand, since GHC 8.6 cannot
-- derive them. With newer GHCs, you can derive these instances by adding above:
-- deriving (R.Reader' tag r, W.Writer' tag w, S.State' tag s)
instance R.Reader' tag r m => R.Reader' tag r (Separation m) where
ask' = Separation (R.ask' @tag)
{-# INLINE ask' #-}
local' f m = Separation (R.local' @tag f (runSeparation m))
{-# INLINE local' #-}
reader' f = Separation (R.reader' @tag f)
{-# INLINE reader' #-}
instance W.Writer' tag w m => W.Writer' tag w (Separation m) where
tell' w = Separation (W.tell' @tag w)
{-# INLINE tell' #-}
listen' m = Separation (W.listen' @tag (runSeparation m))
{-# INLINE listen' #-}
censor' f m = Separation (W.censor' @tag f (runSeparation m))
{-# INLINE censor' #-}
instance S.State' tag s m => S.State' tag s (Separation m) where
get' = Separation (S.get' @tag)
{-# INLINE get' #-}
put' s = Separation (S.put' @tag s)
{-# INLINE put' #-}
state' f = Separation (S.state' @tag f)
{-# INLINE state' #-}
instance (R.Reader' tag r m, W.Writer' tag w m, S.State' tag s m) => RWS' tag r w s (Separation m)
-- | Runs the RWS effect via separation.
runSeparatedRWS'
:: ('[RWS' tag r w s, R.Reader' tag r, W.Writer' tag w, S.State' tag s] `EachVia` Separation) m a
-- ^ The program whose RWS effect should be handled.
-> m a
-- ^ The program with its RWS effect handled.
runSeparatedRWS' = coerce
{-# INLINE runSeparatedRWS' #-}
-- | The untagged version of 'runSeparatedRWS''.
runSeparatedRWS :: ('[RWS r w s, R.Reader r, W.Writer w, S.State s] `EachVia` Separation) m a -> m a
runSeparatedRWS = coerce
{-# INLINE runSeparatedRWS #-}