<div dir="auto"><div dir="auto">It's widely known that the classic WriterT tends to leak space, and that in some cases this leak can be resolved by using StateT instead. The implementations I've seen of this idea both use the module system to prevent the computation from gaining unauthorized access to the state. I believe I've found a way to avoid this. Does this look right?</div><div dir="auto"><br></div><div dir="auto">{-# language RankNTypes, FlexibleInstances, MultiParamTypeClasses #-}</div><div dir="auto"><br></div><div dir="auto">import qualified Control.Monad.Writer.Class as W</div><div dir="auto">import Control.Monad.State.Strict</div><div dir="auto">import Control.Monad.Reader</div><div dir="auto">import Control.Applicative</div><div dir="auto"><br></div><div dir="auto">-- The key idea is that the computation</div><div dir="auto">-- can't inspect the state because it doesn't know the type</div><div dir="auto">newtype WriterT w m a = WriterT</div><div dir="auto">  { unWriterT :: forall s. ReaderT ((w -> w) -> s -> s) (StateT s m) a }</div><div dir="auto"><br></div><div dir="auto">runWriterT :: Monoid w => WriterT w m a -> m (a, w)</div><div dir="auto">runWriterT m = runStateT (runReaderT (unWriterT m) id) mempty</div><div dir="auto"><br></div><div dir="auto">instance Functor m => Functor (WriterT w m) where</div><div dir="auto">  fmap f m = WriterT $ fmap f (unWriterT m)</div><div dir="auto"><br></div><div dir="auto">instance Monad m => Applicative (WriterT w m) where</div><div dir="auto">  pure a = WriterT (pure a)</div><div dir="auto">  liftA2 f m n = WriterT $ liftA2 f (unWriterT m) (unWriterT n)</div><div dir="auto"><br></div><div dir="auto">instance Monad m => Monad (WriterT w m) where</div><div dir="auto">  m >>= f = WriterT $ unWriterT m >>= unWriterT . f</div><div dir="auto"><br></div><div dir="auto">instance MonadTrans (WriterT w) where</div><div dir="auto">  lift m = WriterT $ lift . lift $ m</div><div dir="auto"><br></div><div dir="auto">tell :: (Monad m, Semigroup w) => w -> WriterT w m ()</div><div dir="auto">tell w = WriterT $ do</div><div dir="auto">    p <- ask</div><div dir="auto">    modify' $ p (<> w)</div><div dir="auto"><br></div><div dir="auto"><div dir="auto">pass :: Monad m => WriterT w m (a, w -> w) -> WriterT w m a</div><div dir="auto">pass m = WriterT $ do</div><div dir="auto">  p <- ask</div><div dir="auto">  (a, ww) <- unWriterT m</div><div dir="auto">  modify' (p ww)</div><div dir="auto">  pure a</div></div><div dir="auto"><br></div><div dir="auto">instance (Monoid w, Monad m) => W.MonadWriter w (WriterT w m) where</div><div dir="auto">  tell = tell</div><div dir="auto"><br></div><div dir="auto">  listen m = do</div><div dir="auto">    aw@(_a, w) <- lift $ runWriterT m</div><div dir="auto">    tell w</div><div dir="auto">    pure aw</div><div dir="auto"><br></div><div dir="auto">  pass = pass</div></div>