{-# LANGUAGE DeriveFunctor, ExistentialQuantification, FlexibleContexts, FlexibleInstances, GeneralizedNewtypeDeriving, MultiParamTypeClasses, ScopedTypeVariables, StandaloneDeriving, TypeOperators, UndecidableInstances #-}
module Control.Effect.Writer
( Writer(..)
, tell
, listen
, listens
, censor
, runWriter
, execWriter
, WriterC(..)
) where
import Control.Applicative (Alternative(..))
import Control.Effect.Carrier
import Control.Effect.State
import Control.Effect.Sum
import Control.Monad (MonadPlus(..))
import Control.Monad.Fail
import Control.Monad.IO.Class
import Control.Monad.Trans.Class
data Writer w m k
= Tell w k
| forall a . Listen (m a) (w -> a -> k)
| forall a . Censor (w -> w) (m a) (a -> k)
deriving instance Functor (Writer w m)
instance HFunctor (Writer w) where
hmap _ (Tell w k) = Tell w k
hmap f (Listen m k) = Listen (f m) k
hmap f (Censor g m k) = Censor g (f m) k
{-# INLINE hmap #-}
instance Effect (Writer w) where
handle state handler (Tell w k) = Tell w (handler (k <$ state))
handle state handler (Listen m k) = Listen (handler (m <$ state)) (fmap handler . fmap . k)
handle state handler (Censor f m k) = Censor f (handler (m <$ state)) (handler . fmap k)
{-# INLINE handle #-}
tell :: (Member (Writer w) sig, Carrier sig m) => w -> m ()
tell w = send (Tell w (pure ()))
{-# INLINE tell #-}
listen :: (Member (Writer w) sig, Carrier sig m) => m a -> m (w, a)
listen m = send (Listen m (curry pure))
{-# INLINE listen #-}
listens :: (Member (Writer w) sig, Carrier sig m) => (w -> b) -> m a -> m (b, a)
listens f m = send (Listen m (curry pure . f))
{-# INLINE listens #-}
censor :: (Member (Writer w) sig, Carrier sig m) => (w -> w) -> m a -> m a
censor f m = send (Censor f m pure)
{-# INLINE censor #-}
runWriter :: Monoid w => WriterC w m a -> m (w, a)
runWriter = runState mempty . runWriterC
{-# INLINE runWriter #-}
execWriter :: (Monoid w, Functor m) => WriterC w m a -> m w
execWriter = fmap fst . runWriter
{-# INLINE execWriter #-}
newtype WriterC w m a = WriterC { runWriterC :: StateC w m a }
deriving (Alternative, Applicative, Functor, Monad, MonadFail, MonadIO, MonadPlus, MonadTrans)
instance (Monoid w, Carrier sig m, Effect sig) => Carrier (Writer w :+: sig) (WriterC w m) where
eff (L (Tell w k)) = WriterC $ do
modify (`mappend` w)
runWriterC k
eff (L (Listen m k)) = WriterC $ do
w <- get
put (mempty :: w)
a <- runWriterC m
w' <- get
modify (mappend (w :: w))
runWriterC (k w' a)
eff (L (Censor f m k)) = WriterC $ do
w <- get
put (mempty :: w)
a <- runWriterC m
modify (mappend w . f)
runWriterC (k a)
eff (R other) = WriterC (eff (R (handleCoercible other)))
{-# INLINE eff #-}