{-# OPTIONS -fallow-undecidable-instances #-}
-- Search for -fallow-undecidable-instances to see why this is needed

-----------------------------------------------------------------------------
-- |
-- Module      :  Control.Monad.Writer
-- 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://www.cse.ogi.edu/~mpj/>)
--		  Advanced School of Functional Programming, 1995.
-----------------------------------------------------------------------------

module Control.Monad.Writer (
	MonadWriter(..),
	listens,
	censor,
	Writer(..),
	execWriter,
	mapWriter,
	WriterT(..),
	execWriterT,
	mapWriterT,
	module Control.Monad,
	module Control.Monad.Fix,
	module Control.Monad.Trans,
	module Data.Monoid,
  ) where

import Prelude

import Control.Monad
import Control.Monad.Fix
import Control.Monad.Trans
import Control.Monad.Reader
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)

-- ---------------------------------------------------------------------------
-- Our parameterizable writer monad

newtype Writer w a = Writer { runWriter :: (a, w) }


instance Functor (Writer w) where
	fmap f m = Writer $ let (a, w) = runWriter m in (f a, w)

instance (Monoid w) => Monad (Writer w) where
	return a = Writer (a, mempty)
	m >>= k  = Writer $ let
		(a, w)  = runWriter m
		(b, w') = runWriter (k a)
		in (b, w `mappend` w')

instance (Monoid w) => MonadFix (Writer w) where
	mfix m = Writer $ let (a, w) = runWriter (m a) in (a, w)

instance (Monoid w) => MonadWriter w (Writer w) where
	tell   w = Writer ((), w)
	listen m = Writer $ let (a, w) = runWriter m in ((a, w), w)
	pass   m = Writer $ let ((a, f), w) = runWriter m in (a, f w)


execWriter :: Writer w a -> w
execWriter m = snd (runWriter m)

mapWriter :: ((a, w) -> (b, w')) -> Writer w a -> Writer w' b
mapWriter f m = Writer $ f (runWriter m)

-- ---------------------------------------------------------------------------
-- Our parameterizable writer monad, with an inner monad

newtype WriterT w m a = WriterT { runWriterT :: m (a, w) }


instance (Monad m) => Functor (WriterT w m) where
	fmap f m = WriterT $ do
		(a, w) <- runWriterT m
		return (f a, w)

instance (Monoid w, Monad m) => Monad (WriterT w m) where
	return a = WriterT $ return (a, mempty)
	m >>= k  = WriterT $ do
		(a, w)  <- runWriterT m
		(b, w') <- runWriterT (k a)
		return (b, w `mappend` w')
	fail msg = WriterT $ fail msg

instance (Monoid w, MonadPlus m) => MonadPlus (WriterT w m) where
	mzero       = WriterT mzero
	m `mplus` n = WriterT $ runWriterT m `mplus` runWriterT n

instance (Monoid w, MonadFix m) => MonadFix (WriterT w m) where
	mfix m = WriterT $ mfix $ \ ~(a, _) -> runWriterT (m a)

instance (Monoid w, Monad m) => MonadWriter w (WriterT w m) where
	tell   w = WriterT $ return ((), w)
	listen m = WriterT $ do
		(a, w) <- runWriterT m
		return ((a, w), w)
	pass   m = WriterT $ do
		((a, f), w) <- runWriterT m
		return (a, f w)

instance (Monoid w) => MonadTrans (WriterT w) where
	lift m = WriterT $ do
		a <- m
		return (a, mempty)

instance (Monoid w, MonadIO m) => MonadIO (WriterT w m) where
	liftIO = lift . liftIO

-- This instance needs -fallow-undecidable-instances, because 
-- it does not satisfy the coverage condition
instance (Monoid w, MonadReader r m) => MonadReader r (WriterT w m) where
	ask       = lift ask
	local f m = WriterT $ local f (runWriterT m)


execWriterT :: Monad m => WriterT w m a -> m w
execWriterT m = do
	(_, w) <- runWriterT m
	return w

mapWriterT :: (m (a, w) -> n (b, w')) -> WriterT w m a -> WriterT w' n b
mapWriterT f m = WriterT $ f (runWriterT m)

-- ---------------------------------------------------------------------------
-- MonadWriter instances for other monad transformers

-- This instance needs -fallow-undecidable-instances, because 
-- it does not satisfy the coverage condition
instance (MonadWriter w m) => MonadWriter w (ReaderT r m) where
	tell     = lift . tell
	listen m = ReaderT $ \w -> listen (runReaderT m w)
	pass   m = ReaderT $ \w -> pass   (runReaderT m w)
