    {-# OPTIONS -fallow-undecidable-instances #-}
    -- Search for -fallow-undecidable-instances 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 libraries/base/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 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
        tell   :: w -> m ()
        listen :: m a -> m (a, w)
        pass   :: m (a, w -> w) -> m a

    listens :: (MonadWriter w m) => (w -> b) -> m a -> m (a, b)
    listens f m = do
        ~(a, w) <- listen m
        return (a, f w)

    censor :: (MonadWriter w m) => (w -> w) -> m a -> m a
    censor f m = pass $ do
        a <- m
        return (a, f)