effet-0.2.0.0: src/Control/Effect/Writer.hs
{-# LANGUAGE TemplateHaskell #-}
-----------------------------------------------------------------------------
-- |
-- Module : Control.Effect.Writer
-- Copyright : (c) Michael Szvetits, 2020
-- License : BSD3 (see the file LICENSE)
-- Maintainer : typedbyte@qualified.name
-- Stability : stable
-- Portability : portable
--
-- The writer effect, similar to the @MonadWriter@ type class from the @mtl@
-- library.
--
-- Lazy and strict interpretations of the effect are available here:
-- "Control.Effect.Writer.Lazy" and "Control.Effect.Writer.Strict".
-----------------------------------------------------------------------------
module Control.Effect.Writer
( -- * Tagged Writer Effect
Writer'(..)
-- * Untagged Writer Effect
-- | If you don't require disambiguation of multiple writer effects
-- (i.e., you only have one writer effect in your monadic context),
-- it is recommended to always use the untagged writer effect.
, Writer
, tell
, listen
, censor
-- * Convenience Functions
-- | If you don't require disambiguation of multiple writer effects
-- (i.e., you only have one writer effect in your monadic context),
-- it is recommended to always use the untagged functions.
, listens'
, listens
-- * Tagging and Untagging
-- | Conversion functions between the tagged and untagged writer effect,
-- usually used in combination with type applications, like:
--
-- @
-- 'tagWriter'' \@\"newTag\" program
-- 'retagWriter'' \@\"oldTag\" \@\"newTag\" program
-- 'untagWriter'' \@\"erasedTag\" program
-- @
--
, tagWriter'
, retagWriter'
, untagWriter'
) where
-- base
import Data.Tuple (swap)
-- transformers
import qualified Control.Monad.Trans.RWS.CPS as Strict
import qualified Control.Monad.Trans.RWS.Lazy as Lazy
import qualified Control.Monad.Trans.Writer.Lazy as L
import qualified Control.Monad.Trans.Writer.CPS as S
import Control.Effect.Machinery
-- | An effect that adds a write-only, accumulated output to a given computation.
class Monad m => Writer' tag w m | tag m -> w where
-- | Produces the output @w@. In other words, @w@ is appended to the accumulated output.
tell' :: w -> m ()
-- | Executes a sub-computation and appends @w@ to the accumulated output.
listen' :: m a -> m (w, a)
-- | Executes a sub-computation and applies the function to its output.
censor' :: (w -> w) -- ^ The function which is applied to the output.
-> m a -- ^ The sub-computation which produces the modified output.
-> m a -- ^ The result of the sub-computation.
makeTaggedEffect ''Writer'
instance (Monad m, Monoid w) => Writer' tag w (L.WriterT w m) where
tell' = L.tell
{-# INLINE tell' #-}
listen' = fmap swap . L.listen
{-# INLINE listen' #-}
censor' = L.censor
{-# INLINE censor' #-}
instance (Monad m, Monoid w) => Writer' tag w (S.WriterT w m) where
tell' = S.tell
{-# INLINE tell' #-}
listen' = fmap swap . S.listen
{-# INLINE listen' #-}
censor' = S.censor
{-# INLINE censor' #-}
instance (Monad m, Monoid w) => Writer' tag w (Lazy.RWST r w s m) where
tell' = Lazy.tell
{-# INLINE tell' #-}
listen' = fmap swap . Lazy.listen
{-# INLINE listen' #-}
censor' = Lazy.censor
{-# INLINE censor' #-}
instance (Monad m, Monoid w) => Writer' tag w (Strict.RWST r w s m) where
tell' = Strict.tell
{-# INLINE tell' #-}
listen' = fmap swap . Strict.listen
{-# INLINE listen' #-}
censor' = Strict.censor
{-# INLINE censor' #-}
-- | Executes a sub-computation and applies the function to its output, thus adding
-- an additional value to the result of the sub-computation.
listens' :: forall tag w b m a. Writer' tag w m
=> (w -> b) -- ^ The function which is applied to the output.
-> m a -- ^ The sub-computation which produces the modified output.
-> m (b, a) -- ^ The result of the sub-computation, including the modified output.
listens' f action = do
~(w, a) <- listen' @tag action
pure (f w, a)
{-# INLINE listens' #-}
-- | The untagged version of 'listens''.
listens :: Writer w m => (w -> b) -> m a -> m (b, a)
listens = listens' @G
{-# INLINE listens #-}