module Ether.Writer ( -- * MonadWriter class MonadWriter , writer , tell , listen , pass , listens , censor -- * The Writer monad , Writer , runWriter , execWriter -- * The WriterT monad transformer , WriterT , writerT , runWriterT , execWriterT -- * The Writer monad (lazy) , LazyWriter , runLazyWriter , execLazyWriter -- * The WriterT monad transformer (lazy) , LazyWriterT , lazyWriterT , runLazyWriterT , execLazyWriterT -- * MonadWriter class (implicit) , MonadWriter' , writer' , tell' , listen' , pass' , listens' , censor' -- * The Writer monad (implicit) , Writer' , runWriter' , execWriter' -- * The WriterT monad transformer (implicit) , WriterT' , writerT' , runWriterT' , execWriterT' -- * The Writer monad (lazy, implicit) , LazyWriter' , runLazyWriter' , execLazyWriter' -- * The WriterT monad transformer (lazy, implicit) , LazyWriterT' , lazyWriterT' , runLazyWriterT' , execLazyWriterT' -- * Internal labels , TAGGED , WRITER ) where import Control.Monad.Signatures (Listen, Pass) import qualified Control.Monad.Trans.Lift.Listen as Lift import qualified Control.Monad.Trans.Lift.Pass as Lift import qualified Control.Monad.Writer.Class as T import qualified Control.Monad.Writer.CPS as T.CPS import qualified Control.Monad.Writer.Lazy as T.Lazy import Data.Coerce import Data.Functor.Identity import Ether.Internal import Ether.TaggedTrans class (Monoid w, Monad m) => MonadWriter tag w m | m tag -> w where {-# MINIMAL (writer | tell), listen, pass #-} -- | Embed a simple writer action. writer :: (a, w) -> m a writer ~(a, w) = a <$ tell @tag w -- | Append a value to the accumulator within the monad. tell :: w -> m () tell w = writer @tag ((),w) -- | Execute an action and add its accumulator -- to the value of the computation. listen :: m a -> m (a, w) -- | Execute an action which returns a value and a function, -- and return the value, applying the function to the accumulator. pass :: m (a, w -> w) -> m a instance {-# OVERLAPPABLE #-} ( Lift.LiftListen t , Lift.LiftPass t , Monad (t m) , MonadWriter tag w m , Monoid w ) => MonadWriter tag w (t m) where writer = Lift.lift . writer @tag tell = Lift.lift . tell @tag listen = Lift.liftListen (listen @tag) pass = Lift.liftPass (pass @tag) -- | Execute an action and add the result of applying the given function to -- its accumulator to the value of the computation. listens :: forall tag w m a b . MonadWriter tag w m => (w -> b) -> m a -> m (a, b) listens f m = do ~(a, w) <- listen @tag m return (a, f w) -- | Execute an action and apply a function to its accumulator. censor :: forall tag w m a . MonadWriter tag w m => (w -> w) -> m a -> m a censor f m = pass @tag $ do a <- m return (a, f) -- | Encode type-level information for 'WriterT'. data WRITER type instance HandleSuper WRITER w trans = Monoid w type instance HandleConstraint WRITER w trans m = T.MonadWriter w (trans m) instance Monoid w => Handle WRITER w (T.CPS.WriterT w) where handling r = r instance Monoid w => Handle WRITER w (T.Lazy.WriterT w) where handling r = r instance ( Handle WRITER w trans , Monad m, Monad (trans m) ) => MonadWriter tag w (TaggedTrans (TAGGED WRITER tag) trans m) where writer = handling @WRITER @w @trans @m $ coerce (T.writer @w @(trans m) @a) :: forall eff a . (a, w) -> TaggedTrans eff trans m a tell = handling @WRITER @w @trans @m $ coerce (T.tell @w @(trans m)) listen = handling @WRITER @w @trans @m $ coerce (T.listen @w @(trans m) @a) :: forall eff a . Listen w (TaggedTrans eff trans m) a pass = handling @WRITER @w @trans @m $ coerce (T.pass @w @(trans m) @a) :: forall eff a . Pass w (TaggedTrans eff trans m) a -- | The parametrizable writer monad. -- -- Computations can accumulate a monoid value. -- -- The 'return' function produces the output 'mempty', while '>>=' combines -- the outputs of the subcomputations using 'mappend'. type Writer tag w = WriterT tag w Identity -- | The writer monad transformer. -- -- The 'return' function produces the output 'mempty', while '>>=' combines -- the outputs of the subcomputations using 'mappend'. type WriterT tag w = TaggedTrans (TAGGED WRITER tag) (T.CPS.WriterT w) -- | Constructor for computations in the writer monad transformer. writerT :: forall tag w m a . (Functor m, Monoid w) => m (a, w) -> WriterT tag w m a writerT = coerce (T.CPS.writerT @m @w @a) -- | Runs a 'WriterT' and returns both the normal value -- and the final accumulator. runWriterT :: forall tag w m a . Monoid w => WriterT tag w m a -> m (a, w) runWriterT = coerce (T.CPS.runWriterT @w @m @a) -- | Runs a 'Writer' and returns both the normal value -- and the final accumulator. runWriter :: forall tag w a . Monoid w => Writer tag w a -> (a, w) runWriter = coerce (T.CPS.runWriter @w @a) -- | Runs a 'WriterT' and returns the final accumulator, -- discarding the normal value. execWriterT :: forall tag w m a . (Monad m, Monoid w) => WriterT tag w m a -> m w execWriterT = coerce (T.CPS.execWriterT @m @w @a) -- | Runs a 'Writer' and returns the final accumulator, -- discarding the normal value. execWriter :: forall tag w a . Monoid w => Writer tag w a -> w execWriter = coerce (T.CPS.execWriter @w @a) -- | The parametrizable writer monad. -- -- Computations can accumulate a monoid value. -- -- The 'return' function produces the output 'mempty', while '>>=' combines -- the outputs of the subcomputations using 'mappend'. type LazyWriter tag w = LazyWriterT tag w Identity -- | The writer monad transformer. -- -- The 'return' function produces the output 'mempty', while '>>=' combines -- the outputs of the subcomputations using 'mappend'. type LazyWriterT tag w = TaggedTrans (TAGGED WRITER tag) (T.Lazy.WriterT w) -- | Constructor for computations in the writer monad transformer. lazyWriterT :: forall tag w m a . m (a, w) -> LazyWriterT tag w m a lazyWriterT = coerce (T.Lazy.WriterT @w @m @a) -- | Runs a 'WriterT' and returns both the normal value -- and the final accumulator. runLazyWriterT :: forall tag w m a . LazyWriterT tag w m a -> m (a, w) runLazyWriterT = coerce (T.Lazy.runWriterT @w @m @a) -- | Runs a 'Writer' and returns both the normal value -- and the final accumulator. runLazyWriter :: forall tag w a . LazyWriter tag w a -> (a, w) runLazyWriter = coerce (T.Lazy.runWriter @w @a) -- | Runs a 'WriterT' and returns the final accumulator, -- discarding the normal value. execLazyWriterT :: forall tag w m a . Monad m => LazyWriterT tag w m a -> m w execLazyWriterT = coerce (T.Lazy.execWriterT @m @w @a) -- | Runs a 'Writer' and returns the final accumulator, -- discarding the normal value. execLazyWriter :: forall tag w a . LazyWriter tag w a -> w execLazyWriter = coerce (T.Lazy.execWriter @w @a) type Writer' w = Writer w w runWriter' :: Monoid w => Writer' w a -> (a, w) runWriter' = runWriter execWriter' :: Monoid w => Writer' w a -> w execWriter' = execWriter type WriterT' w = WriterT w w writerT' :: (Functor m, Monoid w) => m (a, w) -> WriterT' w m a writerT' = writerT runWriterT' :: Monoid w => WriterT' w m a -> m (a, w) runWriterT' = runWriterT execWriterT' :: (Monad m, Monoid w) => WriterT' w m a -> m w execWriterT' = execWriterT type LazyWriter' w = LazyWriter w w runLazyWriter' :: LazyWriter' w a -> (a, w) runLazyWriter' = runLazyWriter execLazyWriter' :: LazyWriter' w a -> w execLazyWriter' = execLazyWriter type LazyWriterT' w = LazyWriterT w w lazyWriterT' :: m (a, w) -> LazyWriterT' w m a lazyWriterT' = lazyWriterT runLazyWriterT' :: LazyWriterT' w m a -> m (a, w) runLazyWriterT' = runLazyWriterT execLazyWriterT' :: Monad m => LazyWriterT' w m a -> m w execLazyWriterT' = execLazyWriterT type MonadWriter' w = MonadWriter w w writer' :: forall w m a . MonadWriter' w m => (a, w) -> m a writer' = writer @w tell' :: forall w m . MonadWriter' w m => w -> m () tell' = tell @w listen' :: forall w m a . MonadWriter' w m => m a -> m (a, w) listen' = listen @w pass' :: forall w m a . MonadWriter' w m => m (a, w -> w) -> m a pass' = pass @w listens' :: forall w m a b . MonadWriter' w m => (w -> b) -> m a -> m (a, b) listens' = listens @w censor' :: forall w m a . MonadWriter' w m => (w -> w) -> m a -> m a censor' = censor @w