mtl-2.3.2: Control/Monad/Writer/Class.hs
{-# LANGUAGE Safe #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE UndecidableInstances #-}
-- Search for UndecidableInstances to see why this is needed
-----------------------------------------------------------------------------
-- |
-- Module : Control.Monad.Writer.Class
-- Copyright : (c) Andy Gill 2001,
-- (c) Oregon Graduate Institute of Science and Technology, 2001
-- License : BSD-style (see the file LICENSE)
--
-- Maintainer : libraries@haskell.org
-- Stability : experimental
-- Portability : non-portable (multi-param classes, functional dependencies)
--
-- The MonadWriter class.
--
-- Inspired by the paper
-- /Functional Programming with Overloading and Higher-Order Polymorphism/,
-- Mark P Jones (<http://web.cecs.pdx.edu/~mpj/pubs/springschool.html>)
-- Advanced School of Functional Programming, 1995.
-----------------------------------------------------------------------------
module Control.Monad.Writer.Class (
MonadWriter(..),
listens,
censor,
) where
import Control.Monad.Trans.Except (ExceptT)
import qualified Control.Monad.Trans.Except as Except
import Control.Monad.Trans.Identity (IdentityT)
import qualified Control.Monad.Trans.Identity as Identity
import Control.Monad.Trans.Maybe (MaybeT)
import qualified Control.Monad.Trans.Maybe as Maybe
import Control.Monad.Trans.Reader (ReaderT, mapReaderT)
import qualified Control.Monad.Trans.RWS.Lazy as LazyRWS
import qualified Control.Monad.Trans.RWS.Strict as StrictRWS
import qualified Control.Monad.Trans.State.Lazy as Lazy
import qualified Control.Monad.Trans.State.Strict as Strict
import qualified Control.Monad.Trans.Writer.Lazy as Lazy
import qualified Control.Monad.Trans.Writer.Strict as Strict
import Control.Monad.Trans.Accum (AccumT)
import qualified Control.Monad.Trans.Accum as Accum
import qualified Control.Monad.Trans.RWS.CPS as CPSRWS
import qualified Control.Monad.Trans.Writer.CPS as CPS
import Control.Monad.Trans.Class (lift)
import Data.Functor.Product (Product(..))
-- ---------------------------------------------------------------------------
-- MonadWriter class
--
-- tell is like tell on the MUD's it shouts to monad
-- what you want to be heard. The monad carries this 'packet'
-- upwards, merging it if needed (hence the Monoid requirement).
--
-- listen listens to a monad acting, and returns what the monad "said".
--
-- pass lets you provide a writer transformer which changes internals of
-- the written object.
class (Monoid w, Monad m) => MonadWriter w m | m -> w where
{-# MINIMAL (writer | tell), listen, pass #-}
-- | @'writer' (a,w)@ embeds a simple writer action.
writer :: (a,w) -> m a
writer ~(a, w) = do
tell w
return a
-- | @'tell' w@ is an action that produces the output @w@.
tell :: w -> m ()
tell w = writer ((),w)
-- | @'listen' m@ is an action that executes the action @m@ and adds
-- its output to the value of the computation.
listen :: m a -> m (a, w)
-- | @'pass' m@ is an action that executes the action @m@, which
-- returns a value and a function, and returns the value, applying
-- the function to the output.
pass :: m (a, w -> w) -> m a
-- | @'listens' f m@ is an action that executes the action @m@ and adds
-- the result of applying @f@ to the output to the value of the computation.
--
-- * @'listens' f m = 'liftM' (id *** f) ('listen' m)@
listens :: MonadWriter w m => (w -> b) -> m a -> m (a, b)
listens f m = do
~(a, w) <- listen m
return (a, f w)
-- | @'censor' f m@ is an action that executes the action @m@ and
-- applies the function @f@ to its output, leaving the return value
-- unchanged.
--
-- * @'censor' f m = 'pass' ('liftM' (\\x -> (x,f)) m)@
censor :: MonadWriter w m => (w -> w) -> m a -> m a
censor f m = pass $ do
a <- m
return (a, f)
-- | @since 2.2.2
instance (Monoid w) => MonadWriter w ((,) w) where
writer ~(a, w) = (w, a)
tell w = (w, ())
listen ~(w, a) = (w, (a, w))
pass ~(w, (a, f)) = (f w, a)
-- | @since 2.3
instance (Monoid w, Monad m) => MonadWriter w (CPS.WriterT w m) where
writer = CPS.writer
tell = CPS.tell
listen = CPS.listen
pass = CPS.pass
instance (Monoid w, Monad m) => MonadWriter w (Lazy.WriterT w m) where
writer = Lazy.writer
tell = Lazy.tell
listen = Lazy.listen
pass = Lazy.pass
instance (Monoid w, Monad m) => MonadWriter w (Strict.WriterT w m) where
writer = Strict.writer
tell = Strict.tell
listen = Strict.listen
pass = Strict.pass
-- | @since 2.3
instance (Monoid w, Monad m) => MonadWriter w (CPSRWS.RWST r w s m) where
writer = CPSRWS.writer
tell = CPSRWS.tell
listen = CPSRWS.listen
pass = CPSRWS.pass
instance (Monoid w, Monad m) => MonadWriter w (LazyRWS.RWST r w s m) where
writer = LazyRWS.writer
tell = LazyRWS.tell
listen = LazyRWS.listen
pass = LazyRWS.pass
instance (Monoid w, Monad m) => MonadWriter w (StrictRWS.RWST r w s m) where
writer = StrictRWS.writer
tell = StrictRWS.tell
listen = StrictRWS.listen
pass = StrictRWS.pass
-- ---------------------------------------------------------------------------
-- Instances for other mtl transformers
--
-- All of these instances need UndecidableInstances,
-- because they do not satisfy the coverage condition.
-- | @since 2.2
instance MonadWriter w m => MonadWriter w (ExceptT e m) where
writer = lift . writer
tell = lift . tell
listen = Except.liftListen listen
pass = Except.liftPass pass
instance MonadWriter w m => MonadWriter w (IdentityT m) where
writer = lift . writer
tell = lift . tell
listen = Identity.mapIdentityT listen
pass = Identity.mapIdentityT pass
instance MonadWriter w m => MonadWriter w (MaybeT m) where
writer = lift . writer
tell = lift . tell
listen = Maybe.liftListen listen
pass = Maybe.liftPass pass
instance MonadWriter w m => MonadWriter w (ReaderT r m) where
writer = lift . writer
tell = lift . tell
listen = mapReaderT listen
pass = mapReaderT pass
instance MonadWriter w m => MonadWriter w (Lazy.StateT s m) where
writer = lift . writer
tell = lift . tell
listen = Lazy.liftListen listen
pass = Lazy.liftPass pass
instance MonadWriter w m => MonadWriter w (Strict.StateT s m) where
writer = lift . writer
tell = lift . tell
listen = Strict.liftListen listen
pass = Strict.liftPass pass
-- | There are two valid instances for 'AccumT'. It could either:
--
-- 1. Lift the operations to the inner @MonadWriter@
-- 2. Handle the operations itself, à la a @WriterT@.
--
-- This instance chooses (1), reflecting that the intent
-- of 'AccumT' as a type is different than that of @WriterT@.
--
-- @since 2.3
instance
( Monoid w'
, MonadWriter w m
) => MonadWriter w (AccumT w' m) where
writer = lift . writer
tell = lift . tell
listen = Accum.liftListen listen
pass = Accum.liftPass pass
-- | @since 2.3.2
instance (MonadWriter w m, MonadWriter w n) => MonadWriter w (Product m n) where
writer aw = Pair (writer aw) (writer aw)
tell w = Pair (tell w) (tell w)
listen (Pair ma na) = Pair (listen ma) (listen na)
pass (Pair maf naf) = Pair (pass maf) (pass naf)