{-# LANGUAGE BangPatterns #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE UndecidableInstances #-} {- | A carrier for 'Writer' effects. This carrier performs its append operations strictly and thus avoids the space leaks inherent in lazy writer monads. These appends are left-associative; as such, @[]@ is a poor choice of monoid for computations that entail many calls to 'tell'. The [Seq](http://hackage.haskell.org/package/containersdocs/Data-Sequence.html) or [DList](http://hackage.haskell.org/package/dlist) monoids may be a superior choice. This implementation is based on a post Gabriel Gonzalez made to the Haskell mailing list: @since 1.0.0.0 -} module Control.Carrier.Writer.Strict ( -- * Writer carrier runWriter , execWriter , WriterC(WriterC) -- * Writer effect , module Control.Effect.Writer ) where import Control.Algebra import Control.Applicative (Alternative) import Control.Carrier.State.Strict import Control.Effect.Writer import Control.Monad (MonadPlus) import Control.Monad.Fail as Fail import Control.Monad.Fix import Control.Monad.IO.Class import Control.Monad.Trans.Class -- | Run a 'Writer' effect with a 'Monoid'al log, producing the final log alongside the result value. -- -- @ -- 'runWriter' ('tell' w) = 'pure' (w, ()) -- @ -- @ -- 'runWriter' ('pure' a) = 'pure' ('mempty', a) -- @ runWriter :: Monoid w => WriterC w m a -> m (w, a) runWriter (WriterC m) = runState mempty m {-# INLINE runWriter #-} -- | Run a 'Writer' effect with a 'Monoid'al log, producing the final log and discarding the result value. -- -- @ -- 'execWriter' m = 'fmap' 'fst' ('runWriter' m) -- @ execWriter :: (Monoid w, Functor m) => WriterC w m a -> m w execWriter = fmap fst . runWriter {-# INLINE execWriter #-} -- | A space-efficient carrier for 'Writer' effects, implemented atop "Control.Carrier.State.Strict". -- -- @since 1.0.0.0 newtype WriterC w m a = WriterC { runWriterC :: StateC w m a } deriving (Alternative, Applicative, Functor, Monad, Fail.MonadFail, MonadFix, MonadIO, MonadPlus, MonadTrans) instance (Monoid w, Algebra sig m) => Algebra (Writer w :+: sig) (WriterC w m) where alg hdl sig ctx = WriterC $ case sig of L writer -> StateC $ \ w -> case writer of Tell w' -> do let !w'' = mappend w w' pure (w'', ctx) Listen m -> do (w', a) <- runWriter (hdl (m <$ ctx)) let !w'' = mappend w w' pure (w'', (,) w' <$> a) Censor f m -> do (w', a) <- runWriter (hdl (m <$ ctx)) let !w'' = mappend w (f w') pure (w'', a) R other -> alg (runWriterC . hdl) (R other) ctx {-# INLINE alg #-}