    {-# 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.Error as Error
    import Control.Monad.Trans.Identity as Identity
    import Control.Monad.Trans.Maybe as Maybe
    import Control.Monad.Trans.Reader
    import qualified Control.Monad.Trans.RWS.Lazy as LazyRWS (
            RWST, writer, tell, listen, pass)
    import qualified Control.Monad.Trans.RWS.Strict as StrictRWS (
            RWST, writer, tell, listen, pass)
    import Control.Monad.Trans.State.Lazy as Lazy
    import Control.Monad.Trans.State.Strict as Strict
    import qualified Control.Monad.Trans.Writer.Lazy as Lazy (
            WriterT, writer, tell, listen, pass)
    import qualified Control.Monad.Trans.Writer.Strict as Strict (
            WriterT, writer, tell, listen, pass)

    import Control.Monad.Trans.Class (lift)
    import Control.Monad
    import Data.Monoid

    -- ---------------------------------------------------------------------------
    -- 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
        -- | @'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)

    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

    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.

    instance (Error e, MonadWriter w m) => MonadWriter w (ErrorT e m) where
        writer = lift . writer
        tell   = lift . tell
        listen = Error.liftListen listen
        pass   = Error.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