New monads/LazyWriterT

From HaskellWiki

This came up on the mailing list: Why is WriterT never lazy? The answer is it does not use lazy patterns with "~". So here is a more useful NewMonads/LazyWriterT that add two "~" to the definition of (>>=) and renames WriterT to LazyWriterT.

This is very very handy when (Control.Monad.ST.Lazy) is the transformed Monad.

{-# OPTIONS_GHC -fglasgow-exts #-}
module Main where

-- LazyWriterT, copied from http://darcs.haskell.org/packages/mtl/Control/Monad/Writer.hs
import Control.Monad.Writer
import Control.Monad.Reader
import Control.Monad.Fix
import Control.Monad.Trans

newtype LazyWriterT w m a = LazyWriterT { runLazyWriterT :: m (a, w) }

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

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

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

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

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

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

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

instance (Monoid w, MonadReader r m) => MonadReader r (LazyWriterT w m) where
	ask       = lift ask
	local f m = LazyWriterT $ local f (runLazyWriterT m)


execLazyWriterT :: Monad m => LazyWriterT w m a -> m w
execLazyWriterT m = do
	(_, w) <- runLazyWriterT m
	return w

mapLazyWriterT :: (m (a, w) -> n (b, w')) -> LazyWriterT w m a -> LazyWriterT w' n b
mapLazyWriterT f m = LazyWriterT $ f (runLazyWriterT m)